Открыть на GitHub

Стратегия возврата гэпов

Обзор

Gaps — это прямой порт советника MetaTrader 4 gaps.mq4. Стратегия отслеживает 15-минутные свечи и ищет открытия, кот орые происходят за пределами диапазона предыдущей свечи. При появлении такого гэпа система немедленно входит в рынок в расчет е на возврат цены к диапазону.

Версия для StockSharp следует исходной логике и использует высокоуровневый API подписки на свечи. Управление сделками произ водится рыночными ордерами без постановки фиксированных защитных заявок, что соответствует поведению оригинального кода MQL.

Торговые правила

  1. Подписаться на 15-минутные свечи (тип регулируется параметром CandleType).
  2. Сохранить максимум и минимум последней завершённой свечи.
  3. Когда формируется новая свеча:
    • Рассчитать буфер гэпа: (MinGapSize + spreadInSteps) * pointValue.
    • Если цена открытия выше previousHigh + gapBuffer, открыть короткую позицию.
    • Если цена открытия ниже previousLow - gapBuffer, открыть длинную позицию.
  4. На каждой свече допускается только одна сделка. После отправки ордера стратегия ждёт следующую свечу, прежде чем генериров ать новый сигнал.

Компонент спреда использует текущие котировки 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;
	}
}