Открыть на GitHub

Стратегия GBP 9 AM с отложенными ордерами

Стратегия представляет собой перенос эксперта MetaTrader 4 из файла MQL/7687/Gbp9am.mq4 в экосистему StockSharp. Она воспроизводит утренний пробой Лондона: в момент фикса 9:00 по Лондону выставляются два встречных отложенных ордера, а в рынке одновременно находится не более одной позиции.

Торговая идея

  1. В заданный час и минуту стратегия фиксирует цену последней завершённой свечи.
  2. Над ценой размещается buy stop, под ценой — sell stop. Ордера имеют общий объём, для каждого задаётся отдельный стоп-лосс, а take-profit используется общий.
  3. После исполнения одного из ордеров противоположный мгновенно отменяется, чтобы избежать одновременных позиций в обе стороны.
  4. Открытая позиция сопровождается синтетическими стоп-лоссом и тейк-профитом, которые проверяются на каждой завершённой свече.
  5. При необходимости можно задать час принудительного закрытия, чтобы к окончанию сессии убрать все ордера и позиции.
  6. Если оба отложенных ордера сняты без сделки или время ушло от окна наблюдения, стратегия, как и оригинальный эксперт, активируется снова на следующий день.

Для расчёта пунктов используется минимальный шаг цены инструмента. Если брокер котирует с дробными пунктами (3 или 5 знаков после запятой), алгоритм автоматически пересчитывает расстояния в десятые доли пункта.

Параметры

Параметр Описание
Volume Объём ордеров (лоты), одинаковый для buy stop и sell stop.
LookHour Час, соответствующий 9:00 по Лондону в часовом поясе данных.
LookMinute Минута внутри часа наблюдения, когда фиксируется цена.
CloseHour Час, когда стратегия принудительно закрывает сделки и снимает заявки.
UseCloseHour Включает или отключает механизм ежедневного закрытия.
TakeProfitPips Размер тейк-профита в пунктах для обоих направлений.
BuyDistancePips Расстояние в пунктах от цены фикса до buy stop.
SellDistancePips Расстояние в пунктах от цены фикса до sell stop.
BuyStopLossPips Стоп-лосс в пунктах для длинной позиции.
SellStopLossPips Стоп-лосс в пунктах для короткой позиции.
CandleType Тип свечей, по которым ведётся тайминг и контроль рисков (по умолчанию 1 минута).

Особенности работы

  • Обрабатываются только свечи со статусом Finished, что исключает повторные сигналы внутри бара.
  • Цена ордеров округляется до ближайшего шага цены инструмента.
  • Логика повторной подготовки ордеров воспроизводит флаг clear_to_send из MQL: после выставления дневного «страддла» повторная постановка возможна лишь тогда, когда нет активных отложенных ордеров и позиции, а текущее время либо находится вне часа наблюдения (при этом минута не меньше LookMinute), либо находится за час до следующего сигнала и отличается менее чем на 10 минут.
  • При активном UseCloseHour в момент наступления CloseHour стратегия закрывает позицию рыночным ордером и отменяет все заявки.
  • Расчёт расстояний основан на свечных данных, поэтому фактические уровни могут незначительно отличаться от тикового исполнения в MetaTrader, особенно при широком спреде.

Управление рисками

Перенос сохранил статические стопы и цели оригинального эксперта, без трейлинг-стопов и доливок. В OnStarted вызывается StartProtection(), что снижает риск неожиданного зависания позиции при обрыве соединения.

Использование

  1. Настройте параметры Volume, LookHour и LookMinute в соответствии с часовым поясом поставщика котировок.
  2. Отрегулируйте расстояния в пунктах под спред и волатильность инструмента.
  3. Запустите стратегию на GBPUSD (или другом валютном инструменте) до наступления лондонской сессии.
  4. Наблюдайте за сделками и позицией на автоматически строящейся диаграмме StockSharp.

Реализация следует требованиям AGENTS.md: применяется высокоуровневое API подписки на свечи, параметры оформлены через StrategyParam, отсутствуют низкоуровневые обходы истории.

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>
/// GBP 9AM strategy - session high/low breakout.
/// Buys when close crosses above the Highest channel.
/// Sells when close crosses below the Lowest channel.
/// Uses midpoint crossing for exits.
/// </summary>
public class Gbp9AmStrategy : Strategy
{
	private readonly StrategyParam<int> _channelPeriod;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevClose;
	private decimal _prevMid;
	private bool _hasPrev;

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

	public Gbp9AmStrategy()
	{
		_channelPeriod = Param(nameof(ChannelPeriod), 12)
			.SetDisplay("Channel Period", "Highest/Lowest lookback", "Indicators");

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

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
	protected override void OnReseted() { base.OnReseted(); _prevClose = 0m; _prevMid = 0m; _hasPrev = false; }

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

		_hasPrev = false;

		var highest = new Highest { Length = ChannelPeriod };
		var lowest = new Lowest { Length = ChannelPeriod };

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

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

		var close = candle.ClosePrice;
		var mid = (highest + lowest) / 2;

		if (!_hasPrev)
		{
			_prevClose = close;
			_prevMid = mid;
			_hasPrev = true;
			return;
		}

		if (_prevClose <= _prevMid && close > mid && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		else if (_prevClose >= _prevMid && close < mid && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}

		_prevClose = close;
		_prevMid = mid;
	}
}