Auf GitHub ansehen

Ichimoku Momentum MACD Strategy

Summary

  • Type: Trend following with momentum confirmation.
  • Timeframe: Configurable (default 15-minute candles).
  • Indicators: Ichimoku (Tenkan/Kijun), Linear Weighted Moving Averages, Momentum, MACD.
  • Stops: Optional fixed take-profit and stop-loss in price points via StartProtection.

Strategy Description

This strategy recreates the decision flow of the MetaTrader expert "Ichimoku" (folder MQL/23469). It evaluates the previous closed candle and opens new trades at the start of the next bar when all four confirmations agree:

  1. Ichimoku alignment – Tenkan (conversion line) must be above Kijun (base line) for long trades and below it for shorts.
  2. LWMA trend filter – A fast linear weighted moving average must stay above the slow LWMA for longs and below it for shorts. Both averages are calculated on the same timeframe as the subscribed candles.
  3. Momentum strength – The absolute distance of the momentum oscillator from the neutral level 100 has to be greater than a configurable threshold on at least one of the last three closed candles.
  4. MACD confirmation – The MACD histogram must agree with the direction (MACD line positioned beyond the signal line with the same sign).

When all four conditions line up bullishly and the strategy is not currently long, it buys the configured volume plus any units required to flatten an existing short position. When the conditions flip to bearish it mirrors the process on the sell side. Opposite signals always close open positions, providing a deterministic exit even without protective orders.

Risk management is handled through StockSharp's StartProtection, allowing fixed take-profit and stop-loss distances expressed in instrument points. Setting either parameter to zero disables the corresponding protection leg.

Parameter Overview

Parameter Description
FastMaPeriod Length of the fast linear weighted moving average used for the trend filter.
SlowMaPeriod Length of the slow linear weighted moving average.
MomentumPeriod Lookback period of the momentum oscillator.
MomentumThreshold Minimum distance from 100 that the momentum must achieve on at least one of the last three candles.
MacdFastPeriod Fast EMA length of the MACD filter.
MacdSlowPeriod Slow EMA length of the MACD filter.
MacdSignalPeriod Signal EMA length of the MACD filter.
TenkanPeriod Ichimoku Tenkan-sen length.
KijunPeriod Ichimoku Kijun-sen length.
SenkouSpanBPeriod Ichimoku Senkou Span B length.
TakeProfitPoints Optional take-profit distance in price points (0 disables).
StopLossPoints Optional stop-loss distance in price points (0 disables).
CandleType Timeframe used for all indicator calculations.

Usage Notes

  • The strategy reads only finished candles and stores the indicator values of the previous bar, matching the MetaTrader EA's shift=1 logic.
  • Adjust MomentumThreshold when switching to markets with different momentum scaling (e.g., crypto vs. forex pairs).
  • Protective orders are managed internally; exchange-level bracket orders are not submitted.
  • Charts, if available, will display price candles, both LWMAs, the Ichimoku cloud, and executed trades.
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;
	}
}