The Gap Reversion Strategy is a direct port of the MetaTrader 4 expert advisor gaps.mq4. The system monitors 15-minute candles and loo
ks for opening gaps that occur outside the previous candle's high/low range. When such a gap appears, the strategy immediately e
nters the market in the direction of the expected mean reversion move.
The StockSharp version follows the original logic while relying on the high-level candle subscription API. All trade management is
done with market orders and no fixed protective orders are placed, mirroring the behaviour found in the MQL code.
Trading Rules
Subscribe to 15-minute candles (configurable through the CandleType parameter).
Keep the previous completed candle's high and low.
When a new candle starts:
Calculate the gap buffer: (MinGapSize + spreadInSteps) * pointValue.
If the open price is abovepreviousHigh + gapBuffer, open a short position.
If the open price is belowpreviousLow - gapBuffer, open a long position.
Only one trade per candle is allowed. Once an order is placed, the strategy waits for the next candle before generating a new s
ignal.
The spread component uses the current best bid/ask if available. When no quote data is provided, the strategy falls back to a sin
gle price step as a conservative buffer.
Parameters
Parameter
Default
Description
MinGapSize
1
Minimum gap size in price steps that must be exceeded before sending an order.
GapVolume
0.1
Order volume for market entries triggered by gaps.
CandleType
15m TimeFrame
Candle type used for calculations (defaults to 15-minute candles).
All parameters are registered as StrategyParam<T> and support optimisation inside StockSharp Designer or other tools.
Implementation Notes
Uses SubscribeCandles with Bind to process finished candles only.
Remembers the previous candle's range to avoid recalculating data series.
Blocks duplicate orders on the same candle by tracking the open time of the bar that triggered the trade.
Chart output draws the subscribed candles and the strategy trades for quick visual inspection.
Differences from the MQL Version
Take-profit and stop-loss levels were not set correctly in the original EA (the MQL code passed values to the wrong parameters)
. The StockSharp port therefore keeps the behaviour of running without protective orders.
Spread handling now checks real-time bid/ask quotes when available, providing a more adaptive buffer.
Requirements
StockSharp API with access to candle data for the selected instrument.
Level1 quotes are optional but improve spread detection.
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>
/// Gap Reversion strategy - detects gap openings and trades mean reversion.
/// Buys when candle opens below previous low (gap down reversion).
/// Sells when candle opens above previous high (gap up reversion).
/// Uses EMA as trend filter for exits.
/// </summary>
public class GapReversionStrategy : Strategy
{
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevHigh;
private decimal _prevLow;
private bool _hasPrev;
public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public GapReversionStrategy()
{
_emaPeriod = Param(nameof(EmaPeriod), 20)
.SetDisplay("EMA Period", "EMA trend filter", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
protected override void OnReseted() { base.OnReseted(); _prevHigh = 0m; _prevLow = 0m; _hasPrev = false; }
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();
StartProtection(
takeProfit: new Unit(2, UnitTypes.Percent),
stopLoss: new Unit(1, UnitTypes.Percent)
);
}
private void ProcessCandle(ICandleMessage candle, decimal ema)
{
if (candle.State != CandleStates.Finished)
return;
if (!_hasPrev)
{
_prevHigh = candle.HighPrice;
_prevLow = candle.LowPrice;
_hasPrev = true;
return;
}
var open = candle.OpenPrice;
var close = candle.ClosePrice;
// Gap down reversion - open below previous low, expect bounce
if (open < _prevLow && close > ema && Position == 0)
BuyMarket();
// Gap up reversion - open above previous high, expect pullback
else if (open > _prevHigh && close < ema && Position == 0)
SellMarket();
_prevHigh = candle.HighPrice;
_prevLow = candle.LowPrice;
}
}
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
from StockSharp.Messages import Unit, UnitTypes
class gap_reversion_strategy(Strategy):
def __init__(self):
super(gap_reversion_strategy, self).__init__()
self._ema_period = self.Param("EmaPeriod", 20).SetDisplay("EMA Period", "EMA trend filter", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Candle timeframe", "General")
self._prev_high = 0.0
self._prev_low = 0.0
self._has_prev = False
@property
def ema_period(self): return self._ema_period.Value
@property
def candle_type(self): return self._candle_type.Value
def OnReseted(self):
super(gap_reversion_strategy, self).OnReseted()
self._prev_high = 0.0
self._prev_low = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(gap_reversion_strategy, self).OnStarted2(time)
self._has_prev = False
ema = ExponentialMovingAverage()
ema.Length = self.ema_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(ema, self.process_candle).Start()
self.StartProtection(takeProfit=Unit(2, UnitTypes.Percent), stopLoss=Unit(1, UnitTypes.Percent))
def process_candle(self, candle, ema):
if candle.State != CandleStates.Finished:
return
if not self._has_prev:
self._prev_high = float(candle.HighPrice)
self._prev_low = float(candle.LowPrice)
self._has_prev = True
return
op = float(candle.OpenPrice)
close = float(candle.ClosePrice)
ema_val = float(ema)
if op < self._prev_low and close > ema_val and self.Position == 0:
self.BuyMarket()
elif op > self._prev_high and close < ema_val and self.Position == 0:
self.SellMarket()
self._prev_high = float(candle.HighPrice)
self._prev_low = float(candle.LowPrice)
def CreateClone(self):
return gap_reversion_strategy()