Auf GitHub ansehen

ADX MACD Strategie

ADX MACD verbindet die Trendstärke des Average Directional Index mit Momentum-Wechseln des MACD. Wenn der ADX steigt, haben Ausbrüche eine höhere Chance, sich fortzusetzen, insbesondere wenn der MACD in dieselbe Richtung kreuzt.

Tests zeigen eine durchschnittliche jährliche Rendite von etwa 139%. Die Strategie funktioniert am besten auf dem Aktienmarkt.

Die Strategie handelt diese ausgerichteten Signale und steigt aus, sobald der ADX zu schwächen beginnt oder der MACD gegen die Position dreht.

Ein moderater prozentualer Stop begrenzt Verluste in seitwärts laufenden Märkten.

Details

  • Einstiegskriterien: Indikatorsignal
  • Long/Short: Beide
  • Ausstiegskriterien: Stop-Loss oder entgegengesetztes Signal
  • Stops: Ja, prozentbasiert
  • Standardwerte:
    • CandleType = 15 minute
    • StopLossPercent = 2
    • AdxPeriod = 14
    • MacdFast = 12
    • MacdSlow = 26
    • MacdSignal = 9 Ohne Position eröffnet ein MACD-Kreuzen über die Signallinie einen Long und darunter einen Short, aber nur solange ADX über seinem vorherigen Wert liegt. Die Position schließt, sobald ADX unter seinen vorherigen Wert fällt oder MACD auf die andere Seite der Signallinie zurückkehrt.
  • Filter:
    • Kategorie: Trendfolge
    • Richtung: Beide
    • Indikatoren: ADX, MACD
    • Stops: Ja
    • Komplexität: Mittel
    • Zeitrahmen: Intraday
    • Saisonalität: Nein
    • Neuronale Netze: Nein
    • Divergenz: Nein
    • Risikolevel: Mittel
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;

/// <summary>
/// ADX MACD strategy.
/// While flat, a MACD cross above its signal line goes long and a cross below goes short, but only while ADX is rising.
/// The position closes once ADX weakens below its previous value or MACD returns to the other side of its signal line,
/// and a percent stop limits the loss.
/// </summary>
public class AdxMacdStrategy : Strategy
{
	private readonly StrategyParam<int> _adxPeriod;
	private readonly StrategyParam<int> _macdFast;
	private readonly StrategyParam<int> _macdSlow;
	private readonly StrategyParam<int> _macdSignal;
	private readonly StrategyParam<decimal> _stopLossPercent;
	private readonly StrategyParam<DataType> _candleType;

	private bool? _prevAbove;
	private decimal? _prevAdx;

	/// <summary>
	/// Period of ADX.
	/// </summary>
	public int AdxPeriod
	{
		get => _adxPeriod.Value;
		set => _adxPeriod.Value = value;
	}

	/// <summary>
	/// Fast EMA period of MACD.
	/// </summary>
	public int MacdFast
	{
		get => _macdFast.Value;
		set => _macdFast.Value = value;
	}

	/// <summary>
	/// Slow EMA period of MACD.
	/// </summary>
	public int MacdSlow
	{
		get => _macdSlow.Value;
		set => _macdSlow.Value = value;
	}

	/// <summary>
	/// Signal line period of MACD.
	/// </summary>
	public int MacdSignal
	{
		get => _macdSignal.Value;
		set => _macdSignal.Value = value;
	}

	/// <summary>
	/// Stop loss percentage from 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>
	/// Constructor.
	/// </summary>
	public AdxMacdStrategy()
	{
		_adxPeriod = Param(nameof(AdxPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("ADX Period", "Period of ADX", "ADX");

		_macdFast = Param(nameof(MacdFast), 12)
			.SetGreaterThanZero()
			.SetDisplay("MACD Fast", "Fast EMA period of MACD", "MACD");

		_macdSlow = Param(nameof(MacdSlow), 26)
			.SetGreaterThanZero()
			.SetDisplay("MACD Slow", "Slow EMA period of MACD", "MACD");

		_macdSignal = Param(nameof(MacdSignal), 9)
			.SetGreaterThanZero()
			.SetDisplay("MACD Signal", "Signal line period of MACD", "MACD");

		_stopLossPercent = Param(nameof(StopLossPercent), 2m)
			.SetNotNegative()
			.SetDisplay("Stop Loss %", "Stop loss percentage from entry price", "Risk");

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).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();
		_prevAbove = null;
		_prevAdx = null;
	}

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

		_prevAbove = null;
		_prevAdx = null;

		var adx = new AverageDirectionalIndex { Length = AdxPeriod };
		var macd = new MovingAverageConvergenceDivergenceSignal
		{
			Macd =
			{
				ShortMa = { Length = MacdFast },
				LongMa = { Length = MacdSlow },
			},
			SignalMa = { Length = MacdSignal }
		};

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

		StartProtection(new Unit(), new Unit(StopLossPercent, UnitTypes.Percent), useMarketOrders: true, isLocalStop: true);

		// The stop has to see prices between candles, not only at their close.
		foreach (var field in new[] { Level1Fields.BestBidPrice, Level1Fields.BestAskPrice })
		{
			var quotes = new Subscription(DataType.Level1, Security);
			quotes.MarketData.BuildField = field;
			SubscribeLevel1(quotes).Bind(ObserveProtectionQuote).Start();
		}

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawOwnTrades(area);

			var oscillators = CreateChartArea();
			if (oscillators != null)
			{
				DrawIndicator(oscillators, adx);
				DrawIndicator(oscillators, macd);
			}
		}
	}

	private void ObserveProtectionQuote(Level1ChangeMessage quote)
	{
		// The high-level handler activates native protection before this callback, also between signal bars.
	}

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

		if (!adxValue.IsFormed || !macdValue.IsFormed)
			return;

		if (adxValue is not AverageDirectionalIndexValue { MovingAverage: decimal strength })
			return;

		var macdTyped = (MovingAverageConvergenceDivergenceSignalValue)macdValue;

		if (macdTyped.Macd is not decimal macd || macdTyped.Signal is not decimal signal)
			return;

		var isAbove = macd > signal;
		var wasAbove = _prevAbove;
		var prevAdx = _prevAdx;
		_prevAbove = isAbove;
		_prevAdx = strength;

		if (wasAbove is not bool was || prevAdx is not decimal lastStrength)
			return;

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		var rising = strength > lastStrength;

		if (Position > 0)
		{
			if (!rising || !isAbove)
				SellMarket(Position);
		}
		else if (Position < 0)
		{
			if (!rising || isAbove)
				BuyMarket(-Position);
		}
		else if (rising && was != isAbove)
		{
			if (isAbove)
				BuyMarket(Volume);
			else
				SellMarket(Volume);
		}
	}
}