This strategy is a C# port of the MetaTrader 5 expert advisor 3sma.mq5 (id 21495). It follows the same idea of trading when three simple moving averages separate from each other by a configurable spread. The implementation uses the high-level StockSharp API with candle subscriptions and indicator binding so that no manual series management is required.
Original MT5 behaviour
The MT5 expert relies on three simple moving averages with different periods and display shifts. The fast average uses the current bar, while the middle and slow averages are shifted one and two bars into the past. On every tick it:
Converts the user-defined spread from pips into price units based on symbol precision.
Closes long positions when the fast SMA drops below the middle SMA by at least half of the spread, and closes short positions when the fast SMA rises above the middle SMA by half of the spread.
Opens new long positions if MA1 > MA2 + spread and MA2 > MA3 + spread while no other long trades from the expert remain. Analogously, it opens short positions when all three averages are aligned in the opposite order.
Uses only market orders with a fixed lot size and does not apply explicit stop-loss or take-profit levels.
StockSharp implementation
Indicators – three SimpleMovingAverage instances subscribe to the same candle source. Compact history buffers reproduce the MT5 "shift" parameters so that each comparison uses finished-bar values from the requested offsets.
Spread handling – the spread parameter is entered in pips. The strategy derives a pip size from Security.PriceStep (or Security.Step) and multiplies it by ten for 3/5-digit FX symbols, matching the MT5 adjustment for fractional quotes.
Order flow – orders are submitted with BuyMarket/SellMarket. When a reversal condition appears, the strategy adds the absolute value of the current net position to the base volume in order to flatten the opposite exposure and establish the new direction with a single market order.
Visualization – if charts are available, the strategy plots the source candles together with the three moving averages and executed trades.
Parameters
Name
Description
Default
Volume
Order volume used for each market entry.
0.1
FastMaPeriod
Period of the fast SMA (equivalent to MA1 in MT5).
9
FastMaShift
Number of finished bars used to shift the fast SMA.
0
MiddleMaPeriod
Period of the middle SMA (MA2).
14
MiddleMaShift
Shift in finished bars for the middle SMA.
1
SlowMaPeriod
Period of the slow SMA (MA3).
29
SlowMaShift
Shift in finished bars for the slow SMA.
2
MaSpreadPips
Minimal required spread between consecutive SMAs measured in pips.
10
CandleType
Candle series used for calculations.
1 minute time-frame
Trading logic
Wait for all three moving averages to be formed and for the history buffers to contain values for the requested shifts.
Convert the spread parameter from pips into price units and compute half-spread for exit filters.
Exit filters –
Close long exposure if the shifted fast SMA falls below the shifted middle SMA by at least half of the spread.
Close short exposure if the shifted fast SMA rises above the shifted middle SMA by at least half of the spread.
Entry conditions –
Enter long (or reverse from short to long) when the fast SMA is greater than the middle SMA plus the spread and the middle SMA is greater than the slow SMA plus the spread.
Enter short (or reverse from long to short) when the fast SMA is less than the middle SMA minus the spread and the middle SMA is less than the slow SMA minus the spread.
Differences from the MT5 version
StockSharp works with a single net position per security. When a reversal signal appears the strategy issues a single market order sized to both flatten the previous net exposure and establish the new one. The MT5 expert could keep independent long and short positions.
Pip conversion uses the best available Security metadata. If the broker supplies neither PriceStep nor Step, a value of 1 is used as a fallback.
Orders are submitted on finished candles instead of every tick because the high-level API operates on candle subscriptions.
The strategy does not implement the verbose logging helpers from the MT5 code; StockSharp built-in logging can be used if needed.
Usage notes
Ensure that the selected candle series matches the timeframe used in the original MT5 setup.
Adjust the spread parameter whenever the instrument uses non-standard pip sizes.
Because the strategy works with finished candles, execution will be delayed until the current candle closes.
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Triple SMA strategy that trades when three moving averages are properly aligned.
/// Enters long when fast > middle > slow, enters short when fast < middle < slow.
/// </summary>
public class TripleSmaSpreadStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _middlePeriod;
private readonly StrategyParam<int> _slowPeriod;
private int _prevSignal;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int FastPeriod
{
get => _fastPeriod.Value;
set => _fastPeriod.Value = value;
}
public int MiddlePeriod
{
get => _middlePeriod.Value;
set => _middlePeriod.Value = value;
}
public int SlowPeriod
{
get => _slowPeriod.Value;
set => _slowPeriod.Value = value;
}
public TripleSmaSpreadStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Timeframe", "General");
_fastPeriod = Param(nameof(FastPeriod), 9)
.SetGreaterThanZero()
.SetDisplay("Fast Period", "Fast SMA period", "Indicators");
_middlePeriod = Param(nameof(MiddlePeriod), 14)
.SetGreaterThanZero()
.SetDisplay("Middle Period", "Middle SMA period", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 29)
.SetGreaterThanZero()
.SetDisplay("Slow Period", "Slow SMA period", "Indicators");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevSignal = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevSignal = 0;
var fastSma = new ExponentialMovingAverage { Length = FastPeriod };
var slowSma = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fastSma, slowSma, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fastSma);
DrawIndicator(area, slowSma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var close = candle.ClosePrice;
var signal = 0;
if (fast > slow && close > fast)
signal = 1;
else if (fast < slow && close < fast)
signal = -1;
if (signal == _prevSignal)
return;
var oldSignal = _prevSignal;
_prevSignal = signal;
if (signal == 1 && oldSignal <= 0)
{
if (Position < 0)
BuyMarket();
if (Position <= 0)
BuyMarket();
}
else if (signal == -1 && oldSignal >= 0)
{
if (Position > 0)
SellMarket();
if (Position >= 0)
SellMarket();
}
}
}
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 triple_sma_spread_strategy(Strategy):
def __init__(self):
super(triple_sma_spread_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._fast_period = self.Param("FastPeriod", 9) \
.SetDisplay("Fast Period", "Fast SMA period", "Indicators")
self._middle_period = self.Param("MiddlePeriod", 14) \
.SetDisplay("Middle Period", "Middle SMA period", "Indicators")
self._slow_period = self.Param("SlowPeriod", 29) \
.SetDisplay("Slow Period", "Slow SMA period", "Indicators")
self._prev_signal = 0
@property
def CandleType(self):
return self._candle_type.Value
@property
def FastPeriod(self):
return self._fast_period.Value
@property
def MiddlePeriod(self):
return self._middle_period.Value
@property
def SlowPeriod(self):
return self._slow_period.Value
def OnReseted(self):
super(triple_sma_spread_strategy, self).OnReseted()
self._prev_signal = 0
def OnStarted2(self, time):
super(triple_sma_spread_strategy, self).OnStarted2(time)
self._prev_signal = 0
fast_ema = ExponentialMovingAverage()
fast_ema.Length = self.FastPeriod
slow_ema = ExponentialMovingAverage()
slow_ema.Length = self.SlowPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(fast_ema, slow_ema, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast_ema)
self.DrawIndicator(area, slow_ema)
self.DrawOwnTrades(area)
def _on_process(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
fv = float(fast_value)
sv = float(slow_value)
signal = 0
if fv > sv and close > fv:
signal = 1
elif fv < sv and close < fv:
signal = -1
if signal == self._prev_signal:
return
old_signal = self._prev_signal
self._prev_signal = signal
if signal == 1 and old_signal <= 0:
if self.Position < 0:
self.BuyMarket()
if self.Position <= 0:
self.BuyMarket()
elif signal == -1 and old_signal >= 0:
if self.Position > 0:
self.SellMarket()
if self.Position >= 0:
self.SellMarket()
def CreateClone(self):
return triple_sma_spread_strategy()