Открыть на GitHub

MultiTimeframeEmaAlignmentStrategy

Описание

MultiTimeframeEmaAlignmentStrategy — портирование советника 1h-4h-1d.mq4 из папки MQL/7713 на платформу StockSharp. Исходный робот анализирует три таймфрейма и проверяет, чтобы быстрая EMA находилась по одну сторону от медленной EMA, дополняя логику фиксированным стоп-лоссом, тейк-профитом и трейлинг-стопом. Реализация на C# использует высокоуровневые возможности StockSharp: подписку на свечи, привязку индикаторов и рыночные приказы.

Торговая логика

  • Подписка на три серии свечей: M1 (основной сигнал), M5 (фильтр) и M30 (подтверждение тренда).
  • На каждом таймфрейме рассчитывается пара EMA (по умолчанию 8 и 64).
  • Сигнал на покупку формируется, когда быстрые EMA на всех таймфреймах находятся выше медленных и не теряют импульс (текущее значение не ниже предыдущего и выше значения ShiftDepth свечей назад).
  • Сигнал на продажу требует, чтобы быстрые EMA находились ниже медленных и сохраняли нисходящий импульс.
  • Сделки открываются на закрытии свечи M1. Если уже есть позиция противоположного направления, она закрывается перед открытием нового ордера.

Параметр ShiftDepth имитирует сравнение MA с «сдвигом», которое присутствует в MQL-версии, и позволяет контролировать динамику показаний EMA.

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

  • TradeVolume задаёт объём сделки (по умолчанию 3 лота — как в оригинале).
  • Стоп-лосс и тейк-профит задаются в пунктах и переводятся в цену через PriceStep инструмента (при отсутствии шага используется значение 0.0001).
  • Трейлинг-стоп автоматически подтягивает стоп, когда позиция движется в прибыльную сторону.
  • Все защитные механизмы включаются и выключаются независимо, что соответствует флагам StopLossMode, TakeProfitMode, TrailingStopMode.

Параметры

Параметр Описание Значение по умолчанию
TradeVolume Объём рыночных заявок. 3
FastLength Период быстрой EMA. 8
SlowLength Период медленной EMA. 64
ShiftDepth Количество свечей для проверки импульса. 3
UseStopLoss Включить фиксированный стоп-лосс. true
StopLossPips Размер стоп-лосса в пунктах. 75
UseTakeProfit Включить тейк-профит. true
TakeProfitPips Размер тейк-профита в пунктах. 150
UseTrailingStop Включить трейлинг-стоп. true
TrailingStopPips Дистанция трейлинг-стопа в пунктах. 30
M1CandleType Тип свечей для таймфрейма M1. 1m
M5CandleType Тип свечей для таймфрейма M5. 5m
M30CandleType Тип свечей для таймфрейма M30. 30m

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

  1. Перед запуском убедитесь, что по всем таймфреймам есть исторические данные, чтобы EMA успели сформироваться.
  2. Не снижайте ShiftDepth ниже 2, иначе проверка импульса будет недостаточно надёжной.
  3. Если оставить включённым только трейлинг-стоп, уровень стопа появится после движения позиции в прибыль.
  4. StockSharp обрабатывает сигналы по закрытию свечей, поэтому результаты могут немного отличаться от тикового исполнения в MT4, но логика согласования тренда сохраняется.

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

  • Индикаторы подключаются через метод Bind, без прямого обращения к их буферам.
  • Сделки открываются и закрываются методами BuyMarket / SellMarket, что заменяет прямые вызовы OrderSend.
  • Почтовые уведомления, параметры проскальзывания и другие сервисные функции из MQL-версии не реализованы.

Файлы

  • CS/MultiTimeframeEmaAlignmentStrategy.cs — реализация стратегии.
  • README.md — документация на английском языке.
  • README_zh.md — документация на китайском языке.
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>
/// Multi-Timeframe EMA Alignment strategy - fast/slow EMA crossover with trend EMA filter.
/// Buys when fast EMA crosses above slow EMA while close is above trend EMA.
/// Sells when fast EMA crosses below slow EMA while close is below trend EMA.
/// </summary>
public class MultiTimeframeEmaAlignmentStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _trendPeriod;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevFast;
	private decimal _prevSlow;
	private bool _hasPrev;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int TrendPeriod { get => _trendPeriod.Value; set => _trendPeriod.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public MultiTimeframeEmaAlignmentStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 10)
			.SetDisplay("Fast EMA", "Fast EMA period", "Indicators");

		_slowPeriod = Param(nameof(SlowPeriod), 30)
			.SetDisplay("Slow EMA", "Slow EMA period", "Indicators");

		_trendPeriod = Param(nameof(TrendPeriod), 100)
			.SetDisplay("Trend EMA", "Trend EMA period", "Indicators");

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

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

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

		_hasPrev = false;

		var fast = new ExponentialMovingAverage { Length = FastPeriod };
		var slow = new ExponentialMovingAverage { Length = SlowPeriod };
		var trend = new ExponentialMovingAverage { Length = TrendPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(fast, slow, trend, ProcessCandle)
			.Start();
	}

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

		var close = candle.ClosePrice;

		if (!_hasPrev)
		{
			_prevFast = fast;
			_prevSlow = slow;
			_hasPrev = true;
			return;
		}

		if (_prevFast <= _prevSlow && fast > slow && close > trend && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		else if (_prevFast >= _prevSlow && fast < slow && close < trend && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}

		_prevFast = fast;
		_prevSlow = slow;
	}
}