Ver no GitHub

AG Dual MACD Strategy

Overview

This strategy is a StockSharp port of the MetaTrader 4 expert AG.mq4. The robot operates with two Moving Average Convergence Divergence (MACD) calculations that use different parameter sets. The primary MACD produces entry triggers, while the secondary (scaled) MACD acts as a directional filter to avoid counter-trend trades and to control exits. The logic mirrors the original MQL4 expert by evaluating only closed candles and by reusing the signal line sign checks that gated the original orders.

Trading Logic

  • Indicators
    • Primary MACD: fast EMA = FastEmaLength, slow EMA = SlowEmaLength, signal SMA = SignalSmaLength.
    • Secondary MACD: fast EMA = SlowEmaLength * 2, slow EMA = FastEmaLength * 2, signal SMA = SignalSmaLength * 2.
  • Long entry
    • Primary MACD main line is above its signal line.
    • Primary MACD signal line is negative (below the waterline).
    • Secondary MACD main line is above its signal line.
    • Secondary MACD signal line is negative.
  • Short entry
    • Primary MACD main line is below its signal line.
    • Primary MACD signal line is positive.
    • Secondary MACD main line is below its signal line.
    • Secondary MACD signal line is positive.
  • Exit rules
    • Close long positions when the secondary MACD turns bearish while the primary signal line stays above zero.
    • Close short positions when the secondary MACD turns bullish while the primary signal line stays below zero.
  • The strategy only reacts to finished candles and ignores unfinished bars to avoid repainting.

Position Management

  • All orders are market orders with the fixed volume defined by OrderVolume.
  • MaxOpenOrders mirrors the original ORDER input and caps the total number of active orders plus open positions. Set it to 0 to remove the cap.
  • StartProtection() is enabled once the strategy starts so the StockSharp risk manager can monitor open exposure.

Parameters

Name Description
OrderVolume Base lot size for new trades.
FastEmaLength Fast EMA period of the primary MACD.
SlowEmaLength Slow EMA period of the primary MACD.
SignalSmaLength Signal smoothing period for both MACDs.
MaxOpenOrders Maximum number of combined active orders and open positions. Set 0 for unlimited.
CandleType Time frame used to build candles for both indicators.

Notes

  • The secondary MACD keeps the same fast/slow order as in the original EA, even if the fast period becomes larger than the slow one, to preserve the author's calculations.
  • The strategy does not place pending orders; it opens or closes at market as soon as the conditions appear.
  • No additional stop-loss or take-profit levels are added because the original expert relied exclusively on signal reversals.
using System;
using System.Collections.Generic;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// AG MACD Dual strategy - MACD histogram crossover with EMA trend filter.
/// Buys when MACD histogram crosses above zero while price is above EMA.
/// Sells when MACD histogram crosses below zero while price is below EMA.
/// </summary>
public class AgMacdDualStrategy : Strategy
{
	private readonly StrategyParam<int> _emaPeriod;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevMacd;
	private decimal _prevSignal;
	private bool _hasPrev;
	private decimal _currentEma;

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

	public AgMacdDualStrategy()
	{
		_emaPeriod = Param(nameof(EmaPeriod), 50)
			.SetDisplay("EMA Period", "EMA trend filter", "Indicators");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
	protected override void OnReseted() { base.OnReseted(); _prevMacd = 0m; _prevSignal = 0m; _hasPrev = false; _currentEma = 0m; }

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

		_hasPrev = false;

		var macd = new MovingAverageConvergenceDivergenceSignal();
		var ema = new ExponentialMovingAverage { Length = EmaPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.BindEx(macd, ProcessMacd)
			.Bind(ema, ProcessEma)
			.Start();
	}

	private void ProcessEma(ICandleMessage candle, decimal ema)
	{
		if (candle.State != CandleStates.Finished)
			return;

		_currentEma = ema;
	}

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

		if (!value.IsFinal || value.IsEmpty)
			return;

		var macdVal = value as MovingAverageConvergenceDivergenceSignalValue;
		if (macdVal == null)
			return;

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

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

		var histogram = macdLine.Value - signalLine.Value;

		if (!_hasPrev)
		{
			_prevMacd = macdLine.Value;
			_prevSignal = signalLine.Value;
			_hasPrev = true;
			return;
		}

		var prevHist = _prevMacd - _prevSignal;
		var close = candle.ClosePrice;

		if (prevHist <= 0 && histogram > 0 && close > _currentEma && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		else if (prevHist >= 0 && histogram < 0 && close < _currentEma && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}

		_prevMacd = macdLine.Value;
		_prevSignal = signalLine.Value;
	}
}