The Escape strategy is a StockSharp port of the MetaTrader 4 expert advisor escape.mq4. The original robot trades a five-minute chart and reacts to mean-reversion opportunities: it buys when price falls below a short moving average and sells when price rallies above another fast average. Each position is protected by a fixed-distance take-profit and stop-loss expressed in MetaTrader points. The C# implementation keeps the same minimalist logic while exposing all tunable distances as strategy parameters.
Trading Logic
Initialization
Subscribe to the configurable CandleType series (five-minute candles by default).
Create two SimpleMovingAverage indicators with lengths 5 and 4 that are fed with candle open prices.
Compute the MetaTrader Point equivalent from Security.PriceStep; this value is reused to convert pip-style distances into absolute prices.
Per-candle processing
Only finished candles are processed via SubscribeCandles(...).WhenCandlesFinished(ProcessCandle).
The strategy first checks whether an existing position hit its stop-loss or take-profit by comparing the candle high/low with the recorded exit levels. When a level is breached the position is closed with a market order and duplicate exit orders are prevented through internal flags.
If the account is flat, previous values of the two SMAs are available, trading is allowed, and the portfolio holds enough capital (Portfolio.CurrentValue >= MinimumMarginPerLot * TradeVolume), the strategy evaluates entries:
Long entry – current close is below the previous 5-period SMA of opens.
Short entry – current close is above the previous 4-period SMA of opens.
When a signal triggers, the stop-loss and take-profit levels are calculated from the candle close using the configured point distances and stored for later monitoring.
Risk management
TradeVolume defines the lot size of every market order.
MinimumMarginPerLot approximates the AccountFreeMargin check from MetaTrader. If the available portfolio value is too small the entry is skipped and a diagnostic message is logged.
Parameters
Name
Default
Description
LongTakeProfitPoints
10
Take-profit distance for long positions in MetaTrader points. Set to 0 to disable the target.
ShortTakeProfitPoints
10
Take-profit distance for short positions in MetaTrader points. Set to 0 to disable the target.
LongStopLossPoints
1000
Stop-loss distance for long positions in MetaTrader points. Set to 0 to disable the protective stop.
ShortStopLossPoints
1000
Stop-loss distance for short positions in MetaTrader points. Set to 0 to disable the protective stop.
TradeVolume
0.2
Lot size used when sending market orders.
MinimumMarginPerLot
500
Approximate capital requirement per lot before opening a new trade.
CandleType
Five-minute time frame
Candle series that drives indicator updates and signal generation.
Implementation Notes
Indicators are updated manually inside ProcessCandle with candle open prices so that the stored values always represent the previous bar (mirroring the shift=1 arguments used in iMA).
Exit levels are tracked in decimal fields instead of creating additional collections, satisfying the high-level API guidelines.
Stops and targets are evaluated against candle extremes; because only OHLC data is available, the stop check is performed before the take-profit to emulate MetaTrader’s order priority as closely as possible.
The strategy draws candles together with both moving averages and own trades when a chart area is available, simplifying visual validation.
Differences vs. the MetaTrader version
MetaTrader attaches stop-loss and take-profit orders directly to tickets. The StockSharp port reproduces them by monitoring candle highs and lows and sending market exits; intrabar execution order cannot be guaranteed if both levels are touched within the same bar.
Entry prices are derived from the candle close that triggered the signal instead of the exact bid/ask used by MetaTrader, so slippage and spread handling must be configured at the connector level.
The AccountFreeMargin() guard is approximated through Portfolio.CurrentValue. Users with more detailed margin models can extend HasSufficientMargin if needed.
Cosmetic MQL settings such as colors, sounds, and slippage are omitted; the StockSharp version focuses on the core trading logic.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Escape Mean Reversion: SMA crossover with ATR stops.
/// </summary>
public class EscapeMeanReversionStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _smaLength;
private readonly StrategyParam<int> _atrLength;
private decimal _prevClose;
private decimal _entryPrice;
public EscapeMeanReversionStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Timeframe.", "General");
_smaLength = Param(nameof(SmaLength), 5)
.SetDisplay("SMA Length", "SMA period.", "Indicators");
_atrLength = Param(nameof(AtrLength), 14)
.SetDisplay("ATR Length", "ATR period.", "Indicators");
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int SmaLength
{
get => _smaLength.Value;
set => _smaLength.Value = value;
}
public int AtrLength
{
get => _atrLength.Value;
set => _atrLength.Value = value;
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevClose = 0;
_entryPrice = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevClose = 0;
_entryPrice = 0;
var sma = new SimpleMovingAverage { Length = SmaLength };
var atr = new AverageTrueRange { Length = AtrLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(sma, atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, sma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal smaVal, decimal atrVal)
{
if (candle.State != CandleStates.Finished)
return;
var close = candle.ClosePrice;
if (_prevClose == 0 || atrVal <= 0)
{
_prevClose = close;
return;
}
if (Position > 0)
{
if (close >= _entryPrice + atrVal * 2m || close <= _entryPrice - atrVal * 1.5m || close > smaVal)
{
SellMarket();
_entryPrice = 0;
}
}
else if (Position < 0)
{
if (close <= _entryPrice - atrVal * 2m || close >= _entryPrice + atrVal * 1.5m || close < smaVal)
{
BuyMarket();
_entryPrice = 0;
}
}
if (Position == 0)
{
if (close < smaVal && _prevClose >= smaVal)
{
_entryPrice = close;
BuyMarket();
}
else if (close > smaVal && _prevClose <= smaVal)
{
_entryPrice = close;
SellMarket();
}
}
_prevClose = close;
}
}
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 SimpleMovingAverage, AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class escape_mean_reversion_strategy(Strategy):
"""
Escape Mean Reversion: SMA crossover with ATR stops.
Buys when price crosses below SMA, sells when price crosses above SMA.
Exits at ATR-based TP/SL levels.
"""
def __init__(self):
super(escape_mean_reversion_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._sma_length = self.Param("SmaLength", 5) \
.SetDisplay("SMA Length", "SMA period", "Indicators")
self._atr_length = self.Param("AtrLength", 14) \
.SetDisplay("ATR Length", "ATR period", "Indicators")
self._prev_close = 0.0
self._entry_price = 0.0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(escape_mean_reversion_strategy, self).OnReseted()
self._prev_close = 0.0
self._entry_price = 0.0
def OnStarted2(self, time):
super(escape_mean_reversion_strategy, self).OnStarted2(time)
self._prev_close = 0.0
self._entry_price = 0.0
sma = SimpleMovingAverage()
sma.Length = self._sma_length.Value
atr = AverageTrueRange()
atr.Length = self._atr_length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(sma, atr, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, sma)
self.DrawOwnTrades(area)
def _process_candle(self, candle, sma_val, atr_val):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
sma_val = float(sma_val)
atr_val = float(atr_val)
if self._prev_close == 0.0 or atr_val <= 0:
self._prev_close = close
return
if self.Position > 0:
if (close >= self._entry_price + atr_val * 2.0 or
close <= self._entry_price - atr_val * 1.5 or
close > sma_val):
self.SellMarket()
self._entry_price = 0.0
elif self.Position < 0:
if (close <= self._entry_price - atr_val * 2.0 or
close >= self._entry_price + atr_val * 1.5 or
close < sma_val):
self.BuyMarket()
self._entry_price = 0.0
if self.Position == 0:
if close < sma_val and self._prev_close >= sma_val:
self._entry_price = close
self.BuyMarket()
elif close > sma_val and self._prev_close <= sma_val:
self._entry_price = close
self.SellMarket()
self._prev_close = close
def CreateClone(self):
return escape_mean_reversion_strategy()