Auf GitHub ansehen

Strategie ColorMetroDuplexStrategy

Übersicht

ColorMetroDuplexStrategy ist eine C#-Konvertierung des MetaTrader-5-Experten Exp_ColorMETRO_Duplex. Der ursprüngliche Roboter verwendet zwei unabhängige Instanzen des ColorMETRO-Indikators, um Long- und Short-Handelsmodule zu verwalten. Jedes Modul arbeitet mit seiner eigenen Kerzenabonnierung, wertet zwei gestaffelte RSI-Envelopes aus, die vom ColorMETRO-Indikator erzeugt werden, und öffnet oder schließt optional Positionen, wenn die schnellen und langsamen Envelopes kreuzen.

Die StockSharp-Version behält beide Module bei und reproduziert dieselben Signalauswertungsregeln, während sie die High-Level-API für Kerzenabonnements, Orderverwaltung und Indikatoranbindung verwendet. Ein benutzerdefinierter ColorMetroIndicator ist enthalten, um die MT5-iCustom-Implementierung nachzuahmen, und stellt die schnellen und langsamen ColorMETRO-Bänder zusammen mit dem internen RSI-Wert bereit.

Funktionsweise

  1. Zwei SignalModule-Instanzen werden erstellt — Long und Short — jede mit ihrer eigenen Kerzenserie, ColorMETRO-Einstellungen und Handelsverwaltungsoptionen.
  2. Wenn die Strategie startet, abonniert jedes Modul seinen konfigurierten Zeitrahmen und bindet den ColorMetroIndicator über SubscribeCandles(...).BindEx(...).
  3. Für jede abgeschlossene Kerze erzeugt der Indikator:
    • Das schnelle ColorMETRO-Band (schneller RSI-Envelope).
    • Das langsame ColorMETRO-Band (langsamer RSI-Envelope).
    • Den zugrunde liegenden RSI-Wert (nur als Referenz verwendet).
  4. Das Modul speichert die Indikatorhistorie und wertet die letzten zwei Werte mit dem konfigurierten SignalBar-Versatz aus (entsprechend der CopyBuffer-Logik von MT5).
  5. Handelsregeln:
    • Long-Modul
      • Öffnen: Das schnelle Band lag auf dem vorherigen Balken über dem langsamen und liegt jetzt darunter oder gleich.
      • Schließen: Das langsame Band lag auf dem vorherigen Balken über dem schnellen.
    • Short-Modul
      • Öffnen: Das schnelle Band lag auf dem vorherigen Balken unter dem langsamen und liegt jetzt darüber oder gleich.
      • Schließen: Das langsame Band lag auf dem vorherigen Balken unter dem schnellen.
  6. Aufträge werden über BuyMarket / SellMarket geroutet. Die aktuelle Nettoposition wird berücksichtigt — entgegengesetzte Trades schließen das bestehende Engagement, bevor ein neues eröffnet wird.

Parameter

Jedes Modul stellt eine dedizierte Parametergruppe bereit. Die Standardwerte spiegeln den MT5-Experten wider.

Gemeinsame Marktparameter

  • Long_Volume, Short_Volume — Handelsgröße (Lots) für neue Einstiege.
  • Long_OpenAllowed, Short_OpenAllowed — Eröffnen von Trades für das Modul aktivieren oder deaktivieren.
  • Long_CloseAllowed, Short_CloseAllowed — Automatische Ausstiege aktivieren oder deaktivieren.
  • Long_MarginMode, Short_MarginMode — Geldverwaltungsmodus für Kompatibilität beibehalten (kein Effekt in diesem Port).
  • Long_StopLoss, Long_TakeProfit, Long_Deviation, Short_StopLoss, Short_TakeProfit, Short_Deviation — Für Dokumentationszwecke reserviert; Stops und Slippage-Kontrolle sind in dieser Version nicht automatisiert.
  • Long_Magic, Short_Magic — Ursprüngliche MT5-Magic-Numbers als Referenz beibehalten.

Indikatorparameter

  • Long_CandleType, Short_CandleType — Zeitrahmen für jedes ColorMETRO-Modul.
  • Long_PeriodRSI, Short_PeriodRSI — RSI-Länge innerhalb des ColorMETRO-Algorithmus.
  • Long_StepSizeFast, Short_StepSizeFast — Schritt (in RSI-Punkten) für das schnelle Envelope.
  • Long_StepSizeSlow, Short_StepSizeSlow — Schritt für das langsame Envelope.
  • Long_SignalBar, Short_SignalBar — Balkenversatz beim Lesen der Indikatorbuffer (identisch mit dem MT5-SignalBar-Eingang).
  • Long_AppliedPrice, Short_AppliedPrice — Preisquelle für die RSI-Berechnung (standardmäßig Schlusskurs).

Unterschiede gegenüber MT5

  • Positionsmodell — StockSharp-Strategien arbeiten mit der Nettoposition. Der ursprüngliche Experte speicherte separate Positionen über Magic Numbers; der Port schließt das aktuelle Engagement, bevor die entgegengesetzte Seite geöffnet wird.
  • Geldverwaltung — Margin-Modi und Abweichungseinstellungen werden als Parameter beibehalten, aber nicht automatisch angewendet. Verwenden Sie die Volume-Eingaben zur Größensteuerung.
  • Stop-Loss / Take-Profit — Der MT5-Experte platzierte bei jeder Order Schutzstops. Die StockSharp-Version behält die Abstände als Referenzparameter, aber tatsächliche Stop-Orders müssen bei Bedarf separat implementiert werden.
  • Zeitstufenkontrolle — Der MT5-Code verwendete globale Variablen, um nur einen Trade pro Signalzeitraum sicherzustellen. In StockSharp verarbeiten wir jede abgeschlossene Kerze einmal und verlassen uns auf die Nettopositionsprüfung, um doppelte Einstiege zu verhindern.

Hinweise

  • Der benutzerdefinierte ColorMetroIndicator reproduziert die MT5-Logik einschließlich der gestaffelten RSI-Envelopes und der Trendgedächtnis. Er stellt die schnellen/langsamen Bänder und den internen RSI zum Charting oder zur Fehlersuche bereit.
  • Kommentare im Code sind absichtlich ausführlich gehalten, um die Portierungsentscheidungen zu erläutern und weitere Anpassungen zu erleichtern.
  • Um Stop-Loss- oder Take-Profit-Automatisierung zu aktivieren, erweitern Sie SignalModule.ProcessModule, um Schutzorders mit den Risikokontrollen von StockSharp zu platzieren.
using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Conversion of the MT5 expert "Exp_ColorMETRO_Duplex".
/// Uses RSI with step-based envelopes (fast/slow) to generate long/short signals.
/// </summary>
public class ColorMetroDuplexStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<int> _fastStep;
	private readonly StrategyParam<int> _slowStep;
	private readonly StrategyParam<int> _signalCooldownBars;

	// fast envelope state
	private decimal? _fastMin, _fastMax;
	private int _fastTrend;
	private decimal? _prevFastBand;

	// slow envelope state
	private decimal? _slowMin, _slowMax;
	private int _slowTrend;
	private decimal? _prevSlowBand;
	private int _cooldownRemaining;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
	public int FastStep { get => _fastStep.Value; set => _fastStep.Value = value; }
	public int SlowStep { get => _slowStep.Value; set => _slowStep.Value = value; }
	public int SignalCooldownBars { get => _signalCooldownBars.Value; set => _signalCooldownBars.Value = value; }

	public ColorMetroDuplexStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe", "General");

		_rsiPeriod = Param(nameof(RsiPeriod), 7)
			.SetGreaterThanZero()
			.SetDisplay("RSI Period", "RSI lookback", "Indicator");

		_fastStep = Param(nameof(FastStep), 8)
			.SetGreaterThanZero()
			.SetDisplay("Fast Step", "Step size for fast envelope", "Indicator");

		_slowStep = Param(nameof(SlowStep), 24)
			.SetGreaterThanZero()
			.SetDisplay("Slow Step", "Step size for slow envelope", "Indicator");

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

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

		_fastMin = _fastMax = null;
		_slowMin = _slowMax = null;
		_fastTrend = _slowTrend = 0;
		_prevFastBand = _prevSlowBand = null;
		_cooldownRemaining = 0;
	}

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

		_fastMin = _fastMax = null;
		_slowMin = _slowMax = null;
		_fastTrend = _slowTrend = 0;
		_prevFastBand = _prevSlowBand = null;
		_cooldownRemaining = 0;

		var rsi = new RelativeStrengthIndex { Length = RsiPeriod };

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

		StartProtection(
			takeProfit: new Unit(2, UnitTypes.Percent),
			stopLoss: new Unit(1, UnitTypes.Percent)
		);

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

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

		if (_cooldownRemaining > 0)
			_cooldownRemaining--;

		var fStep = (decimal)FastStep;
		var sStep = (decimal)SlowStep;

		// fast envelope
		var fastMinCand = rsiVal - 2m * fStep;
		var fastMaxCand = rsiVal + 2m * fStep;

		if (_fastMin == null || _fastMax == null)
		{
			_fastMin = fastMinCand;
			_fastMax = fastMaxCand;
			_fastTrend = 0;

			_slowMin = rsiVal - 2m * sStep;
			_slowMax = rsiVal + 2m * sStep;
			_slowTrend = 0;
			return;
		}

		// fast trend
		if (rsiVal > _fastMax) _fastTrend = 1;
		else if (rsiVal < _fastMin) _fastTrend = -1;

		if (_fastTrend > 0 && fastMinCand < _fastMin) fastMinCand = _fastMin.Value;
		else if (_fastTrend < 0 && fastMaxCand > _fastMax) fastMaxCand = _fastMax.Value;

		// slow envelope
		var slowMinCand = rsiVal - 2m * sStep;
		var slowMaxCand = rsiVal + 2m * sStep;

		if (rsiVal > _slowMax) _slowTrend = 1;
		else if (rsiVal < _slowMin) _slowTrend = -1;

		if (_slowTrend > 0 && slowMinCand < _slowMin) slowMinCand = _slowMin.Value;
		else if (_slowTrend < 0 && slowMaxCand > _slowMax) slowMaxCand = _slowMax.Value;

		// compute band values
		decimal? fastBand = null;
		if (_fastTrend > 0) fastBand = fastMinCand + fStep;
		else if (_fastTrend < 0) fastBand = fastMaxCand - fStep;

		decimal? slowBand = null;
		if (_slowTrend > 0) slowBand = slowMinCand + sStep;
		else if (_slowTrend < 0) slowBand = slowMaxCand - sStep;

		_fastMin = fastMinCand;
		_fastMax = fastMaxCand;
		_slowMin = slowMinCand;
		_slowMax = slowMaxCand;

		if (fastBand == null || slowBand == null)
		{
			_prevFastBand = fastBand;
			_prevSlowBand = slowBand;
			return;
		}

		if (_prevFastBand == null || _prevSlowBand == null)
		{
			_prevFastBand = fastBand;
			_prevSlowBand = slowBand;
			return;
		}

		var up = fastBand.Value;
		var down = slowBand.Value;
		var prevUp = _prevFastBand.Value;
		var prevDown = _prevSlowBand.Value;

		_prevFastBand = fastBand;
		_prevSlowBand = slowBand;

		// Long signal: fast crosses below slow (up crosses down downward)
		var longOpen = prevUp > prevDown && up <= down;
		// Short signal: fast crosses above slow (up crosses down upward)
		var shortOpen = prevUp < prevDown && up >= down;

		if (_cooldownRemaining == 0 && longOpen && Position == 0)
		{
			BuyMarket();
			_cooldownRemaining = SignalCooldownBars;
		}
		else if (_cooldownRemaining == 0 && shortOpen && Position == 0)
		{
			SellMarket();
			_cooldownRemaining = SignalCooldownBars;
		}
	}
}