Gaps — это прямой порт советника MetaTrader 4 gaps.mq4. Стратегия отслеживает 15-минутные свечи и ищет открытия, кот
орые происходят за пределами диапазона предыдущей свечи. При появлении такого гэпа система немедленно входит в рынок в расчет
е на возврат цены к диапазону.
Версия для StockSharp следует исходной логике и использует высокоуровневый API подписки на свечи. Управление сделками произ
водится рыночными ордерами без постановки фиксированных защитных заявок, что соответствует поведению оригинального кода MQL.
Торговые правила
Подписаться на 15-минутные свечи (тип регулируется параметром CandleType).
Сохранить максимум и минимум последней завершённой свечи.
Если цена открытия вышеpreviousHigh + gapBuffer, открыть короткую позицию.
Если цена открытия нижеpreviousLow - gapBuffer, открыть длинную позицию.
На каждой свече допускается только одна сделка. После отправки ордера стратегия ждёт следующую свечу, прежде чем генериров
ать новый сигнал.
Компонент спреда использует текущие котировки bid/ask, когда они доступны. При отсутствии данных стратегия применяет один ша
г цены в качестве консервативного буфера.
Параметры
Параметр
Значение по умолчанию
Описание
MinGapSize
1
Минимальный размер гэпа в шагах цены, необходимый для открытия позиции.
GapVolume
0.1
Объём рыночного ордера, отправляемого при срабатывании сигнала.
CandleType
15m TimeFrame
Тип свечей, используемых для расчётов (по умолчанию 15 минут).
Все параметры зарегистрированы через StrategyParam<T> и поддерживают оптимизацию в StockSharp Designer и других инструментах.
Особенности реализации
Используется SubscribeCandles с Bind, чтобы обрабатывать только завершённые свечи.
Диапазон предыдущей свечи сохраняется во внутренних переменных, что исключает необходимость дополнительных коллекций.
За счёт хранения времени открытия свечи, вызвавшей сделку, предотвращаются повторные ордера в рамках того же бара.
На график выводятся свечи и сделки стратегии для оперативной визуальной проверки.
Отличия от версии MQL
В оригинальном советнике значения для стоп-лосса и тейк-профита передавались в неверные параметры, поэтому сделки выполнялись
без защитных заявок. Перенос сохранён в таком же виде и не добавляет стопов.
Оценка спреда теперь использует актуальные котировки bid/ask и только при их отсутствии возвращается к минимальному шагу цены.
Требования
Доступ к свечным данным выбранного инструмента в среде StockSharp.
Поток котировок Level1 не обязателен, но повышает точность вычисления буфера по спреду.
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()