Smart AC Trader adapts the original MetaTrader "Smart AC Trader" idea to StockSharp's high level API. The MQL expert evaluated the relative strength of the currencies inside a pair and reacted when the base currency outperformed the quote currency. In StockSharp we focus on the same momentum-driven behaviour but operate on a single instrument that the strategy is attached to. Strength is approximated through a combination of exponential moving averages (EMAs) and the rate of change (ROC) indicator:
A fast EMA measures short-term trend direction.
A slow EMA represents the primary trend.
ROC confirms that price momentum aligns with the trend before entries are allowed.
Once a position is opened, the strategy actively manages the trade using stop-loss, take-profit, trailing stop and break-even rules that mirror the extensive money management configuration of the original expert.
Trading Logic
Subscribe to the configured candle type (time-frame) and calculate the fast EMA, slow EMA and ROC on the candle close.
Enter a long position when the fast EMA is above the slow EMA and ROC is greater than or equal to the buy momentum threshold. Existing short exposure is closed before the new long is opened.
Enter a short position when the fast EMA is below the slow EMA and ROC is less than or equal to the negative sell momentum threshold. Existing long exposure is closed before the new short is opened.
Manage an open position on every finished candle:
Close the trade at the configured take-profit or stop-loss distances (expressed in price steps).
Optionally arm a break-even exit once price moves in favour of the trade by the trigger distance and liquidate if price returns to the preserved offset.
Optionally trail the stop by the configured distance from the highest high (long) or lowest low (short) observed after entry.
Parameters
Parameter
Description
Fast EMA
Length of the fast EMA trend filter.
Slow EMA
Length of the slow EMA trend filter.
ROC Period
Lookback window for the rate of change momentum filter.
Buy Momentum
Minimum positive ROC required to open long trades.
Sell Momentum
Minimum absolute negative ROC required to open short trades.
Stop Loss
Stop-loss distance expressed in price steps.
Take Profit
Take-profit distance expressed in price steps.
Use Trailing
Enables trailing stop management.
Trailing
Trailing stop distance in price steps.
Use Break Even
Enables the break-even protection logic.
Break Even Trigger
Profit in price steps required to arm the break-even logic.
Break Even Offset
Distance in price steps kept after the break-even trigger is hit.
Candle Type
Candle type used to feed the indicators.
Notes
The strategy uses Strategy.StartProtection() once at start-up to ensure the built-in position protection system is active as recommended by the project guidelines.
Position sizing relies on the base Strategy.Volume property. Reversal orders automatically include the current exposure so that an opposite signal both closes the existing position and establishes a new one.
All risk parameters are expressed in price steps because the original expert advisor used pip-based distances. Make sure the instrument has a valid PriceStep configured.
namespace StockSharp.Samples.Strategies;
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// Smart AC Trader: EMA trend + ROC momentum filter.
/// Buys when fast EMA above slow EMA and ROC positive.
/// Sells when fast EMA below slow EMA and ROC negative.
/// </summary>
public class SmartAcTraderStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _rocPeriod;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int FastPeriod
{
get => _fastPeriod.Value;
set => _fastPeriod.Value = value;
}
public int SlowPeriod
{
get => _slowPeriod.Value;
set => _slowPeriod.Value = value;
}
public int RocPeriod
{
get => _rocPeriod.Value;
set => _rocPeriod.Value = value;
}
public SmartAcTraderStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_fastPeriod = Param(nameof(FastPeriod), 10)
.SetGreaterThanZero()
.SetDisplay("Fast EMA", "Fast EMA period", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 50)
.SetGreaterThanZero()
.SetDisplay("Slow EMA", "Slow EMA period", "Indicators");
_rocPeriod = Param(nameof(RocPeriod), 13)
.SetGreaterThanZero()
.SetDisplay("ROC Period", "Rate of Change period", "Indicators");
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var fast = new ExponentialMovingAverage { Length = FastPeriod };
var slow = new ExponentialMovingAverage { Length = SlowPeriod };
var roc = new RateOfChange { Length = RocPeriod };
decimal? prevFast = null;
decimal? prevSlow = null;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fast, slow, roc, (candle, fastVal, slowVal, rocVal) =>
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (prevFast.HasValue && prevSlow.HasValue)
{
var crossUp = prevFast.Value <= prevSlow.Value && fastVal > slowVal;
var crossDown = prevFast.Value >= prevSlow.Value && fastVal < slowVal;
if (crossUp && rocVal > 0 && Position <= 0)
BuyMarket();
else if (crossDown && rocVal < 0 && Position >= 0)
SellMarket();
}
prevFast = fastVal;
prevSlow = slowVal;
})
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fast);
DrawIndicator(area, slow);
DrawOwnTrades(area);
}
}
}
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, RateOfChange
from StockSharp.Algo.Strategies import Strategy
class smart_ac_trader_strategy(Strategy):
def __init__(self):
super(smart_ac_trader_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))) \
.SetDisplay("Candle Type", "Candle timeframe", "General")
self._fast_period = self.Param("FastPeriod", 10) \
.SetGreaterThanZero() \
.SetDisplay("Fast EMA", "Fast EMA period", "Indicators")
self._slow_period = self.Param("SlowPeriod", 50) \
.SetGreaterThanZero() \
.SetDisplay("Slow EMA", "Slow EMA period", "Indicators")
self._roc_period = self.Param("RocPeriod", 13) \
.SetGreaterThanZero() \
.SetDisplay("ROC Period", "Rate of Change period", "Indicators")
self._prev_fast = None
self._prev_slow = None
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(smart_ac_trader_strategy, self).OnReseted()
self._prev_fast = None
self._prev_slow = None
def OnStarted2(self, time):
super(smart_ac_trader_strategy, self).OnStarted2(time)
self._fast_ind = ExponentialMovingAverage()
self._fast_ind.Length = self._fast_period.Value
self._slow_ind = ExponentialMovingAverage()
self._slow_ind.Length = self._slow_period.Value
self._roc_ind = RateOfChange()
self._roc_ind.Length = self._roc_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._fast_ind, self._slow_ind, self._roc_ind, self._process_candle).Start()
def _process_candle(self, candle, fast_value, slow_value, roc_value):
if candle.State != CandleStates.Finished:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
fast_val = float(fast_value)
slow_val = float(slow_value)
roc_val = float(roc_value)
if self._prev_fast is not None and self._prev_slow is not None:
cross_up = self._prev_fast <= self._prev_slow and fast_val > slow_val
cross_down = self._prev_fast >= self._prev_slow and fast_val < slow_val
if cross_up and roc_val > 0 and self.Position <= 0:
self.BuyMarket()
elif cross_down and roc_val < 0 and self.Position >= 0:
self.SellMarket()
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return smart_ac_trader_strategy()