View on GitHub

Macd Momentum Reversal

Strategy uses MACD histogram to detect momentum reversals. Shorts when bullish candle grows but MACD histogram declines. Buys when bearish candle grows but MACD histogram rises.

Details

  • Entry Criteria: Larger candle body with fading MACD momentum.
  • Long/Short: Both directions.
  • Exit Criteria: Opposite signal.
  • Stops: No.
  • Default Values:
    • FastLength = 12
    • SlowLength = 26
    • SignalLength = 9
    • CandleType = TimeSpan.FromMinutes(1)
  • Filters:
    • Category: Momentum
    • Direction: Both
    • Indicators: MACD
    • Stops: No
    • Complexity: Basic
    • Timeframe: Intraday (1m)
    • Seasonality: No
    • Neural Networks: No
    • Divergence: No
    • Risk Level: Medium
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;

/// <summary>
/// MACD momentum reversal strategy.
/// A bullish candle with a larger body than the previous candle while the MACD histogram declines goes short; a bearish candle with a
/// larger body than the previous candle while the histogram rises goes long. An opposite signal reverses the position.
/// </summary>
public class MacdMomentumReversalStrategy : Strategy
{
	private readonly StrategyParam<int> _fastLength;
	private readonly StrategyParam<int> _slowLength;
	private readonly StrategyParam<int> _signalLength;
	private readonly StrategyParam<DataType> _candleType;

	private decimal? _prevBody;
	private decimal? _prevHist;

	/// <summary>
	/// Fast EMA length of MACD.
	/// </summary>
	public int FastLength
	{
		get => _fastLength.Value;
		set => _fastLength.Value = value;
	}

	/// <summary>
	/// Slow EMA length of MACD.
	/// </summary>
	public int SlowLength
	{
		get => _slowLength.Value;
		set => _slowLength.Value = value;
	}

	/// <summary>
	/// Signal line length of MACD.
	/// </summary>
	public int SignalLength
	{
		get => _signalLength.Value;
		set => _signalLength.Value = value;
	}

	/// <summary>
	/// Candle type.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Constructor.
	/// </summary>
	public MacdMomentumReversalStrategy()
	{
		_fastLength = Param(nameof(FastLength), 12)
			.SetGreaterThanZero()
			.SetDisplay("Fast Length", "Fast EMA length of MACD", "MACD");

		_slowLength = Param(nameof(SlowLength), 26)
			.SetGreaterThanZero()
			.SetDisplay("Slow Length", "Slow EMA length of MACD", "MACD");

		_signalLength = Param(nameof(SignalLength), 9)
			.SetGreaterThanZero()
			.SetDisplay("Signal Length", "Signal line length of MACD", "MACD");

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(1).TimeFrame())
			.SetDisplay("Candle Type", "Type of candles to use", "General");
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevBody = null;
		_prevHist = null;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_prevBody = null;
		_prevHist = null;

		var macd = new MovingAverageConvergenceDivergenceSignal
		{
			Macd =
			{
				ShortMa = { Length = FastLength },
				LongMa = { Length = SlowLength },
			},
			SignalMa = { Length = SignalLength }
		};

		var subscription = SubscribeCandles(CandleType);
		subscription
			.BindEx(macd, ProcessCandle)
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawOwnTrades(area);

			var oscillators = CreateChartArea();
			if (oscillators != null)
				DrawIndicator(oscillators, macd);
		}
	}

	private void ProcessCandle(ICandleMessage candle, IIndicatorValue macdValue)
	{
		if (candle.State != CandleStates.Finished)
			return;

		var body = Math.Abs(candle.ClosePrice - candle.OpenPrice);
		var prevBody = _prevBody;
		_prevBody = body;

		if (!macdValue.IsFormed || macdValue is not MovingAverageConvergenceDivergenceSignalValue { Macd: decimal macd, Signal: decimal signal })
			return;

		var hist = macd - signal;
		var prevHist = _prevHist;
		_prevHist = hist;

		if (prevBody is not decimal pb || prevHist is not decimal ph)
			return;

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		var grows = body > pb;

		if (grows && candle.ClosePrice > candle.OpenPrice && hist < ph && Position >= 0)
			SellMarket(Volume + Math.Abs(Position));
		else if (grows && candle.ClosePrice < candle.OpenPrice && hist > ph && Position <= 0)
			BuyMarket(Volume + Math.Abs(Position));
	}
}