GitHub で見る

MACD Trend

Strategy based on MACD indicator

Testing indicates an average annual return of about 64%. It performs best in the forex market.

MACD Trend reacts to crossovers between the MACD line and its signal line. Bullish crosses initiate longs while bearish crosses start shorts. Opposite crosses or a stop close the trade.

The moving-average convergence divergence indicator adapts well to shifting markets by measuring momentum. This approach aims to ride trending swings while the indicator maintains a clear bullish or bearish bias.

Details

  • Entry Criteria: Signals based on MA, MACD.
  • Long/Short: Both directions.
  • Exit Criteria: Opposite signal or stop.
  • Stops: Yes.
  • Default Values:
    • FastEmaPeriod = 12
    • SlowEmaPeriod = 26
    • SignalPeriod = 9
    • StopLossPercent = 2m
    • CandleType = TimeSpan.FromMinutes(5)
  • Filters:
    • Category: Trend
    • Direction: Both
    • Indicators: MA, MACD
    • Stops: Yes
    • Complexity: Basic
    • Timeframe: Intraday (5m)
    • Seasonality: No
    • Neural Networks: No
    • Divergence: No
    • Risk Level: Medium
using System;
using System.Linq;
using System.Collections.Generic;

using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Strategy based on MACD indicator.
/// It enters long position when MACD crosses above signal line and short position when MACD crosses below signal line.
/// </summary>
public class MacdTrendStrategy : Strategy
{
	private readonly StrategyParam<int> _fastEmaPeriod;
	private readonly StrategyParam<int> _slowEmaPeriod;
	private readonly StrategyParam<int> _signalPeriod;
	private readonly StrategyParam<decimal> _stopLossPercent;
	private readonly StrategyParam<DataType> _candleType;

	// Current state
	private bool _prevIsMacdAboveSignal;

	/// <summary>
	/// Period for fast EMA in MACD.
	/// </summary>
	public int FastEmaPeriod
	{
		get => _fastEmaPeriod.Value;
		set => _fastEmaPeriod.Value = value;
	}

	/// <summary>
	/// Period for slow EMA in MACD.
	/// </summary>
	public int SlowEmaPeriod
	{
		get => _slowEmaPeriod.Value;
		set => _slowEmaPeriod.Value = value;
	}

	/// <summary>
	/// Period for signal line in MACD.
	/// </summary>
	public int SignalPeriod
	{
		get => _signalPeriod.Value;
		set => _signalPeriod.Value = value;
	}

	/// <summary>
	/// Stop-loss as percentage of entry price.
	/// </summary>
	public decimal StopLossPercent
	{
		get => _stopLossPercent.Value;
		set => _stopLossPercent.Value = value;
	}

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

	/// <summary>
	/// Initialize the MACD Trend strategy.
	/// </summary>
	public MacdTrendStrategy()
	{
		_fastEmaPeriod = Param(nameof(FastEmaPeriod), 200)
			.SetDisplay("Fast EMA Period", "Period for fast EMA in MACD", "Indicators")

			.SetOptimize(8, 16, 2);

		_slowEmaPeriod = Param(nameof(SlowEmaPeriod), 500)
			.SetDisplay("Slow EMA Period", "Period for slow EMA in MACD", "Indicators")

			.SetOptimize(20, 32, 2);

		_signalPeriod = Param(nameof(SignalPeriod), 200)
			.SetDisplay("Signal Period", "Period for signal line in MACD", "Indicators")

			.SetOptimize(5, 13, 2);

		_stopLossPercent = Param(nameof(StopLossPercent), 2m)
			.SetDisplay("Stop Loss (%)", "Stop loss as a percentage of entry price", "Risk parameters")
			
			.SetOptimize(1, 3, 0.5m);

		_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();
		_prevIsMacdAboveSignal = false;

	}

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

		// Create MACD indicator with signal line

		var macd = new MovingAverageConvergenceDivergenceSignal
		{
			Macd =
			{
				ShortMa = { Length = FastEmaPeriod },
				LongMa = { Length = SlowEmaPeriod },
			},
			SignalMa = { Length = SignalPeriod }
		};
		// Create subscription and bind indicator
		var subscription = SubscribeCandles(CandleType);
		subscription
			.BindEx(macd, ProcessCandle)
			.Start();

		// Setup chart visualization if available
		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, macd);
			DrawOwnTrades(area);
		}

	}

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

		// Check if strategy is ready to trade
		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		var macdTyped = (MovingAverageConvergenceDivergenceSignalValue)macdValue;
		if (macdTyped.Macd is not decimal macd || macdTyped.Signal is not decimal signal)
			return;

		// Check MACD position relative to signal line
		var isMacdAboveSignal = macd > signal;
		
		// Check for crossovers
		var isMacdCrossedAboveSignal = isMacdAboveSignal && !_prevIsMacdAboveSignal;
		var isMacdCrossedBelowSignal = !isMacdAboveSignal && _prevIsMacdAboveSignal;

		// Entry/exit logic based on MACD crossovers
		if (isMacdCrossedAboveSignal && Position <= 0)
		{
			// MACD crossed above signal line - Buy signal
			var volume = Volume + Math.Abs(Position);
			BuyMarket(volume);
			LogInfo($"Buy signal: MACD ({macd:F5}) crossed above Signal ({signal:F5})");
		}
		else if (isMacdCrossedBelowSignal && Position >= 0)
		{
			// MACD crossed below signal line - Sell signal
			var volume = Volume + Math.Abs(Position);
			SellMarket(volume);
			LogInfo($"Sell signal: MACD ({macd:F5}) crossed below Signal ({signal:F5})");
		}

		// Update previous state
		_prevIsMacdAboveSignal = isMacdAboveSignal;
	}
}