Открыть на GitHub

Утренняя пробойная стратегия Early Bird

Обзор

Early Bird Range Breakout — порт торгового советника MetaTrader 4 earlyBird1 на C#. Стратегия измеряет экстремумы заданного «ночного» диапазона, применяет 14-периодный RSI по ценам открытия для определения допускаемого направления и входит по первому пробою диапазона после начала торговой сессии. Перенос сохранил дневные ограничения на количество сделок по каждому направлению, волатильностный трейлинг и принудительное закрытие в конце дня.

Логика стратегии

Формирование диапазона

  • Временное окно – максимум и минимум рассчитываются между Range Start Hour и Range End Hour (с учётом автоматической корректировки на летнее/зимнее время). Каждая свеча, пересекающая окно, обновляет границы диапазона.
  • Буфер входа – к максимуму добавляется, а от минимума вычитается заданное количество пунктов, что повторяет MT4-параметр ±2/Fakt.
  • Сброс каждый день – границы диапазона, уровни входа и счётчики сделок обнуляются с первой завершённой свечи нового торгового дня.

Фильтр направления

  • RSI по ценам открытия – индикатор питается значениями открытия свечи, полностью повторяя вызов iRSI(..., PRICE_OPEN) в исходном советнике.
  • Выбор стороны – если RSI > 50, активируется только длинный сценарий, если RSI ≤ 50 – только короткий. Это гарантирует, что за свечу генерируется максимум один торговый сигнал, как и в оригинале.

Правила входа

  • Торговая сессия – сделки открываются только в рабочие дни, когда текущее время находится между Session Start и Session End, и диапазон уже построен.
  • Одна попытка на направление – после открытия длинной (или короткой) позиции соответствующий триггер отключается до конца дня. Логика полностью повторяет проверку истории сделок в MQL.
  • Переключатель хеджа – параметр Allow Hedging позволяет при наличии обратной позиции сразу перевернуться, докупив/допродав нужный объём. При отключённом хедже новые заявки подаются только при нулевой позиции.

Правила выхода

  • Фиксированные стоп и цель – расстояния в пунктах. Итоговый take-profit ограничивается стопом и шириной диапазона, что соответствует формуле MathMin из MT4.
  • Трейлинг по волатильности – если текущая свеча шире среднего диапазона за 16 свечей, умноженного на Trailing Risk, и цена прошла не меньше Trailing Trigger, стоп подтягивается на расстояние первоначального стопа, а take-profit сдвигается на половину трейлингового шага.
  • Закрытие дня – в час, заданный параметром Closing Hour, прибыльные сделки закрываются, убыточные переводятся в безубыток (take-profit на цену входа), что повторяет поведение оригинального советника.

Параметры

  • Auto Trading – глобальный переключатель автоторговли.
  • Allow Hedging – разрешает переворот позиции при открытой противоположной сделке.
  • Trade Direction – доступное направление: 0 – обе стороны, 1 – только лонг, 2 – только шорт.
  • Volume – объём рыночных заявок.
  • Take Profit (pips) – максимальная дистанция цели (ограничивается стопом и диапазоном).
  • Stop Loss (pips) – фиксированная дистанция защитного стопа.
  • Trailing Trigger (pips) – минимальный профит для активации трейлинга.
  • Trailing Risk – коэффициент, умножаемый на средний диапазон 16 свечей при оценке волатильности.
  • Entry Buffer (pips) – буфер в пунктах, добавляемый к границам диапазона.
  • Session Start Hour / Minute – начало рабочей сессии (время графика до учёта сезонного сдвига).
  • Session End Hour – окончание окна для новых сделок.
  • Closing Hour – время принудительного завершения позиций.
  • Range Start Hour / Range End Hour – границы расчёта утреннего диапазона.
  • Summer Time Start / Winter Time Start – дни года, в которые используется двухчасовой и одночасовой сдвиг (аналог полей Sommerzeit/Winterzeit).
  • RSI Length – период RSI (по умолчанию 14).
  • Candle Type – основной таймфрейм для подписки на свечи (по умолчанию 15 минут).

Дополнительные сведения

  • Размер пункта вычисляется по текущей цене (≥ 10 → 0.01, иначе 0.0001), точно как в расчёте Fakt у MT4.
  • Для оценки волатильности используется очередь из 16 завершённых свечей без учёта текущей.
  • StockSharp ведёт нетто-позицию, поэтому «хеджирование» реализуется через отправку объёма, который закрывает текущую позицию и тут же открывает противоположную.
  • В репозитории присутствует только C# версия; Python-реализация не создавалась.
using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Early Bird Range Breakout: N-bar high/low breakout with EMA filter and ATR stops.
/// </summary>
public class EarlyBirdRangeBreakoutStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _emaLength;
	private readonly StrategyParam<int> _atrLength;

	private decimal _prevHigh;
	private decimal _prevLow;
	private decimal _entryPrice;

	public EarlyBirdRangeBreakoutStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(8).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe.", "General");
		_emaLength = Param(nameof(EmaLength), 20)
			.SetDisplay("EMA Length", "Trend filter.", "Indicators");
		_atrLength = Param(nameof(AtrLength), 14)
			.SetDisplay("ATR Length", "ATR period.", "Indicators");
	}

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int EmaLength { get => _emaLength.Value; set => _emaLength.Value = value; }
	public int AtrLength { get => _atrLength.Value; set => _atrLength.Value = value; }

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

		_prevHigh = 0; _prevLow = 0; _entryPrice = 0;
	}

		protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevHigh = 0; _prevLow = 0; _entryPrice = 0;
		var ema = new ExponentialMovingAverage { Length = EmaLength };
		var atr = new AverageTrueRange { Length = AtrLength };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(ema, atr, ProcessCandle).Start();
		var area = CreateChartArea();
		if (area != null) { DrawCandles(area, subscription); DrawIndicator(area, ema); DrawOwnTrades(area); }
	}

	private void ProcessCandle(ICandleMessage candle, decimal emaVal, decimal atrVal)
	{
		if (candle.State != CandleStates.Finished) return;
		var close = candle.ClosePrice;
		if (_prevHigh == 0 || atrVal <= 0) { _prevHigh = candle.HighPrice; _prevLow = candle.LowPrice; return; }

		if (Position > 0)
		{
			if (close <= _entryPrice - atrVal * 1.5m || close >= _entryPrice + atrVal * 2.5m) { SellMarket(); _entryPrice = 0; }
		}
		else if (Position < 0)
		{
			if (close >= _entryPrice + atrVal * 1.5m || close <= _entryPrice - atrVal * 2.5m) { BuyMarket(); _entryPrice = 0; }
		}

		if (Position == 0)
		{
			if (close > _prevHigh && close > emaVal) { _entryPrice = close; BuyMarket(); }
			else if (close < _prevLow && close < emaVal) { _entryPrice = close; SellMarket(); }
		}
		_prevHigh = candle.HighPrice; _prevLow = candle.LowPrice;
	}
}