Открыть на GitHub

Стратегия Sample Detect Economic Calendar

Общее описание

Sample Detect Economic Calendar — порт оригинального советника MetaTrader SampleDetectEconomicCalendar.mq5. Стратегия отслеживает вручную заданные события экономического календаря и при приближении важной новости для выбранной валюты выставляет симметричную пару стоп-заявок. Логика стоп-лосса, тейк-профита и трейлинг-стопа повторяет исходный код.

В версии StockSharp нет прямого доступа к календарю MetaTrader, поэтому список событий задаётся пользователем через параметр CalendarDefinition.

Принцип работы

  1. Подписка на котировки Level1 для получения лучших цен Bid/Ask.
  2. При запуске каждая строка из CalendarDefinition разбирается в отдельное событие.
  3. Для событий с важностью High и валютой, соответствующей BaseCurrency, стратегия:
    • ждёт наступления окна за LeadMinutes минут до релиза;
    • рассчитывает объём ордеров (фиксированный или по риску);
    • выставляет buy stop и sell stop на расстояниях BuyDistancePoints и SellDistancePoints от текущих цен.
  4. После публикации новости висящие заявки отменяются по истечении PostMinutes либо при достижении общей границы ExpiryMinutes.
  5. При исполнении одной заявки противоположная снимается, а позиция сопровождается стоп-лоссом, тейк-профитом и трейлинг-стопом.

Параметры

Параметр Описание
TradeNews Включает постановку заявок вокруг новостей.
OrderVolume Фиксированный объём при отключённом управлении капиталом.
StopLossPoints Расстояние стоп-лосса в пунктах (0 — отключить).
TakeProfitPoints Расстояние тейк-профита в пунктах (0 — отключить).
TrailingStopPoints Шаг трейлинг-стопа в пунктах (0 — выключен).
ExpiryMinutes Максимальное время жизни заявок после релиза (минуты).
UseMoneyManagement Использовать риск-менеджмент на базе баланса.
RiskPercent Доля капитала, которую допускается потерять в одной сделке.
BuyDistancePoints Отступ для buy stop относительно Ask.
SellDistancePoints Отступ для sell stop относительно Bid.
LeadMinutes За сколько минут до новости размещать заявки.
PostMinutes Через сколько минут после новости снять неисполненные ордера.
BaseCurrency Валютный код события (по умолчанию USD).
CalendarDefinition Многострочное описание календаря.

Формат событий

Каждое событие записывается отдельной строкой:

yyyy-MM-dd HH:mm;CUR;High;Название события
  • yyyy-MM-dd HH:mm — время события в UTC (поддерживаются секунды и альтернативные форматы yyyy/MM/dd, dd.MM.yyyy).
  • CUR — валютный код (например, USD). Торгуются только события, совпадающие с BaseCurrency.
  • High — уровень важности (High, Medium, Low, Nfp). Сделки открываются только на High.
  • Название события — произвольная подпись, отображаемая в логах.

Пример:

2024-06-12 18:00;USD;High;FOMC Statement
2024-07-05 12:30;USD;Nfp;Non-Farm Payrolls

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

  • При выключенном UseMoneyManagement используется фиксированный объём OrderVolume.
  • При включённом UseMoneyManagement объём рассчитывается исходя из RiskPercent капитала и величины StopLossPoints. Соблюдаются биржевые ограничения по минимальному/максимальному объёму и шагу.
  • Трейлинг-стоп активируется после прохождения ценой TrailingStopPoints пунктов в прибыльную сторону и закрывает позицию при откате, также учитываются уровни стоп-лосса и тейк-профита.

Отличия от версии MetaTrader

  • События необходимо вводить вручную в параметр CalendarDefinition.
  • Каждый экземпляр стратегии обслуживает одну валютную пару.
  • Время истечения заявок реализовано внутренними таймерами PostMinutes/ExpiryMinutes, так как в StockSharp отсутствует режим ORDER_TIME_SPECIFIED.

Рекомендации по использованию

  1. Перед запуском заполните CalendarDefinition, время указывается в UTC.
  2. Активируйте TradeNews и настройте рисковые параметры.
  3. Убедитесь в наличии данных Level1, чтобы стратегия могла выставить ордера в нужный момент.
  4. Контролируйте журнал сообщений, чтобы подтверждать корректную постановку и отмену ордеров по каждому событию.
namespace StockSharp.Samples.Strategies;

using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;

/// <summary>
/// Sample Detect Economic Calendar strategy: Parabolic SAR trend with EMA filter.
/// Buys when close above both SAR and EMA, sells when close below both.
/// </summary>
public class SampleDetectEconomicCalendarStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _emaPeriod;

	private decimal _prevClose;
	private decimal _prevSar;
	private decimal _prevEma;
	private bool _hasPrev;

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

	public SampleDetectEconomicCalendarStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
		_emaPeriod = Param(nameof(EmaPeriod), 50)
			.SetGreaterThanZero()
			.SetDisplay("EMA Period", "EMA filter period", "Indicators");
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevClose = 0;
		_prevSar = 0;
		_prevEma = 0;
		_hasPrev = false;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevClose = 0;
		_prevSar = 0;
		_prevEma = 0;
		_hasPrev = false;
		var sar = new ParabolicSar { Acceleration = 0.01m, AccelerationMax = 0.1m };
		var ema = new ExponentialMovingAverage { Length = EmaPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(sar, ema, ProcessCandle).Start();
	}

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

		if (_hasPrev)
		{
			var aboveSar = candle.ClosePrice > sarValue;
			var belowSar = candle.ClosePrice < sarValue;
			var aboveEma = candle.ClosePrice > emaValue;
			var belowEma = candle.ClosePrice < emaValue;

			if (aboveSar && aboveEma && !(_prevClose > _prevSar && _prevClose > _prevEma) && Position <= 0)
				BuyMarket();
			else if (belowSar && belowEma && !(_prevClose < _prevSar && _prevClose < _prevEma) && Position >= 0)
				SellMarket();
		}

		_prevClose = candle.ClosePrice;
		_prevSar = sarValue;
		_prevEma = emaValue;
		_hasPrev = true;
	}
}