Открыть на GitHub

Сетка OverHedge V2

OverHedge V2 — это хеджирующая мартингейл-сетка, которая по очереди открывает покупки и продажи и наращивает объём каждой следующей сделки. Направление стартового шага определяется сравнением быстрой и медленной экспоненциальных средних (EMA). После запуска цикла стратегия фиксирует текущую цену Bid, строит туннель вокруг неё и открывает первую сделку по направлению EMA. Противоположная заявка активируется, когда цена смещается на заданное расстояние.

Каждое новое звено сетки увеличивает объём на множитель, компенсируя просадку предыдущих позиций и ускоряя выход в плюс при возврате цены. Стратегия ведёт раздельный учёт длинных и коротких объёмов, использует котировки Best Bid/Best Ask для запуска сделок и закрывает весь цикл при достижении целевой прибыли или при активации параметра остановки.

Как это работает

  1. Фильтр направления. На закрытых свечах рассчитываются EMA. Если быстрая EMA выше медленной, цикл стартует с покупки, иначе — с продажи.
  2. Инициализация цикла. Фиксируется текущий Bid и вычисляются границы туннеля с учётом спреда и параметра ширины. Первая сделка отправляется по направлению фильтра, противоположная сторона ждёт касания границы.
  3. Расширение сетки. Если цена идёт против позиции, открываются новые сделки: покупка, продажа, покупка и т.д. Объём каждой следующей заявки умножается на коэффициент хеджирования.
  4. Фиксация прибыли. Нереализованная прибыль отслеживается по лучшим ценам покупки/продажи. При достижении целевого значения или при включённом Shutdown Grid все позиции закрываются рыночными ордерами.
  5. Учёт экспозиции. Стратегия хранит среднюю цену и объём по длинной и короткой стороне, чтобы корректно считать прибыль и не дублировать заявки, пока предыдущие ещё исполняются.

Параметры по умолчанию

  • Base Volume = 0.1 лота — объём первой сделки в цикле.
  • Hedge Multiplier = 2.0 — во сколько раз увеличивается объём каждой следующей заявки.
  • Tunnel Width (points) = 20 — дополнительная ширина туннеля в пунктах поверх текущего спреда.
  • Profit Target = 100 — целевая нереализованная прибыль в валюте счёта, при которой цикл закрывается.
  • Short EMA = 8 — период быстрой EMA.
  • Long EMA = 21 — период медленной EMA.
  • Candle Type = 1 минута — таймфрейм для расчёта EMA.
  • Shutdown Grid = false — при значении true стратегия закрывает все позиции и перестаёт торговать.

Дополнительные замечания

  • Для корректной работы требуются котировки уровня Level 1 (лучшие Bid/Ask). Широкий спред автоматически расширяет туннель.
  • Объём заявок нормализуется под шаг объёма инструмента, чтобы избежать отказов биржи.
  • Мартингейл-множитель увеличивает нагрузку на депозит. Длительные тренды без откатов могут привести к крупной просадке.
  • Для возобновления работы после остановки верните параметр Shutdown Grid в false или перезапустите стратегию.
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>
/// OverHedge V2 Grid strategy using RSI mean reversion with grid averaging.
/// Buy when RSI is oversold, sell when RSI is overbought.
/// </summary>
public class OverHedgeV2GridStrategy : Strategy
{
	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<decimal> _oversold;
	private readonly StrategyParam<decimal> _overbought;
	private readonly StrategyParam<DataType> _candleType;

	public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
	public decimal Oversold { get => _oversold.Value; set => _oversold.Value = value; }
	public decimal Overbought { get => _overbought.Value; set => _overbought.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public OverHedgeV2GridStrategy()
	{
		_rsiPeriod = Param(nameof(RsiPeriod), 14)
			.SetDisplay("RSI Period", "RSI period", "Indicators");

		_oversold = Param(nameof(Oversold), 30m)
			.SetDisplay("Oversold", "RSI oversold level", "Indicators");

		_overbought = Param(nameof(Overbought), 70m)
			.SetDisplay("Overbought", "RSI overbought level", "Indicators");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
	}

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

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

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		var rsi = new RelativeStrengthIndex { Length = RsiPeriod };

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

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

		if (rsiValue <= Oversold && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		else if (rsiValue >= Overbought && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}
	}
}