Открыть на GitHub

Стратегия Graal EMA Momentum

Эта стратегия представляет собой конвертацию советника MetaTrader 4 0Graal-CROSSmuvingi. Она отслеживает пересечения экспоненциальных средних: быстрая EMA считается по ценам закрытия, а медленная EMA — по ценам открытия. Сделки совершаются только тогда, когда моментум подтверждает силу пробоя, а фиксированный тейк-профит в пунктах повторяет логику исходного советника.

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

  1. Быстрая EMA по закрытию отражает текущее направление цены.
  2. Медленная EMA по открытию обеспечивает плавную базовую линию для пересечения.
  3. Осциллятор Momentum (период 14) измеряет ускорение движения относительно уровня 100. Сигнал считается действительным, если моментум отклоняется от 100 больше заданного фильтра и продолжает усиливаться.
  4. Тейк-профит фиксирует прибыль после прохождения заданного расстояния в пунктах (TakeProfitPoints).

Условия входа

  • Покупка
    • На текущей завершенной свече быстрая EMA выше медленной, а на предыдущей свече была ниже либо равна.
    • Значение моментума (минус 100) превышает порог MomentumFilter и выше предыдущего значения моментума.
    • Открытые короткие позиции закрываются перед открытием лонга. Объем заявки равен Volume плюс количество, необходимое для переворота.
  • Продажа
    • Быстрая EMA опускается ниже медленной, в то время как на предыдущей свече была выше либо равна.
    • Моментум (минус 100) ниже отрицательного порога MomentumFilter и меньше предыдущего значения моментума.
    • Длинные позиции закрываются перед открытием шорта. Объем заявки равен Volume плюс количество для закрытия лонга.

Условия выхода

  • Позиция закрывается, когда цена достигает тейк-профита (TakeProfitPoints * PriceStep).
  • Новый противоположный сигнал немедленно разворачивает позицию, так как объем ордера включает текущий размер позиции.

Параметры

Параметр Описание Значение по умолчанию
FastPeriod Период EMA по ценам закрытия. 13
SlowPeriod Период EMA по ценам открытия. 34
MomentumPeriod Глубина расчета моментума. 14
MomentumFilter Минимальное отклонение моментума от 100. 0.1
TakeProfitPoints Дистанция до тейк-профита в пунктах (умножается на PriceStep). 200
CandleType Тип свечей для расчётов (по умолчанию 15 минут). 15-минутные свечи
Volume Базовый торговый объем, наследуется от базового класса. 1

Особенности реализации

  • Обработка сигналов ведется только по завершенным свечам (CandleStates.Finished).
  • Подписка на свечи выполняется через SubscribeCandles, а индикаторы подключаются высокоуровневым API.
  • Медленная EMA обновляется вручную значениями открытия внутри колбэка, чтобы воспроизвести использование PRICE_OPEN в MT4.
  • Контроль тейк-профита отслеживает экстремумы свечи, что имитирует точечный выход оригинального советника.
  • Метод StartProtection() запускается при старте, защищая от неожиданных позиций до включения логики.
using System;

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

namespace StockSharp.Samples.Strategies;

public class GraalEmaMomentumStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _momentumPeriod;
	private readonly StrategyParam<int> _cooldownCandles;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevFast;
	private decimal _prevSlow;
	private bool _hasPrev;
	private int _cooldownRemaining;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int MomentumPeriod { get => _momentumPeriod.Value; set => _momentumPeriod.Value = value; }
	public int CooldownCandles { get => _cooldownCandles.Value; set => _cooldownCandles.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public GraalEmaMomentumStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 20).SetDisplay("Fast EMA", "Fast EMA period", "Indicators");
		_slowPeriod = Param(nameof(SlowPeriod), 80).SetDisplay("Slow EMA", "Slow EMA period", "Indicators");
		_momentumPeriod = Param(nameof(MomentumPeriod), 14).SetDisplay("Momentum", "Momentum period", "Indicators");
		_cooldownCandles = Param(nameof(CooldownCandles), 100).SetDisplay("Cooldown", "Candles between signals", "General");
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame()).SetDisplay("Candle Type", "Candle timeframe", "General");
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevFast = default;
		_prevSlow = default;
		_hasPrev = default;
		_cooldownRemaining = default;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevFast = 0;
		_prevSlow = 0;
		_hasPrev = false;
		_cooldownRemaining = 0;

		var fast = new ExponentialMovingAverage { Length = FastPeriod };
		var slow = new ExponentialMovingAverage { Length = SlowPeriod };
		var mom = new Momentum { Length = MomentumPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(fast, slow, mom, ProcessCandle).Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow, decimal mom)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!_hasPrev) { _prevFast = fast; _prevSlow = slow; _hasPrev = true; return; }

		if (_cooldownRemaining > 0)
		{
			_cooldownRemaining--;
			_prevFast = fast;
			_prevSlow = slow;
			return;
		}

		if (_prevFast <= _prevSlow && fast > slow && mom > 0 && Position <= 0)
		{
			if (Position < 0) BuyMarket();
			BuyMarket();
			_cooldownRemaining = CooldownCandles;
		}
		else if (_prevFast >= _prevSlow && fast < slow && mom < 0 && Position >= 0)
		{
			if (Position > 0) SellMarket();
			SellMarket();
			_cooldownRemaining = CooldownCandles;
		}
		_prevFast = fast;
		_prevSlow = slow;
	}
}