View on GitHub

Tipu MACD EA Strategy

Overview

The strategy is a high-level StockSharp port of the Tipu MACD EA from MQL4. It trades a single symbol using MACD-based signals and mirrors the original expert advisor features:

  • Optional trading-hour filter with two configurable time windows.
  • MACD zero-line and signal-line crossover entries with adjustable EMA lengths and shift.
  • Automatic position management including take-profit, stop-loss, trailing stop, and breakeven.
  • Volume capping that emulates the "maximum lots" setting from the source code.

All operations use market orders. Protective levels are tracked internally and orders are closed once a candle pierces the stop-loss or take-profit levels.

Trading logic

  1. Subscribe to the configured candle type and calculate a MovingAverageConvergenceDivergenceSignal indicator (MACD line + signal line).
  2. Evaluate MACD values using the selected shift (MacdShift 0 = current candle, 1 = previous candle) and build crossover signals:
    • Zero-line crossover (optional) – buy when MACD crosses above zero, sell when it crosses below.
    • Signal-line crossover (optional) – buy when MACD crosses above the signal line, sell when it crosses below.
  3. Before opening a position, ensure the current hour belongs to at least one of the two time windows when the filter is enabled.
  4. When a long signal appears:
    • If hedging is disabled and a short is open, optionally close it (CloseOnReverseSignal) or skip the new trade.
    • Place a buy market order for the lesser of TradeVolume and the remaining volume until MaxPositionVolume is reached.
    • Update the long entry snapshot and compute protective stop/take levels if enabled.
  5. When a short signal appears follow the symmetric logic for sell orders.
  6. While a position is active:
    • Monitor stops and targets on each finished candle and close the trade if either level is breached.
    • When trailing is enabled and price advances by TrailingPips + TrailingCushionPips, move the stop to maintain TrailingPips distance from price.
    • When the breakeven module is active and profit exceeds RiskFreePips, move the stop to the entry price.

Parameters

Name Description
CandleType Candle series used for MACD calculations.
TradeVolume Volume of each market entry (lots).
MaxPositionVolume Maximum cumulative long or short exposure allowed.
UseTimeFilter Enables the dual-window trading hour filter.
Zone1StartHour, Zone1EndHour Start/end hours for the first trading window (inclusive, exchange time).
Zone2StartHour, Zone2EndHour Start/end hours for the second trading window.
FastPeriod, SlowPeriod, SignalPeriod MACD fast EMA, slow EMA, and signal SMA lengths.
MacdShift 0 = evaluate the current bar, 1 = evaluate the previous bar (matching the MQL iShift).
UseZeroCross Enables MACD zero-line cross entries.
UseSignalCross Enables MACD vs. signal-line cross entries.
AllowHedging Allows building both long and short exposure without closing the opposite side first.
CloseOnReverseSignal Closes the opposite position when a new signal appears (used when hedging is disabled).
UseTakeProfit, TakeProfitPips Enables and configures the take-profit distance (pips).
UseStopLoss, StopLossPips Enables and configures the stop-loss distance (pips).
UseTrailingStop, TrailingPips, TrailingCushionPips Enables trailing management, sets trailing distance and cushion (pips).
UseRiskFree, RiskFreePips Moves the stop to breakeven once profit exceeds the specified pips.

Usage notes

  • Configure the candle type to match the timeframe used in MetaTrader (default 15-minute bars).
  • The pip size is derived from Security.PriceStep. If the instrument lacks this metadata, a default of 0.0001 is used.
  • The strategy assumes immediate execution of market orders. When running live, ensure proper slippage handling if necessary.
  • When both zero-line and signal-line entries are disabled the strategy remains idle.
namespace StockSharp.Samples.Strategies;

using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;

/// <summary>
/// Tipu MACD EA strategy: MACD signal line crossover.
/// Buys when MACD crosses above signal, sells when crosses below.
/// </summary>
public class TipuMacdEaStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _signalPeriod;
	private readonly StrategyParam<int> _signalCooldownCandles;

	private decimal _prevMacd;
	private decimal _prevSignal;
	private int _candlesSinceTrade;
	private bool _hasPrev;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	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 int SignalCooldownCandles { get => _signalCooldownCandles.Value; set => _signalCooldownCandles.Value = value; }

	public TipuMacdEaStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
		_fastPeriod = Param(nameof(FastPeriod), 12)
			.SetGreaterThanZero()
			.SetDisplay("MACD Fast", "MACD fast EMA period", "Indicators");
		_slowPeriod = Param(nameof(SlowPeriod), 26)
			.SetGreaterThanZero()
			.SetDisplay("MACD Slow", "MACD slow EMA period", "Indicators");
		_signalPeriod = Param(nameof(SignalPeriod), 9)
			.SetGreaterThanZero()
			.SetDisplay("Signal Period", "MACD signal period", "Indicators");
		_signalCooldownCandles = Param(nameof(SignalCooldownCandles), 4)
			.SetGreaterThanZero()
			.SetDisplay("Signal Cooldown", "Bars to wait between trades", "Trading");
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevMacd = 0;
		_prevSignal = 0;
		_candlesSinceTrade = SignalCooldownCandles;
		_hasPrev = false;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevMacd = 0;
		_prevSignal = 0;
		_candlesSinceTrade = SignalCooldownCandles;
		_hasPrev = false;
		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;

		if (_candlesSinceTrade < SignalCooldownCandles)
			_candlesSinceTrade++;

		if (macdValue is not MovingAverageConvergenceDivergenceSignalValue typed) return;
		if (typed.Macd is not decimal macdMain || typed.Signal is not decimal signal) return;

		if (_hasPrev)
		{
			if (_prevMacd <= _prevSignal && macdMain > signal && Position <= 0 && _candlesSinceTrade >= SignalCooldownCandles)
			{
				BuyMarket();
				_candlesSinceTrade = 0;
			}
			else if (_prevMacd >= _prevSignal && macdMain < signal && Position >= 0 && _candlesSinceTrade >= SignalCooldownCandles)
			{
				SellMarket();
				_candlesSinceTrade = 0;
			}
		}

		_prevMacd = macdMain;
		_prevSignal = signal;
		_hasPrev = true;
	}
}