Открыть на GitHub

Стратегия Ichimoku Momentum MACD

Кратко

  • Тип: трендовая стратегия с подтверждением по импульсу.
  • Таймфрейм: настраиваемый (по умолчанию 15-минутные свечи).
  • Индикаторы: Ichimoku (Tenkan/Kijun), линейные взвешенные средние, Momentum, MACD.
  • Стопы: опциональный тейк-профит и стоп-лосс в пунктах через StartProtection.

Описание стратегии

Стратегия воспроизводит логику эксперта MetaTrader «Ichimoku» (каталог MQL/23469). Все расчёты выполняются на закрытии предыдущей свечи, а сделки открываются в начале следующей при одновременном выполнении четырёх условий:

  1. Выравнивание Ichimoku — для покупок Tenkan должен быть выше Kijun, для продаж — ниже.
  2. Трендовый фильтр LWMA — быстрая линейная взвешенная средняя должна располагаться выше (для лонга) или ниже (для шорта) медленной.
  3. Сила импульса — абсолютное отклонение индикатора Momentum от уровня 100 должно превышать заданный порог хотя бы на одной из трёх последних свечей.
  4. Подтверждение MACD — линия MACD должна находиться по ту же сторону от нуля, что и сигнал, и располагаться дальше от него в сторону сделки.

Если все фильтры дают бычий сигнал и открытых лонгов нет, стратегия покупает заданный объём (закрывая при необходимости шорты). При медвежьем наборе условий выполняется зеркальная продажа. Противоположный сигнал закрывает текущую позицию, поэтому даже без защитных ордеров присутствует чёткое правило выхода.

Риск-менеджмент реализован через StartProtection: можно задать фиксированные расстояния для тейк-профита и стоп-лосса в пунктах, значение 0 отключает соответствующий уровень.

Параметры

Параметр Описание
FastMaPeriod Длина быстрой линейной взвешенной средней.
SlowMaPeriod Длина медленной линейной взвешенной средней.
MomentumPeriod Период расчёта индикатора Momentum.
MomentumThreshold Минимальное отклонение Momentum от 100 на одной из трёх последних свечей.
MacdFastPeriod Период быстрой EMA в MACD.
MacdSlowPeriod Период медленной EMA в MACD.
MacdSignalPeriod Период сигнальной EMA в MACD.
TenkanPeriod Длина Tenkan-sen индикатора Ichimoku.
KijunPeriod Длина Kijun-sen.
SenkouSpanBPeriod Длина Senkou Span B.
TakeProfitPoints Тейк-профит в пунктах (0 — отключен).
StopLossPoints Стоп-лосс в пунктах (0 — отключен).
CandleType Таймфрейм, используемый во всех расчётах.

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

  • Обработка ведётся только по закрытым свечам, что соответствует логике shift=1 исходного советника.
  • Масштаб индикатора Momentum зависит от инструмента, поэтому при смене рынка корректируйте MomentumThreshold.
  • Защитные ордера не выставляются на биржу, управление ведётся внутри стратегии.
  • При доступности графика отображаются свечи цены, обе LWMA, облако Ichimoku и сделки.
using System;
using System.Collections.Generic;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

public class IchimokuMomentumMacdStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _takeProfitPoints;

	private ExponentialMovingAverage _fast;
	private ExponentialMovingAverage _slow;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;
	private int _cooldown;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
	public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }

	public IchimokuMomentumMacdStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 12).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
		_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
		_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
		_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
	}

	protected override void OnReseted()
	{
		base.OnReseted();
		_fast = null; _slow = null;
		_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_fast = new ExponentialMovingAverage { Length = FastPeriod };
		_slow = new ExponentialMovingAverage { Length = SlowPeriod };
		var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
		subscription.Bind(_fast, _slow, ProcessCandle);
		subscription.Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
		if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }

		var close = candle.ClosePrice;
		var step = Security?.PriceStep ?? 1m;

		if (Position > 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}
		else if (Position < 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}

		if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
		{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
		else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
		{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }

		_prevFast = fastValue; _prevSlow = slowValue;
	}
}