The MA S.R. Trading strategy is a trend-reversal system converted from the original MetaTrader advisor "MA S.R Trading". It monitors the shape of a short simple moving average (SMA) to detect when price momentum bends into a local top or bottom. When the SMA peaks or troughs, the strategy immediately enters in the direction of the turn and protects the position with a stop level anchored at the most recent swing.
Unlike classical crossover systems that compare multiple moving averages with different lengths, this approach analyses the curvature of the same SMA by comparing its value on the three most recent completed candles. A local maximum (SMA[t-2] greater than both SMA[t-1] and SMA[t-3]) signals a bearish reversal and triggers a short entry. A local minimum (SMA[t-2] below both neighbours) signals a bullish reversal and opens a long position. Immediately after a signal the strategy stores the extreme price over a configurable lookback window and uses it as a protective stop.
The exit logic mimics the trailing modification from the MQL source. For short trades the stop is set to the highest high inside the lookback window, provided that this level remains above the previous close (otherwise the level is ignored). Long positions use the lowest low under the same rule. If price touches the stored level on subsequent candles, the strategy closes the position at market, effectively emulating the stop-loss update from the original expert.
The system is designed for instruments that exhibit pronounced swing behaviour on intraday to short-term charts. Short SMA periods (default = 5) allow the algorithm to react quickly to micro structure changes, while the stop lookback (default = 5 bars for both highs and lows) controls how aggressively the trailing level follows price. Use tighter windows for scalping environments and wider settings for noisier markets.
Backtests on FX majors and liquid index CFDs show the best performance during ranging periods with frequent oscillations. Trends with smooth pullbacks may require additional filters or volatility confirmation to avoid premature reversals. Consider pairing the strategy with broader market context or time filters when deploying live.
Details
Entry Conditions
Short: SMA[t-1] < SMA[t-2] AND SMA[t-3] < SMA[t-2]. The last finished SMA sample forms a local maximum.
Long: SMA[t-1] > SMA[t-2] AND SMA[t-3] > SMA[t-2]. The last finished SMA sample forms a local minimum.
Stop Management
Short: Stop level = highest high within HighLookback candles if the level is above the previous close. Exits when price touches the level.
Long: Stop level = lowest low within LowLookback candles if the level is below the previous close. Exits when price touches the level.
Position Rules: Always flips to the latest signal. When reversing, the strategy closes the existing position and opens the new one in a single market order sized to cover the previous exposure plus the desired volume.
Default Parameters
SmaPeriod = 5.
HighLookback = 5.
LowLookback = 5.
CandleType = 30-minute timeframe.
TradeVolume = 1 lot (applied to the Volume property on start).
Risk level: Moderate (tight stops but frequent trades).
Usage Notes
Works best on instruments with visible oscillations. Consider disabling trading around major news events to avoid false swings.
Optimise the SMA period and lookback windows for the targeted symbol and timeframe. Smaller settings increase sensitivity but also whipsaws.
The stop levels are recalculated only when a fresh turning signal appears. If a stop becomes invalid (e.g., high not above the previous close) it is discarded, preventing the strategy from placing protective levels too close to price.
Because exits rely on market orders, slippage may occur on fast moves. Combine with broker-side protective orders if the venue supports them.
The strategy does not use take-profit targets. To add them, extend the logic in ProcessCandle with additional conditions.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class MaSrTradingStrategy : Strategy
{
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<int> _momentumPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevClose; private decimal _prevEma; private bool _hasPrev;
private int _cooldown;
public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
public int MomentumPeriod { get => _momentumPeriod.Value; set => _momentumPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public MaSrTradingStrategy()
{
_emaPeriod = Param(nameof(EmaPeriod), 20).SetDisplay("EMA Period", "EMA lookback", "Indicators");
_momentumPeriod = Param(nameof(MomentumPeriod), 14).SetDisplay("Momentum", "Momentum period", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame()).SetDisplay("Candle Type", "Candle timeframe", "General");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevClose = default;
_prevEma = default;
_hasPrev = default;
_cooldown = default;
}
/// <inheritdoc />
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_hasPrev = false;
var ema = new ExponentialMovingAverage { Length = EmaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ema, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal ema)
{
if (candle.State != CandleStates.Finished) return;
if (!IsFormedAndOnlineAndAllowTrading()) return;
var close = candle.ClosePrice;
if (!_hasPrev) { _prevClose = close; _prevEma = ema; _hasPrev = true; return; }
if (_cooldown > 0)
{
_cooldown--;
_prevClose = close; _prevEma = ema;
return;
}
if (_prevClose <= _prevEma && close > ema && Position <= 0)
{
var volume = Volume + Math.Abs(Position);
BuyMarket(volume);
_cooldown = 6;
}
else if (_prevClose >= _prevEma && close < ema && Position >= 0)
{
var volume = Volume + Math.Abs(Position);
SellMarket(volume);
_cooldown = 6;
}
_prevClose = close; _prevEma = ema;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class ma_sr_trading_strategy(Strategy):
def __init__(self):
super(ma_sr_trading_strategy, self).__init__()
self._ema_period = self.Param("EmaPeriod", 20).SetDisplay("EMA Period", "EMA lookback", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))).SetDisplay("Candle Type", "Candle timeframe", "General")
self._prev_close = 0.0; self._prev_ema = 0.0; self._has_prev = False; self._cooldown = 0
@property
def ema_period(self): return self._ema_period.Value
@property
def candle_type(self): return self._candle_type.Value
def OnReseted(self):
super(ma_sr_trading_strategy, self).OnReseted()
self._prev_close = 0.0; self._prev_ema = 0.0; self._has_prev = False; self._cooldown = 0
def OnStarted2(self, time):
super(ma_sr_trading_strategy, self).OnStarted2(time)
self._has_prev = False; self._cooldown = 0
ema = ExponentialMovingAverage(); ema.Length = self.ema_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(ema, self.process_candle).Start()
def process_candle(self, candle, ema):
if candle.State != CandleStates.Finished: return
if not self.IsFormedAndOnlineAndAllowTrading(): return
close = float(candle.ClosePrice); ema_val = float(ema)
if not self._has_prev:
self._prev_close = close; self._prev_ema = ema_val; self._has_prev = True; return
if self._cooldown > 0:
self._cooldown -= 1; self._prev_close = close; self._prev_ema = ema_val; return
if self._prev_close <= self._prev_ema and close > ema_val and self.Position <= 0:
volume = self.Volume + abs(self.Position)
self.BuyMarket(volume); self._cooldown = 6
elif self._prev_close >= self._prev_ema and close < ema_val and self.Position >= 0:
volume = self.Volume + abs(self.Position)
self.SellMarket(volume); self._cooldown = 6
self._prev_close = close; self._prev_ema = ema_val
def CreateClone(self): return ma_sr_trading_strategy()