Ver no GitHub

Simple MACD EA Strategy

Overview

The Simple MACD EA strategy is a direct port of the classic MetaTrader expert advisor "Simple MACD EA". The approach uses two exponential moving averages (EMAs) to emulate the MACD histogram and determine the dominant trend on one-minute candles. Long positions are opened when the fast EMA (period 100) crosses above the slow EMA (user-defined MACD level). Short positions are opened when the fast EMA drops below the slow EMA. Only one position is maintained at any time.

Trade Management Logic

  • Trend detection: The difference between the 100-period EMA and the configurable MACD EMA defines the current trend direction (+1, 0, -1). A reversal from negative to positive opens a long position. A reversal from positive to negative opens a short position.
  • Momentum confirmation: The strategy keeps track of the difference between the MACD EMA and a slightly slower EMA (MACD level + 1). If the difference shrinks against the current trade after the price has moved at least five points in profit, the position is closed early.
  • Time-based protection: After a trade stays open for a user-defined number of evaluation cycles, the system activates a soft stop that reduces tolerance for adverse price movement relative to the entry price.
  • Trailing exit: Once the trade moves into profit and stays active for enough cycles, an internal trailing stop is engaged. The stop level follows the price by the configured number of points and can be updated a limited number of times. If the limit is reached the position is closed.
  • Trend reversal exit: When the trend signal flips in the opposite direction while price is already five points in profit, the position is closed immediately.

Parameters

  • Candle Type – Time frame used for the EMA calculations (default: 1-minute candles).
  • Volume – Order volume for new entries.
  • MACD Level – EMA length that defines the slow MACD component. A secondary EMA with length MACD Level + 1 is derived automatically.
  • Trailing Stop – Distance in points for the trailing exit. Set to zero to disable.
  • Trailing Updates – Maximum number of trailing stop adjustments per trade.
  • Wait Cycles – Number of candle evaluations to wait before the adaptive soft stop becomes active.

Additional Notes

  • The strategy always flattens the current position before reversing direction.
  • Price step information from the selected security is used to translate point-based distances into actual prices.
  • The implementation relies on StockSharp's high-level candle subscription API and does not enqueue custom indicator buffers.
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>
/// Simple MACD EA strategy using fast/slow EMA difference for trend detection.
/// Buy when EMA difference crosses above zero, sell when it crosses below zero.
/// </summary>
public class SimpleMacdEaStrategy : Strategy
{
	private readonly StrategyParam<int> _fastEmaPeriod;
	private readonly StrategyParam<int> _slowEmaPeriod;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevDiff;
	private bool _hasPrev;

	public int FastEmaPeriod
	{
		get => _fastEmaPeriod.Value;
		set => _fastEmaPeriod.Value = value;
	}

	public int SlowEmaPeriod
	{
		get => _slowEmaPeriod.Value;
		set => _slowEmaPeriod.Value = value;
	}

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

	public SimpleMacdEaStrategy()
	{
		_fastEmaPeriod = Param(nameof(FastEmaPeriod), 12)
			.SetDisplay("Fast EMA", "Fast EMA period", "Indicators");

		_slowEmaPeriod = Param(nameof(SlowEmaPeriod), 26)
			.SetDisplay("Slow EMA", "Slow EMA period", "Indicators");

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

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

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

		_prevDiff = 0m;
		_hasPrev = false;
	}

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

		_hasPrev = false;

		var fastEma = new ExponentialMovingAverage { Length = FastEmaPeriod };
		var slowEma = new ExponentialMovingAverage { Length = SlowEmaPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(fastEma, slowEma, ProcessCandle)
			.Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow)
	{
		if (candle.State != CandleStates.Finished)
			return;

		var diff = fast - slow;

		if (!_hasPrev)
		{
			_prevDiff = diff;
			_hasPrev = true;
			return;
		}

		// Buy: MACD crosses above zero
		var longSignal = _prevDiff <= 0 && diff > 0;
		// Sell: MACD crosses below zero
		var shortSignal = _prevDiff >= 0 && diff < 0;

		if (Position <= 0 && longSignal)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		else if (Position >= 0 && shortSignal)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}

		_prevDiff = diff;
	}
}