Auf GitHub ansehen

MACD Vier Farben 2 Martingale Strategie

MACD Vier Farben 2 Martingale

Die Strategie portiert den Fachberater „MACD Four Colors 2 Martingale“ von MetaTrader auf StockSharp. Es behält die ursprüngliche Logik bei, die auf der „Farb“-Interpretation von MACD und einem Martingal-Positionsgrößenmodell basiert.

Überblick

Der zugrunde liegende Indikator malt das MACD-Histogramm mit fünf Farben. Im standardmäßigen „neuen“ Farbschema ändert das Histogramm seine Farbe abhängig davon, ob die MACD-Linie über/unter die Nulllinie steigt oder fällt. Der Expert Advisor eröffnet eine Position immer dann, wenn die Farben von Silber zu Gelb (negative MACD-Abwärtsbewegung) oder von Rot zu Blau (positive MACD-Umstellung) wechseln. Die StockSharp-Version reproduziert diese Sequenz, indem sie die Farben aus MACD-Werten rekonstruiert.

Es ist immer nur ein direktionaler Handelskorb aktiv. Ein neuer Trade ist nur zulässig, wenn sein Preis den durchschnittlichen Einstieg des aktuellen Korbs verbessert (niedrigerer Preis für Long-Positionen, höherer Preis für Short-Positionen). Jeder neue Eintrag multipliziert das zuletzt gefüllte Volumen mit einem konfigurierbaren Loskoeffizienten und implementiert so die Martingal-Mittelung aus dem ursprünglichen EA.

Handelsregeln

  • Indikatorlogik: Ein MovingAverageConvergenceDivergenceSignal-Indikator mit der klassischen 12/26/9-Konfiguration generiert MACD-Werte.
  • Farbrekonstruktion: Die Strategie vergleicht die letzten beiden MACD-Werte. Ansteigendes Negativ MACD wird der Farbe 1 (Silber) zugeordnet, ansteigend positiv der Farbe 2 (Rot), fallend positiv der Farbe 3 (Blau) und fallend negativ der Farbe 4 (Gelb).
  • Langer Einstieg: Wird ausgelöst, wenn sich die rekonstruierten Farben von 1 auf 4 bewegen, während der MACD im vorherigen Balken unter Null bleibt. Der Handel wird nur ausgeführt, wenn kein Short-Engagement besteht und der neue Preis niedriger ist als ein bestehender Long-Einstieg.
  • Kurzer Eintrag: Wird ausgelöst, wenn sich die Farben von 2 auf 3 bewegen, während der MACD im vorherigen Balken über Null bleibt. Der Handel wird nur ausgelöst, wenn kein Long-Engagement besteht und der neue Preis höher ist als jeder bestehende Short-Einstieg.
  • Volumenverwaltung: Die erste Bestellung verwendet InitialVolume. Jede weitere Bestellung im selben Warenkorb multipliziert das zuletzt ausgeführte Volumen mit LotCoefficient. Durch Einstellen des Koeffizienten ≤ 0 wird der Multiplikator deaktiviert.
  • Gewinn- und Verlustkontrolle: Der variable PnL wird für jede fertige Kerze ausgewertet. Durch Drücken von TargetProfit werden alle Positionen geschlossen und der Martingalzyklus zurückgesetzt. Das Überschreiten von MaxDrawdown (interpretiert als Verlustschwelle) schließt ebenfalls alles und startet den Zyklus neu. Negative Schwellenwerte werden wie im Originalcode unterstützt.
  • Positionsausstieg: Außer den Geldzielen gibt es keine automatischen Stops. Positionen bleiben offen, bis eine Risikoschwelle erreicht wird oder der Benutzer manuell eingreift.

Parameter

  • CandleType (DataType, Standard 1h) – Zeitrahmen für die MACD-Berechnung.
  • InitialVolume (dezimal, Standard 1) – Volumen der ersten Bestellung in einem Warenkorb.
  • LotCoefficient (dezimal, Standard 2) – Multiplikator, der auf das vorherige Volumen angewendet wird, wenn Martingal aktiv ist.
  • MaxDrawdown (dezimal, Standard 50) – schwankender Verlustschwellenwert (in Geld), der die Liquidation erzwingt. Positive Werte beobachten -MaxDrawdown, negative Werte verwenden den genauen Wert.
  • TargetProfit (dezimal, Standard 150) – variables Gewinnziel (in Geld), das den Warenkorb schließt. Negative Werte invertieren den Vergleich wie in der MQL-Version.
  • FastEmaPeriod (int, default 12) – Länge des schnellen EMA für MACD.
  • SlowEmaPeriod (int, Standard 26) – Länge des langsamen EMA für MACD.
  • SignalPeriod (int, Standard 9) – Länge des Signals EMA für die Glättung durch MACD.

Nutzungshinweise

  • Funktioniert auf jedem Instrument, das PriceStep und StepPrice definiert, da der nicht realisierte PnL anhand der Börsenspezifikationen berechnet wird.
  • Durch die Martingalgröße kann die Position schnell wachsen. Überprüfen Sie die Risikogrenzen, bevor Sie den Handel auf einem Live-Konto aktivieren.
  • Zur visuellen Analyse fügen Sie den durch die Strategie erstellten Diagrammbereich hinzu. Es zeichnet Kerzen, den MACD-Indikator und ausgeführte Trades auf.

Katalogfilter

  • Kategorie: Trend-/Momentum-Mittelung
  • Richtung: Beide (lange und kurze Körbe)
  • Indikatoren: MACD
  • Stopps: Nur geldbasierter Ausstieg
  • Zeitrahmen: Konfigurierbar (Standard 1 Stunde)
  • Komplexität: Mittelschwer
  • Risiko: Hoch aufgrund der Martingal-Skalierung
  • Automatisierung: Vollautomatisch nach dem Start
using System;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// MACD histogram crossover with martingale volume scaling.
/// Based on the MACD Four Colors 2 Martingale expert advisor.
/// </summary>
public class MacdFourColors2MartingaleStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastEmaPeriod;
	private readonly StrategyParam<int> _slowEmaPeriod;
	private readonly StrategyParam<int> _signalPeriod;
	private readonly StrategyParam<decimal> _lotCoefficient;
	private readonly StrategyParam<int> _maxMartingale;

	private MovingAverageConvergenceDivergence _macd;
	private readonly System.Collections.Generic.Queue<decimal> _macdHistory = new();
	private decimal? _prevHistogram;
	private decimal _currentVolume;
	private int _consecutiveLosses;
	private decimal _entryPrice;

	/// <summary>
	/// Type of candles used for MACD analysis.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

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

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

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

	/// <summary>
	/// Multiplier applied after a losing trade.
	/// </summary>
	public decimal LotCoefficient
	{
		get => _lotCoefficient.Value;
		set => _lotCoefficient.Value = value;
	}

	/// <summary>
	/// Maximum number of martingale steps before resetting.
	/// </summary>
	public int MaxMartingale
	{
		get => _maxMartingale.Value;
		set => _maxMartingale.Value = value;
	}

	/// <summary>
	/// Initialize strategy parameters.
	/// </summary>
	public MacdFourColors2MartingaleStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
			.SetDisplay("Candle Type", "Type of candles for MACD analysis", "General");

		_fastEmaPeriod = Param(nameof(FastEmaPeriod), 20)
			.SetDisplay("Fast EMA", "Fast EMA period for MACD", "Indicators")
			.SetGreaterThanZero();

		_slowEmaPeriod = Param(nameof(SlowEmaPeriod), 50)
			.SetDisplay("Slow EMA", "Slow EMA period for MACD", "Indicators")
			.SetGreaterThanZero();

		_signalPeriod = Param(nameof(SignalPeriod), 12)
			.SetDisplay("Signal Period", "Signal line smoothing period", "Indicators")
			.SetGreaterThanZero();

		_lotCoefficient = Param(nameof(LotCoefficient), 1.5m)
			.SetDisplay("Lot Coefficient", "Multiplier after a loss", "Money Management")
			.SetGreaterThanZero();

		_maxMartingale = Param(nameof(MaxMartingale), 5)
			.SetDisplay("Max Martingale", "Maximum consecutive doublings", "Money Management")
			.SetGreaterThanZero();
	}

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

		_prevHistogram = null;
		_currentVolume = Volume > 0 ? Volume : 1;
		_consecutiveLosses = 0;
		_entryPrice = 0;
		_macdHistory.Clear();

		_macd = new MovingAverageConvergenceDivergence
		{
			ShortMa = { Length = FastEmaPeriod },
			LongMa = { Length = SlowEmaPeriod }
		};

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(_macd, ProcessCandle)
			.Start();

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

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

		_macdHistory.Enqueue(macdValue);
		while (_macdHistory.Count > SignalPeriod)
			_macdHistory.Dequeue();

		if (!_macd.IsFormed || _macdHistory.Count < SignalPeriod)
		{
			_prevHistogram = null;
			return;
		}

		// Calculate signal line (SMA of MACD)
		var sum = 0m;
		var history = _macdHistory.ToArray();
		foreach (var v in history)
			sum += v;
		var signalValue = sum / history.Length;

		var histogram = macdValue - signalValue;

		if (_prevHistogram is null)
		{
			_prevHistogram = histogram;
			return;
		}

		var crossUp = _prevHistogram < 0 && histogram >= 0;
		var crossDown = _prevHistogram >= 0 && histogram < 0;

		if (crossUp)
		{
			// Check for loss on closing short
			if (Position < 0)
			{
				var pnl = _entryPrice - candle.ClosePrice;
				if (pnl < 0)
				{
					_consecutiveLosses++;
					if (_consecutiveLosses <= MaxMartingale)
						_currentVolume *= LotCoefficient;
				}
				else
				{
					_consecutiveLosses = 0;
					_currentVolume = Volume > 0 ? Volume : 1;
				}

				BuyMarket(Math.Abs(Position) + _currentVolume);
				_entryPrice = candle.ClosePrice;
			}
			else if (Position == 0)
			{
				BuyMarket(_currentVolume);
				_entryPrice = candle.ClosePrice;
			}
		}
		else if (crossDown)
		{
			// Check for loss on closing long
			if (Position > 0)
			{
				var pnl = candle.ClosePrice - _entryPrice;
				if (pnl < 0)
				{
					_consecutiveLosses++;
					if (_consecutiveLosses <= MaxMartingale)
						_currentVolume *= LotCoefficient;
				}
				else
				{
					_consecutiveLosses = 0;
					_currentVolume = Volume > 0 ? Volume : 1;
				}

				SellMarket(Math.Abs(Position) + _currentVolume);
				_entryPrice = candle.ClosePrice;
			}
			else if (Position == 0)
			{
				SellMarket(_currentVolume);
				_entryPrice = candle.ClosePrice;
			}
		}

		_prevHistogram = histogram;
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		_macd = null;
		_prevHistogram = null;
		_currentVolume = 0;
		_consecutiveLosses = 0;
		_entryPrice = 0;
		_macdHistory.Clear();

		base.OnReseted();
	}
}