Auf GitHub ansehen

Color Ma RSI Trigger Duplex Strategie

Diese Strategie portiert den Experten-Berater Exp_ColorMaRsi-Trigger_Duplex.mq5 auf die StockSharp High-Level-API. Sie betreibt zwei unabhängige MaRsi-Trigger-Detektoren: Der Long-Block entscheidet, wann Long-Positionen geöffnet oder geschlossen werden sollen, während der Short-Block dieselbe Aufgabe für Short-Positionen übernimmt. Jeder Detektor bewertet, ob ein benutzerdefinierter Indikator bullischen (+1), neutralen (0) oder bärischen (-1) Marktdruck meldet. Die ursprüngliche MetaTrader-Logik wird beibehalten, einschließlich der verzögerten Bestätigung, die auf zwei abgeschlossene Balken wartet, bevor sie reagiert, und der separaten Geldverwaltungseinstellungen pro Richtung.

Trading-Idee

  1. Berechnung zweier exponentieller gleitender Durchschnitte (schnell und langsam) und zweier RSI-Oszillatoren (schnell und langsam) auf einer wählbaren Kerzenserie für jeden Block.
  2. Bei jeder abgeschlossenen Kerze gibt der Indikator +1 zurück, wenn beide schnellen Studien ihre langsamen Gegenstücke übersteigen, -1 wenn beide schwächer sind und 0 sonst. Der Rohwert wird auf den Bereich [-1, 1] begrenzt, wie im MT5-Indikator.
  3. Die Strategie speichert eine fortlaufende Geschichte von Indikatorwerten. Für einen konfigurierten SignalBar-Versatz vergleicht sie den Wert der Kerze SignalBar + 1 Perioden zurück (genannt older) mit dem Wert der Kerze SignalBar Perioden zurück (genannt recent).
  4. Long-Logik:
    • Wenn older < 0 schließt der Long-Block alle aktiven Long-Positionen (sofern Long-Ausstiege aktiviert sind).
    • Wenn older > 0 und recent <= 0 bereitet der Long-Block einen neuen Long-Einstieg vor (sofern Long-Einstiege aktiviert sind).
  5. Die Short-Logik spiegelt den Long-Block:
    • Wenn older > 0 verlässt der Short-Block bestehende Short-Positionen (wenn Short-Ausstiege aktiviert sind).
    • Wenn older < 0 und recent >= 0 öffnet der Block eine neue Short-Position (wenn Short-Einstiege aktiviert sind).
  6. Optionale Stop-Loss- und Take-Profit-Niveaus, ausgedrückt in Instrumentenpreisschritten, schließen Positionen, wenn der Preis die konfigurierten Niveaus kreuzt.

Die beiden Blöcke können unterschiedliche Kerzen-Zeitrahmen und Preisquellen abonnieren, sodass der Benutzer das ursprüngliche Dual-Zeitrahmen-Verhalten replizieren oder alternative Kombinationen ausprobieren kann.

Parameter

Parameter Beschreibung
LongCandleType, ShortCandleType Von Long- und Short-Block verwendete Kerzen-Datenserien. Standardmäßig 4-Stunden-Kerzen.
LongVolume, ShortVolume Gehandeltes Marktvolumen, wenn der entsprechende Block eine neue Position eröffnet.
LongAllowOpen, ShortAllowOpen Öffnen neuer Positionen für jeden Block aktivieren oder deaktivieren.
LongAllowClose, ShortAllowClose Schließsignale für jeden Block aktivieren oder deaktivieren.
LongStopLossPoints, ShortStopLossPoints Stop-Loss-Abstand gemessen in Preisschritten. Auf 0 setzen zum Deaktivieren.
LongTakeProfitPoints, ShortTakeProfitPoints Take-Profit-Abstand gemessen in Preisschritten. Auf 0 setzen zum Deaktivieren.
LongSignalBar, ShortSignalBar Anzahl abgeschlossener Balken zwischen der aktuellen Kerze und der für die Entscheidungslogik verwendeten.
LongRsiPeriod, LongRsiLongPeriod, ShortRsiPeriod, ShortRsiLongPeriod Längen der schnellen und langsamen RSI-Oszillatoren.
LongMaPeriod, LongMaLongPeriod, ShortMaPeriod, ShortMaLongPeriod Längen der schnellen und langsamen gleitenden Durchschnitte.
LongRsiPrice, ShortRsiPrice Preisquelle für den schnellen RSI (Schluss, Eröffnung, Hoch, Tief, Median, typisch oder gewichtet).
LongRsiLongPrice, ShortRsiLongPrice Preisquelle für den langsamen RSI.
LongMaPrice, ShortMaPrice Preisquelle für den schnellen gleitenden Durchschnitt.
LongMaLongPrice, ShortMaLongPrice Preisquelle für den langsamen gleitenden Durchschnitt.
LongMaType, ShortMaType Gleitender-Durchschnitt-Methode für die schnelle Linie (einfach, exponentiell, geglättet oder gewichtet).
LongMaLongType, ShortMaLongType Gleitender-Durchschnitt-Methode für die langsame Linie.

Handelsregeln

  1. Warten, bis die ausgewählte Kerzenserie abgeschlossene Balken produziert und alle Indikatoren vollständig aufgewärmt sind.
  2. Für jeden Block den MaRsi-Trigger-Wert berechnen und den Historienpuffer aktualisieren.
  3. Wenn die Historie mindestens SignalBar + 2 Einträge enthält, die Long- und Short-Bedingungen aus dem Abschnitt Trading-Idee auswerten.
  4. Vor dem Öffnen einer Position neutralisiert die Strategie alle entgegengesetzten Exposures (wenn das entsprechende Schließ-Flag aktiviert ist). Ein neuer Long-Einstieg kauft beispielsweise genug Volumen, um eine Short-Position zu schließen, und fügt dann erst das Long-Volumen hinzu.
  5. Nachdem eine Position eröffnet wurde, werden optionale Stop-Loss- und Take-Profit-Niveaus bei jeder abgeschlossenen Kerze überwacht.
  6. Eröffnungs- und Schließorders werden als Marktorders über die High-Level-Hilfsmethoden BuyMarket und SellMarket gesendet.

Risikomanagement

  • Stops und Ziele werden mit Security.PriceStep gemessen. Wenn das Instrument keinen Preisschritt bereitstellt, wird ein Standardwert von 1 angenommen, entsprechend dem Verhalten vieler bestehender Strategien in diesem Repository.
  • Long- und Short-Blöcke behalten unabhängige Stop- und Take-Einstellungen.
  • Die Strategie platziert keine zusätzlichen Schutzorders (wie Trailing Stops); das Verhalten spiegelt den MT5-Experten, der Trades nur schließt, wenn der Indikator feuert oder der harte Stop/Ziel erreicht wird.

Hinweise

  • Der StockSharp-Port gibt Marktorders sofort nach Abschluss der auswertenden Kerze aus. In MetaTrader plante der Experte seine Orders für den Eröffnungszeitpunkt des nächsten Balkens über Zeitstempel-Offsets; beide Verhaltensweisen richten sich effektiv aus, da StockSharp das Signal verarbeitet, sobald die Kerze schließt.
  • Der ursprüngliche EA exponierte mehrere Geldverwaltungsmodi (LOT, BALANCE usw.). StockSharp-Strategien arbeiten mit direkten Volumenwerten, daher hält der Port das Volumen als einfachen Parameter (LongVolume/ShortVolume).
  • Slippage und Magic-Number-spezifische Logik aus der MT5-Hilfsbibliothek sind in StockSharp nicht notwendig und wurden weggelassen.
  • Indikatorberechnungen nutzen die eingebauten StockSharp-Implementierungen für gleitende Durchschnitte und RSI; die Ausgabe wird auf [-1, 1] begrenzt, um dem ursprünglichen ColorMaRsi-Trigger-Indikator zu entsprechen.
namespace StockSharp.Samples.Strategies;

using System;
using System.Collections.Generic;

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

/// <summary>
/// Recreates the Exp_ColorMaRsi-Trigger_Duplex expert advisor.
/// Combines fast/slow MA and fast/slow RSI comparisons into a color code (+1/0/-1).
/// Trades based on color code transitions.
/// </summary>
public class ColorMaRsiTriggerDuplexStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastMaPeriod;
	private readonly StrategyParam<int> _slowMaPeriod;
	private readonly StrategyParam<int> _fastRsiPeriod;
	private readonly StrategyParam<int> _slowRsiPeriod;
	private readonly StrategyParam<int> _signalBar;
	private readonly StrategyParam<int> _signalCooldownBars;

	private readonly List<decimal> _colorHistory = new();
	private int _cooldownRemaining;

	/// <summary>Candle type.</summary>
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	/// <summary>Fast MA period.</summary>
	public int FastMaPeriod { get => _fastMaPeriod.Value; set => _fastMaPeriod.Value = value; }
	/// <summary>Slow MA period.</summary>
	public int SlowMaPeriod { get => _slowMaPeriod.Value; set => _slowMaPeriod.Value = value; }
	/// <summary>Fast RSI period.</summary>
	public int FastRsiPeriod { get => _fastRsiPeriod.Value; set => _fastRsiPeriod.Value = value; }
	/// <summary>Slow RSI period.</summary>
	public int SlowRsiPeriod { get => _slowRsiPeriod.Value; set => _slowRsiPeriod.Value = value; }
	/// <summary>Signal bar shift.</summary>
	public int SignalBar { get => _signalBar.Value; set => _signalBar.Value = value; }
	/// <summary>Bars to wait between reversals.</summary>
	public int SignalCooldownBars { get => _signalCooldownBars.Value; set => _signalCooldownBars.Value = value; }

	public ColorMaRsiTriggerDuplexStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe for candles", "General");

		_fastMaPeriod = Param(nameof(FastMaPeriod), 5)
			.SetDisplay("Fast MA Period", "Fast moving average length", "Indicators");

		_slowMaPeriod = Param(nameof(SlowMaPeriod), 10)
			.SetDisplay("Slow MA Period", "Slow moving average length", "Indicators");

		_fastRsiPeriod = Param(nameof(FastRsiPeriod), 3)
			.SetDisplay("Fast RSI Period", "Fast RSI length", "Indicators");

		_slowRsiPeriod = Param(nameof(SlowRsiPeriod), 13)
			.SetDisplay("Slow RSI Period", "Slow RSI length", "Indicators");

		_signalBar = Param(nameof(SignalBar), 1)
			.SetDisplay("Signal Bar", "History shift for signal evaluation", "Strategy");

		_signalCooldownBars = Param(nameof(SignalCooldownBars), 8)
			.SetGreaterThanZero()
			.SetDisplay("Signal Cooldown", "Bars to wait between reversals", "Strategy");
	}

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

		lock (_colorHistory)
			_colorHistory.Clear();

		_cooldownRemaining = 0;
	}

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

		lock (_colorHistory)
			_colorHistory.Clear();

		_cooldownRemaining = 0;

		var fastMa = new ExponentialMovingAverage { Length = FastMaPeriod };
		var slowMa = new ExponentialMovingAverage { Length = SlowMaPeriod };
		var fastRsi = new RelativeStrengthIndex { Length = FastRsiPeriod };
		var slowRsi = new RelativeStrengthIndex { Length = SlowRsiPeriod };

		var subscription = SubscribeCandles(CandleType);

		subscription
			.Bind(fastMa, slowMa, fastRsi, slowRsi, ProcessCandle)
			.Start();

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

	private void ProcessCandle(ICandleMessage candle, decimal fastMaVal, decimal slowMaVal, decimal fastRsiVal, decimal slowRsiVal)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		if (_cooldownRemaining > 0)
			_cooldownRemaining--;

		// Calculate color code from MA and RSI comparisons
		var score = 0m;

		if (fastMaVal > slowMaVal)
			score = 1m;
		else if (fastMaVal < slowMaVal)
			score = -1m;

		if (fastRsiVal > slowRsiVal)
			score += 1m;
		else if (fastRsiVal < slowRsiVal)
			score -= 1m;

		// Clamp to [-1, 1]
		if (score > 1m) score = 1m;
		else if (score < -1m) score = -1m;

		decimal recent;
		decimal older;

		lock (_colorHistory)
		{
			_colorHistory.Insert(0, score);
			var maxHistory = Math.Max(2, SignalBar + 2);
			while (_colorHistory.Count > maxHistory)
				_colorHistory.RemoveAt(_colorHistory.Count - 1);

			if (_colorHistory.Count <= SignalBar + 1)
				return;

			recent = _colorHistory[SignalBar];
			older = _colorHistory[SignalBar + 1];
		}

		var longOpen = older == -1m && recent == 1m;
		var shortOpen = older == 1m && recent == -1m;
		var longExit = Position > 0 && recent < 0m;
		var shortExit = Position < 0 && recent > 0m;

		if (longExit)
		{
			SellMarket(Position);
			_cooldownRemaining = SignalCooldownBars;
		}
		else if (shortExit)
		{
			BuyMarket(Math.Abs(Position));
			_cooldownRemaining = SignalCooldownBars;
		}
		else if (_cooldownRemaining == 0 && longOpen && Position <= 0)
		{
			BuyMarket(Volume + Math.Abs(Position));
			_cooldownRemaining = SignalCooldownBars;
		}
		else if (_cooldownRemaining == 0 && shortOpen && Position >= 0)
		{
			SellMarket(Volume + Math.Abs(Position));
			_cooldownRemaining = SignalCooldownBars;
		}
	}
}