Auf GitHub ansehen

Cs2011-Strategie

Überblick

Die Cs2011-Strategie ist ein Umkehrsystem, das aus dem ursprünglichen cs2011.mq5-Expertenberater übersetzt wurde. Es überwacht das MACD-Histogramm und die Signallinie jeder fertigen Kerze und sucht nach Erschöpfungsmustern rund um die Nulllinie. Der C#-Port behält die Kern-Timing-Regeln bei und macht sie über die hohe Ebene StockSharp API verfügbar.

Handelslogik

  • Nulllinienumkehr – wenn der MACD-Wert des vorherigen Balkens über Null liegt, während der Balken davor unter Null lag, gibt die Strategie ein Short-Signal aus. Der entgegengesetzte Übergang (von positiv nach negativ) gibt ein langes Signal aus. Dies ahmt die konträren Einträge nach, die im Skript MQL5 implementiert sind.
  • Signalleitungsextreme – Die Strategie speichert die letzten drei Signalleitungswerte. Ein lokales Maximum, während MACD negativ blieb, löst einen zusätzlichen Short-Eintrag aus; ein lokales Minimum, während MACD positiv blieb, löst einen Long-Eintrag aus. Dies reproduziert die Musterprüfungen basierend auf Sig[0], Sig[1] und Sig[2] in der Quelle EA.
  • Signale werden nur für fertige Kerzen ausgewertet, die von SubscribeCandles bereitgestellt werden, daher werden Teildaten ignoriert.

Positionsverwaltung

  • Die Strategie zielt auf eine feste absolute Positionsgröße (TargetVolume) ab. Wenn ein bullisches Signal eintrifft, werden genügend Kontrakte gekauft, um +TargetVolume zu erreichen. Das Gleiche gilt für bärische Signale für -TargetVolume. Bestehendes Engagement in die gleiche Richtung wird respektiert – es werden keine weiteren Aufträge erteilt, wenn das Ziel bereits erreicht ist.
  • StartProtection spiegelt die ursprünglichen Take-Profit- und Stop-Loss-Einstellungen wider. Punktabstände werden in UnitTypes.Point-Werte umgewandelt und an das integrierte Risikomodul übergeben. Wenn Sie einen Wert auf 0 belassen, wird die entsprechende Barriere deaktiviert.
  • Anstelle der Low-Level-Anfragestruktur aus der MQL-Version werden High-Level-Helfer (BuyMarket, SellMarket) verwendet.

Parameter

Name Standard Beschreibung
TargetVolume 1 Los Absolute Positionsgröße, die nach einem Signal erreicht wird. Ersetzt die Risk × Balance-Größenbestimmungsroutine aus EA.
TakeProfitPoints 2200 Abstand in Preispunkten für Take-Profit-Management. 0 deaktiviert den Take-Profit.
StopLossPoints 0 Abstand in Preispunkten für den Stop-Loss. 0 deaktiviert den Stop-Loss und entspricht den EA-Standardwerten.
FastEmaPeriod 30 Schnelle EMA-Länge für den MACD-Kern.
SlowEmaPeriod 500 Langsame EMA-Länge für MACD.
SignalPeriod 36 Glättungszeitraum der Signalleitung.
CandleType 1 hour Zeitrahmen Von SubscribeCandles verwendete Kerzenquelle. Passen Sie dies an den in MetaTrader verwendeten Diagrammzeitraum an.

Alle Parameter werden über Param() registriert, sodass sie in der Optimierer-Benutzeroberfläche von StockSharp optimiert werden können.

Unterschiede zur MQL5-Version

  • Die Geldverwaltungsroutine (Money_M) stützte sich auf historische Geschäfte und den Kontostand von MetaTrader. StockSharp-Strategien arbeiten mit Broker-agnostischen Portfolios, daher stellt der Port einen einfachen TargetVolume-Parameter bereit. Benutzer können ihre eigene Geldverwaltung verbinden, indem sie den Parameterwert oder die Methode ExecuteSignals überschreiben.
  • Orderanfragen werden zu Single-Market-Orders vereinfacht. Wiederholungslogik, Spread-basierte Abweichung und Handelskontextprüfungen werden von der StockSharp-Infrastruktur übernommen.
  • Die Strategie läuft auf Kerzenabonnements statt auf dem benutzerdefinierten IsNewBar-Helper. Dadurch ist gewährleistet, dass nur fertig geformte Kerzen verarbeitet werden.

Nutzungshinweise

  1. Konfigurieren Sie das Wertpapier, das Portfolio und den Kerzentyp, bevor Sie die Strategie starten.
  2. Passen Sie TargetVolume an die gewünschte nominale Losgröße an.
  3. Passen Sie optional TakeProfitPoints und StopLossPoints an, um die Schutzstufen des Originals EA zu reproduzieren.
  4. Starten Sie die Strategie – Protokollierungsnachrichten zeichnen jeden Handelsauslöser zusammen mit dem angestrebten Engagement auf.

Der Code enthält englische Inline-Kommentare, die jeden Schritt des Portierungsprozesses beschreiben.


namespace StockSharp.Samples.Strategies;

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;

/// <summary>
/// MACD based reversal strategy converted from the original cs2011 MetaTrader 5 expert advisor.
/// It reacts to zero line crosses and local extremes of the MACD signal line.
/// </summary>
public class Cs2011Strategy : Strategy
{
	private readonly StrategyParam<decimal> _targetVolume;
	private readonly StrategyParam<int> _takeProfitPoints;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _fastEmaPeriod;
	private readonly StrategyParam<int> _slowEmaPeriod;
	private readonly StrategyParam<int> _signalPeriod;
	private readonly StrategyParam<DataType> _candleType;

	private decimal? _macdPrev1;
	private decimal? _macdPrev2;
	private decimal? _signalPrev1;
	private decimal? _signalPrev2;
	private decimal? _signalPrev3;

	/// <summary>
	/// Target absolute position in lots when a bullish signal appears.
	/// </summary>
	public decimal TargetVolume
	{
		get => _targetVolume.Value;
		set => _targetVolume.Value = value;
	}

	/// <summary>
	/// Take-profit distance expressed in instrument points.
	/// </summary>
	public int TakeProfitPoints
	{
		get => _takeProfitPoints.Value;
		set => _takeProfitPoints.Value = value;
	}

	/// <summary>
	/// Stop-loss distance expressed in instrument points.
	/// </summary>
	public int StopLossPoints
	{
		get => _stopLossPoints.Value;
		set => _stopLossPoints.Value = value;
	}

	/// <summary>
	/// Fast EMA length used in MACD calculation.
	/// </summary>
	public int FastEmaPeriod
	{
		get => _fastEmaPeriod.Value;
		set => _fastEmaPeriod.Value = value;
	}

	/// <summary>
	/// Slow EMA length used in MACD calculation.
	/// </summary>
	public int SlowEmaPeriod
	{
		get => _slowEmaPeriod.Value;
		set => _slowEmaPeriod.Value = value;
	}

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

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

	/// <summary>
	/// Initialize strategy parameters with defaults close to the original expert.
	/// </summary>
	public Cs2011Strategy()
	{
		_targetVolume = Param(nameof(TargetVolume), 1m)
			.SetDisplay("Target Volume", "Absolute position size targeted on entries", "Risk")
			
			.SetOptimize(0.5m, 3m, 0.5m);

		_takeProfitPoints = Param(nameof(TakeProfitPoints), 2200)
			.SetDisplay("Take Profit (points)", "Take-profit distance in price points", "Risk")
			
			.SetOptimize(200, 4000, 200);

		_stopLossPoints = Param(nameof(StopLossPoints), 0)
			.SetDisplay("Stop Loss (points)", "Stop-loss distance in price points", "Risk")
			
			.SetOptimize(0, 2000, 200);

		_fastEmaPeriod = Param(nameof(FastEmaPeriod), 30)
			.SetDisplay("Fast EMA", "Fast EMA period for MACD", "Indicators")
			
			.SetOptimize(10, 60, 5);

		_slowEmaPeriod = Param(nameof(SlowEmaPeriod), 50)
			.SetDisplay("Slow EMA", "Slow EMA period for MACD", "Indicators")
			
			.SetOptimize(200, 700, 20);

		_signalPeriod = Param(nameof(SignalPeriod), 36)
			.SetDisplay("Signal Period", "Signal line period for MACD", "Indicators")
			
			.SetOptimize(10, 60, 5);

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Source timeframe for MACD", "General");
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

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

		_macdPrev1 = null;
		_macdPrev2 = null;
		_signalPrev1 = null;
		_signalPrev2 = null;
		_signalPrev3 = null;
	}

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

		Volume = TargetVolume;

		var macd = new MovingAverageConvergenceDivergenceSignal
		{
			Macd =
			{
				ShortMa = { Length = FastEmaPeriod },
				LongMa = { Length = SlowEmaPeriod }
			},
			SignalMa = { Length = SignalPeriod }
		};

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

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

		// removed StartProtection
	}

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

		if (indicatorValue is not IMovingAverageConvergenceDivergenceSignalValue macdValue)
			return;

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

		var prevMacd1 = _macdPrev1;
		var prevMacd2 = _macdPrev2;
		var prevSignal1 = _signalPrev1;
		var prevSignal2 = _signalPrev2;
		var prevSignal3 = _signalPrev3;

		var upSignal = false;
		var downSignal = false;

		if (prevMacd1.HasValue && prevMacd2.HasValue)
		{
			if (prevMacd1 > 0m && prevMacd2 < 0m)
				downSignal = true;

			if (prevMacd1 < 0m && prevMacd2 > 0m)
				upSignal = true;
		}

		if (prevMacd2.HasValue && prevSignal1.HasValue && prevSignal2.HasValue && prevSignal3.HasValue)
		{
			if (prevMacd2 < 0m && prevSignal1 < prevSignal2 && prevSignal2 > prevSignal3)
				downSignal = true;

			if (prevMacd2 > 0m && prevSignal1 > prevSignal2 && prevSignal2 < prevSignal3)
				upSignal = true;
		}

		if (upSignal || downSignal)
			ExecuteSignals(upSignal, downSignal);

		_macdPrev2 = _macdPrev1;
		_macdPrev1 = macd;
		_signalPrev3 = _signalPrev2;
		_signalPrev2 = _signalPrev1;
		_signalPrev1 = signal;
	}

	private void ExecuteSignals(bool upSignal, bool downSignal)
	{
		// removed IsFormedAndOnlineAndAllowTrading guard

		if (upSignal)
		{
			var targetPosition = TargetVolume;
			var difference = targetPosition - Position;
			if (difference > 0m)
			{
				BuyMarket(difference);
				LogInfo($"Buy signal executed. Target={targetPosition:F2}, current position after order={Position + difference:F2}");
			}
		}

		if (downSignal)
		{
			var targetPosition = -TargetVolume;
			var difference = targetPosition - Position;
			if (difference < 0m)
			{
				SellMarket(-difference);
				LogInfo($"Sell signal executed. Target={targetPosition:F2}, current position after order={Position - difference:F2}");
			}
		}
	}
}