Ver no GitHub

MACD Signal Crossover Strategy

Overview

This sample converts the original MetaTrader 4 expert advisor MACD_v1.mq4 into a StockSharp high-level strategy. The algorithm tracks moving average convergence divergence (MACD) crossovers and trades in the direction of the new trend. Optional protective exits replicate the original advisor's behaviour: a stop-loss, a distant take-profit and a partial profit target that liquidates half of the current position.

Trading Logic

  1. Data source – the strategy subscribes to the configured candle series (5-minute candles by default) and binds a MovingAverageConvergenceDivergenceSignal indicator.
  2. Entry conditions:
    • Enter long when the MACD line crosses above the signal line. If a short position is active, it is closed before opening the long.
    • Enter short when the MACD line crosses below the signal line. If a long position exists, it is closed first.
  3. Exit conditions:
    • Opposite MACD crossover closes the current position and opens a new position in the opposite direction.
    • A configurable take-profit and stop-loss managed by StartProtection mirror the original TP/SL parameters (measured in instrument points).
    • A partial profit target closes half of the open position once price advances by a specified amount from the entry level. The partial exit is triggered only once per position.

Parameters

Name Default Description
Fast Period 23 Fast EMA length for the MACD calculation (mirrors the MQL parameter a = 2300).
Slow Period 40 Slow EMA length for the MACD calculation (b = 4000 in the source).
Signal Period 8 Signal line length (c = 800 in the source).
Take Profit 500 Distance in price points for the protective take-profit order. Set to 0 to disable.
Stop Loss 80 Distance in price points for the protective stop-loss order. Set to 0 to disable.
Partial Profit 70 Distance in price points to close half of the open position. Set to 0 to disable partial exits.
Candle Type 5-minute time frame Candle series used for indicator calculations.

Notes

  • Indicator parameters were scaled to typical MACD lengths (23/40/8) because the MQL script expressed them as hundredths (2300/4000/800).
  • The strategy automatically restores the partial exit flag whenever a new position is accumulated.
  • Chart drawing helpers highlight candles, MACD values and the strategy's trades when a chart panel is available.
  • Volume handling relies on the base strategy Volume property. Adjust it before starting the strategy to match your instrument size.
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 MacdSignalCrossoverStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _signalPeriod;
	private readonly StrategyParam<DataType> _candleType;

	private bool _prevMacdAboveSignal;
	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 SignalPeriod
	{
		get => _signalPeriod.Value;
		set => _signalPeriod.Value = value;
	}

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public MacdSignalCrossoverStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 23);
		_slowPeriod = Param(nameof(SlowPeriod), 40);
		_signalPeriod = Param(nameof(SignalPeriod), 8);
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame());
	}

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_prevMacdAboveSignal = false;
		_hasPrev = false;
	}

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

		var macd = new MovingAverageConvergenceDivergenceSignal
		{
			Macd =
			{
				ShortMa = { Length = FastPeriod },
				LongMa = { Length = SlowPeriod },
			},
			SignalMa = { Length = SignalPeriod }
		};

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

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

		if (!macdValue.IsFinal)
			return;

		var macdTyped = macdValue as MovingAverageConvergenceDivergenceSignalValue;
		if (macdTyped == null)
			return;

		var macdLine = macdTyped.Macd;
		var signalLine = macdTyped.Signal;

		if (macdLine == null || signalLine == null)
			return;

		var isMacdAboveSignal = macdLine.Value > signalLine.Value;

		if (!_hasPrev)
		{
			_hasPrev = true;
			_prevMacdAboveSignal = isMacdAboveSignal;
			return;
		}

		var crossedAbove = isMacdAboveSignal && !_prevMacdAboveSignal;
		var crossedBelow = !isMacdAboveSignal && _prevMacdAboveSignal;

		if (crossedAbove)
		{
			if (Position < 0)
				BuyMarket();
			if (Position <= 0)
				BuyMarket();
		}
		else if (crossedBelow)
		{
			if (Position > 0)
				SellMarket();
			if (Position >= 0)
				SellMarket();
		}

		_prevMacdAboveSignal = isMacdAboveSignal;
	}
}