Открыть на GitHub

Стратегия Gaps

Стратегия на основе ценового действия, которая реагирует на разрывы между последовательными свечами. После появления новой свечи алгоритм проверяет, ушла ли цена открытия за пределы экстремумов предыдущей свечи на заданное количество пунктов, входит в противоположном направлении и сопровождает сделку фиксированными стопами, тейк-профитом и при необходимости ступенчатым трейлингом.

Логика работы

  1. Стратегия отслеживает один инструмент в выбранном таймфрейме.
  2. После закрытия каждой свечи сравниваются цены открытия двух последних баров:
    • если открытие ниже минимума прошлой свечи на величину больше GapPips, открывается длинная позиция в расчёте на откат;
    • если открытие выше максимума прошлой свечи на величину больше GapPips, открывается короткая позиция с ожиданием снижения.
  3. Управление позицией выполняется полностью внутри стратегии:
    • стоп-лосс ставится на расстоянии StopLossPips пунктов от цены входа (ниже для лонга, выше для шорта);
    • тейк-профит фиксируется на расстоянии TakeProfitPips от цены входа по направлению позиции;
    • трейлинг-стоп активируется, когда цена проходит TrailingStopPips + TrailingStepPips пунктов в прибыль, после чего стоп подтягивается на расстояние TrailingStopPips от локального максимума/минимума и двигается ступенями не меньше TrailingStepPips.
  4. Все защитные уровни проверяются на каждой завершённой свече по максимуму и минимуму, поэтому даже внутридневные касания учитываются корректно.
  5. Перед новым входом отменяются активные заявки, а переворот автоматически закрывает противоположную позицию.

Параметры

  • OrderVolume = 0.1 — объём сделки в лотах.
  • StopLossPips = 50 — расстояние от точки входа до стоп-лосса в пунктах (0 — стоп отключён).
  • TakeProfitPips = 50 — расстояние до тейк-профита в пунктах (0 — тейк отключён).
  • TrailingStopPips = 5 — размер трейлинг-стопа в пунктах (0 — трейлинг выключен).
  • TrailingStepPips = 5 — минимальный шаг цены в пунктах для переноса трейлинг-стопа.
  • GapPips = 1 — минимальный размер разрыва открытия, необходимый для сигнала.
  • CandleType = часовой таймфрейм — свечи, по которым ищутся гэпы и контролируются риски.

Особенности реализации

  • Параметры в пунктах переводятся в абсолютные цены через шаг цены инструмента; для пяти- и трёхзнаковых форекс-котировок выполняется корректировка до реального размера пункта.
  • Если включён трейлинг-стоп (TrailingStopPips > 0), параметр TrailingStepPips также должен быть положительным; при нарушении стратегия завершит запуск с ошибкой, как и оригинальная версия на MQL.
  • Проверки защитных уровней выполняются только на закрытых свечах в соответствии с требованиями высокоуровневого API StockSharp.
  • Для выхода используются рыночные заявки, отдельные стоп- или лимитные ордера в стакан не выставляются.
  • Настройки по умолчанию ориентированы на валютный рынок; при торговле другими активами корректируйте размеры пунктов и таймфрейм под волатильность инструмента.
using System;
using System.Collections.Generic;

using Ecng.Common;

using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Gap trading strategy. Detects price gaps between candles and trades the gap fill.
/// </summary>
public class GapsStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<decimal> _gapPercent;

	private decimal? _prevClose;

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public decimal GapPercent
	{
		get => _gapPercent.Value;
		set => _gapPercent.Value = value;
	}

	public GapsStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe", "General");

		_gapPercent = Param(nameof(GapPercent), 0.05m)
			.SetDisplay("Gap Percent", "Minimum gap size as percentage", "Trading");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevClose = null;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_prevClose = null;

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(ProcessCandle)
			.Start();

		StartProtection(
			takeProfit: new Unit(2, UnitTypes.Percent),
			stopLoss: new Unit(1, UnitTypes.Percent)
		);

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawOwnTrades(area);
		}
	}

	private void ProcessCandle(ICandleMessage candle)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (_prevClose == null)
		{
			_prevClose = candle.ClosePrice;
			return;
		}

		var prevClose = _prevClose.Value;
		var open = candle.OpenPrice;
		var close = candle.ClosePrice;
		_prevClose = close;

		if (prevClose == 0)
			return;

		var gapPct = (open - prevClose) / prevClose * 100;

		// Gap up detected - sell expecting gap fill
		if (gapPct > GapPercent && Position == 0)
		{
			SellMarket();
		}
		// Gap down detected - buy expecting gap fill
		else if (gapPct < -GapPercent && Position == 0)
		{
			BuyMarket();
		}
	}
}