The Executer AC Strategy is a faithful StockSharp port of the MetaTrader 5 "Executer AC" expert advisor. The original EA trades on the Accelerator Oscillator (AC) developed by Bill Williams and combines its momentum swings with a fixed stop/limit framework and a trailing stop module. This conversion keeps the behaviour of the MQL5 version while exposing user-friendly parameters that integrate with the high-level StockSharp API.
Trading logic
The strategy operates on finished candles of the selected timeframe and relies on the last four Accelerator Oscillator values:
AC[0] – most recent completed bar (called ac[1] in the original code).
AC[1], AC[2], AC[3] – progressively older values used for pattern detection.
The decision tree is identical to the EA:
Position management
Long positions are closed when AC[0] < AC[1] (momentum decreasing).
Short positions are closed when AC[0] > AC[1] (momentum increasing).
A trailing stop routine dynamically tightens the protective stop once price moves beyond the configured distance plus the trailing step.
Entry rules when flat
Bullish acceleration above zero: if AC[0] > 0 and AC[1] > 0 and AC[0] > AC[1] > AC[2], a market buy is issued.
Bearish acceleration above zero: if AC[0] > 0 and AC[1] > 0 and AC[0] < AC[1] < AC[2] < AC[3], a market sell is issued.
Bullish acceleration below zero: if AC[0] < 0 and AC[1] < 0 and AC[0] > AC[1] > AC[2] > AC[3], a market buy is issued.
Bearish acceleration below zero: if AC[0] < 0 and AC[1] < 0 and AC[0] < AC[1] < AC[2], a market sell is issued.
Zero-line crossings: a downwards cross (AC[0] > 0 and AC[1] < 0) triggers a buy, while an upwards cross (AC[0] < 0 and AC[1] > 0) triggers a sell.
Signals are evaluated only after confirming that candles are finished, indicator values are formed and trading is enabled.
Risk management
Stop-loss and take-profit: configurable distances (in pips) converted to price units using the instrument’s step. Stops are recalculated on every fresh entry and remain unchanged until either hit or replaced by the trailing stop.
Trailing stop: mirrors the EA logic. When unrealised profit exceeds TrailingStop + TrailingStep (both in pips), the stop price is moved to Close - TrailingStop for longs and Close + TrailingStop for shorts, while enforcing the required improvement before each step.
Position protection: the built-in StartProtection() helper is invoked to let StockSharp guard against unexpected disconnections.
Parameters
Parameter
Description
TradeVolume
Order volume used for all market entries. It is normalised according to the security volume step and limits.
StopLossPips
Stop-loss distance in pips. A value of zero disables the stop-loss.
TakeProfitPips
Take-profit distance in pips. A value of zero disables the take-profit.
TrailingStopPips
Trailing stop distance in pips. Set to zero to disable trailing.
TrailingStepPips
Minimum extra profit (in pips) required before moving the trailing stop again.
CandleType
Timeframe of the candles used to compute the Accelerator Oscillator.
Implementation notes
Price normalisation respects both the exchange tick size and three/five-digit Forex symbols by multiplying the point size by ten when appropriate.
Indicator history is kept in a fixed-size buffer to replicate the original ac[1] … ac[4] comparisons without resorting to expensive collections or history queries.
The strategy always exits before evaluating new entries on the same candle, matching the control flow of the MQL5 EA where return statements prevent immediate re-entry.
Trailing stop values update both the internal trailing state and the effective stop price used for stop-loss checks, ensuring consistency with the EA’s PositionModify behaviour.
Usage tips
Choose a candle timeframe that matches the market you trade (the original script was commonly used on intraday Forex charts).
Tune stop-loss, take-profit and trailing distances to the volatility of the chosen instrument; extremely tight values can lead to frequent whipsaws.
Enable risk controls on the connected broker side when possible, as the strategy relies on software-side exits.
Combine with portfolio-level money management if you intend to run multiple strategies simultaneously.
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Executer AC strategy using EMA crossover.
/// Buys when fast EMA crosses above slow EMA, sells on reverse.
/// </summary>
public class ExecuterAcStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<int> _takeProfitPoints;
private ExponentialMovingAverage _fast;
private ExponentialMovingAverage _slow;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
private int _cooldown;
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }
public ExecuterAcStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 12).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
}
protected override void OnReseted()
{
base.OnReseted();
_fast = null; _slow = null;
_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fast = new ExponentialMovingAverage { Length = FastPeriod };
_slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
subscription.Bind(_fast, _slow, ProcessCandle);
subscription.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished) return;
if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }
var close = candle.ClosePrice;
var step = Security?.PriceStep ?? 1m;
if (Position > 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
else if (Position < 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }
_prevFast = fastValue; _prevSlow = slowValue;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class executer_ac_strategy(Strategy):
"""
Executer AC strategy using EMA crossover with SL/TP and cooldown.
Buys when fast EMA crosses above slow EMA, sells on reverse.
"""
def __init__(self):
super(executer_ac_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 12) \
.SetDisplay("Fast Period", "Fast EMA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 50) \
.SetDisplay("Slow Period", "Slow EMA period", "Indicator")
self._stop_loss_points = self.Param("StopLossPoints", 200) \
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 400) \
.SetDisplay("Take Profit", "Take-profit in price steps", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Trading timeframe", "General")
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(executer_ac_strategy, self).OnReseted()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(executer_ac_strategy, self).OnStarted2(time)
fast = ExponentialMovingAverage()
fast.Length = self._fast_period.Value
slow = ExponentialMovingAverage()
slow.Length = self._slow_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(fast, slow, self._process_candle).Start()
def _process_candle(self, candle, fast_val, slow_val):
if candle.State != CandleStates.Finished:
return
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_fast = float(fast_val)
self._prev_slow = float(slow_val)
return
fast_val = float(fast_val)
slow_val = float(slow_val)
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fast_val
self._prev_slow = slow_val
return
close = float(candle.ClosePrice)
step = 1.0
if self.Security is not None and self.Security.PriceStep is not None:
step = float(self.Security.PriceStep)
if step <= 0:
step = 1.0
sl_pts = self._stop_loss_points.Value
tp_pts = self._take_profit_points.Value
if self.Position > 0 and self._entry_price > 0:
if sl_pts > 0 and close <= self._entry_price - sl_pts * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if tp_pts > 0 and close >= self._entry_price + tp_pts * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
elif self.Position < 0 and self._entry_price > 0:
if sl_pts > 0 and close >= self._entry_price + sl_pts * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if tp_pts > 0 and close <= self._entry_price - tp_pts * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._prev_fast <= self._prev_slow and fast_val > slow_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 100
elif self._prev_fast >= self._prev_slow and fast_val < slow_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return executer_ac_strategy()