Открыть на GitHub

Стратегия ENewsLuckyw

Обзор

ENewsLuckyw — это конверсия советника MetaTrader e-News-Lucky$, построенная на времени выхода новостей. В заданный момент стратегия размещает симметричные отложенные buy stop и sell stop заявки вокруг текущей цены, поддерживает их актуальность при появлении новых баров и управляет позициями в точном соответствии с оригинальной логикой MQL.

Логика торговли

  • Запуск «вилок» по расписанию. В момент SetOrdersTime стратегия удаляет старые отложенные заявки, считывает цену закрытия текущей свечи и выставляет новые stop-заявки на расстоянии DistancePips.
  • Постоянное обновление цен. Пока обе заявки активны, на закрытии каждой свечи их уровни пересчитываются, чтобы центр «вилки» всегда следовал за рынком.
  • Подготовка к входу. Расчёт стоп-лосса и, при необходимости, тейк-профита происходит заранее, поэтому при открытии позиции защитные ордера отправляются сразу. Противоположная отложенная заявка отменяется, как только появляется позиция.
  • Трейлинг-стоп. Если параметр UseTrailing включён, защитный стоп двигается на величину TrailingStopPips, когда цена продвигается минимум на TrailingStepPips. Опция ProfitTrailing заставляет ждать, пока нереализованная прибыль не превысит дистанцию трейлинга — это аналог одноимённого флага в MQL.
  • Вечерняя зачистка. В момент DeleteOrdersTime стратегия отменяет все отложенные заявки и закрывает позицию, чтобы не переносить риск через ночь.

Параметры

Имя Описание
Volume Объём обеих отложенных заявок (в лотах).
StopLossPips Дистанция защитного стопа в пунктах (0 — без стопа).
TakeProfitPips Дистанция тейк-профита (0 — без цели).
DistancePips Отступ от цены для постановки stop-заявок.
UseTrailing Включает или отключает трейлинг-стоп.
ProfitTrailing Требует положительного результата, превышающего дистанцию трейлинга, перед его активацией.
TrailingStopPips Расстояние между ценой и трейлинг-стопом.
TrailingStepPips Минимальное улучшение цены, после которого стоп сдвигается дальше.
SetOrdersTime Время выставления отложенных заявок.
DeleteOrdersTime Время удаления заявок и закрытия позиции.
CandleType Тип свечей, по которым выполняется отслеживание времени и обновление уровней.

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

  1. Подключите стратегию к нужному инструменту и выберите размер свечи через CandleType (по умолчанию — минутные бары).
  2. Настройте время SetOrdersTime и DeleteOrdersTime под конкретное событие или торговую сессию.
  3. Подберите DistancePips, StopLossPips и TrailingStopPips под волатильность инструмента. Для форекс-символов убедитесь, что PriceStep настроен корректно.
  4. В стратегии используются стоп- и лимитные заявки для выхода. Если площадка их не поддерживает, замените их на рыночные операции или эмуляцию перед запуском на реальном счёте.
  5. Очистка выполняется один раз в день. Если торговля продолжается после полуночи по времени площадки, убедитесь, что расписание охватывает один торговый день.

Особенности конверсии

  • Перенесены все ключевые функции оригинала: SetOrders, ModifyOrders, DeleteOppositeOrders, TrailingPositions и финальное закрытие.
  • Учёт спреда при вычислении цен приблизительно воспроизводится через цену закрытия свечи, так как StockSharp нормализует цены по PriceStep.
  • Настройки звуковых оповещений, проверки номера счёта и цвета линий исключены — высокоуровневый API StockSharp не требует их для работы стратегии.
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>
/// ENews Lucky strategy - momentum breakout with ATR filter.
/// Buys when close breaks above recent high with strong momentum.
/// Sells when close breaks below recent low with strong momentum.
/// </summary>
public class ENewsLuckywStrategy : Strategy
{
	private readonly StrategyParam<int> _channelPeriod;
	private readonly StrategyParam<int> _momentumPeriod;
	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 int MomentumPeriod { get => _momentumPeriod.Value; set => _momentumPeriod.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public ENewsLuckywStrategy()
	{
		_channelPeriod = Param(nameof(ChannelPeriod), 15)
			.SetDisplay("Channel Period", "Highest/Lowest period", "Indicators");

		_momentumPeriod = Param(nameof(MomentumPeriod), 10)
			.SetDisplay("Momentum Period", "Momentum period", "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 momentum = new Momentum { Length = MomentumPeriod };

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

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

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

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

		// Breakout above midpoint with positive momentum
		if (_prevClose <= _prevMid && close > mid && momentum > 0 && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		// Breakout below midpoint with negative momentum
		else if (_prevClose >= _prevMid && close < mid && momentum < 0 && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}

		_prevClose = close;
		_prevMid = mid;
	}
}