Auf GitHub ansehen

EMA Cross-Trailing-Strategie

Überblick

Diese Strategie ist die StockSharp-Umstellung des MetaTrader 4-Expertenberaters mit Sitz in MQL/8606/EMA_CROSS_2.mq4. Es behält die ursprüngliche Idee bei, die Beziehung zwischen einem langsamen und einem schnellen exponentiellen gleitenden Durchschnitt zu verfolgen und eine einzelne Marktposition zu eröffnen, wenn ein Crossover auftritt. Schutzexits (Take-Profit, Stop-Loss und Trailing-Stop) werden über den übergeordneten StartProtection-Helfer abgewickelt, sodass das Verhalten die MetaTrader-Implementierung unter Verwendung der Best Practices von StockSharp widerspiegelt.

Handelslogik

  • Erstellen Sie Kerzen mit den konfigurierbaren CandleType (standardmäßig 15-Minuten-Balken) und füttern Sie zwei EMA-Indikatoren: Der langsame EMA verwendet SlowEmaLength und der schnelle EMA verwendet FastEmaLength.
  • Behalten Sie die aktuelle Richtung des langsamen EMA relativ zum schnellen EMA bei. Die erste abgeschlossene Kerze, nachdem beide Indikatoren gebildet wurden, wird nur zur Initialisierung dieser Richtung verwendet, genau wie der first_time-Schutz im ursprünglichen Berater.
  • Wenn sich der langsame EMA über den schnellen EMA bewegt (die neue Richtung wird zu 1) und die Strategie flach ist, senden Sie eine Marktkauforder. Wenn der langsame EMA unter den schnellen EMA fällt (die neue Richtung wird zu 2) und die Strategie flach ist, senden Sie einen Marktverkaufsauftrag. Dies reproduziert die genaue Auf-/Ab-Zuordnung der MQL-Funktion Crossed(LEma, SEma).
  • Es kann jeweils nur eine Position aktiv sein. Während ein Trade offen ist (oder die Einstiegsorder noch aussteht), werden zusätzliche Crossovers ignoriert.

Handels- und Risikomanagement

  • StartProtection konfiguriert Take-Profit-, Stop-Loss- und Trailing-Stop-Abstände in Preiseinheiten, die aus dem Instrument PriceStep berechnet werden. Trailing Stops sind optional: Setzen Sie TrailingStopPips auf Null, um sie zu deaktivieren.
  • Aufträge werden mit BuyMarket/SellMarket aufgegeben und vom Markt geschlossen, wenn ein Schutzniveau ausgelöst wird, genau wie die OrderSend- und Nachfolgelogik des ursprünglichen Beraters.
  • Die Basislosgröße wird durch OrderVolume gesteuert. Vor jeder Eingabe wird auf die Lautstärkestufe, das Minimum und das Maximum des Instruments abgeglichen, um eine Ablehnung zu vermeiden.

Parameter

Parameter Beschreibung
TakeProfitPips Abstand in Pips (Preisschritten), der für den schützenden Take-Profit verwendet wird. Standard: 20.
StopLossPips Abstand in Pips, der für den schützenden Stop-Loss verwendet wird. Standard: 30.
TrailingStopPips Nachlaufdistanz in Pips. Auf 0 setzen, um das Nachstellen zu deaktivieren. Standard: 50.
OrderVolume Losgröße der Markteintritte vor der Ausrichtung. Standard: 2.
FastEmaLength Periode des schnellen EMA, der auf die Schlusskurse angewendet wird. Standard: 5.
SlowEmaLength Zeitraum der langsamen EMA, angewendet auf die Schlusskurse. Standard: 60.
CandleType Zeitrahmen für den Kerzenbau. Standard: 15 Minuten.

Notizen

  • Die Strategie wartet, bis beide EMAs vollständig gebildet sind, bevor sie auf einen Crossover reagiert. Dabei wird die Bars < 100-Prüfung aus dem MQL-Skript entfernt und gleichzeitig die gleiche Stabilität erreicht.
  • Da nur Marktaufträge verwendet werden, gibt es keine einzelnen OrderModify-Aufrufe. Das integrierte Schutzmodul positioniert den Trailing Stop automatisch auf die gleiche Weise neu, wie die MetaTrader-Schleife OrderStopLoss aktualisiert hat.
  • Es wird kein Python-Port bereitgestellt (auf Anfrage); Es ist nur die C#-Implementierung enthalten.
using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// EMA crossover strategy with trailing stop.
/// Buys when fast EMA crosses above slow EMA, sells on the opposite crossover.
/// </summary>
public class EmaCrossTrailingStrategy : Strategy
{
	private readonly StrategyParam<int> _fastEmaLength;
	private readonly StrategyParam<int> _slowEmaLength;
	private readonly StrategyParam<DataType> _candleType;

	private int _currentDirection;
	private bool _hasInitialDirection;

	public EmaCrossTrailingStrategy()
	{
		_fastEmaLength = Param(nameof(FastEmaLength), 5)
			.SetDisplay("Fast EMA", "Length of the fast exponential moving average.", "Indicator");

		_slowEmaLength = Param(nameof(SlowEmaLength), 60)
			.SetDisplay("Slow EMA", "Length of the slow exponential moving average.", "Indicator");

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
			.SetDisplay("Candle Type", "Time frame used to build candles and EMAs.", "General");
	}

	public int FastEmaLength
	{
		get => _fastEmaLength.Value;
		set => _fastEmaLength.Value = value;
	}

	public int SlowEmaLength
	{
		get => _slowEmaLength.Value;
		set => _slowEmaLength.Value = value;
	}

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

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

		_currentDirection = 0;
		_hasInitialDirection = false;
	}

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

		var fastEma = new ExponentialMovingAverage { Length = FastEmaLength };
		var slowEma = new ExponentialMovingAverage { Length = SlowEmaLength };

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

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

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

		// Determine direction: 1 = fast above slow (bullish), -1 = fast below slow (bearish)
		var newDirection = fastValue > slowValue ? 1 : fastValue < slowValue ? -1 : 0;

		if (newDirection == 0)
			return;

		if (!_hasInitialDirection)
		{
			_currentDirection = newDirection;
			_hasInitialDirection = true;
			return;
		}

		if (newDirection == _currentDirection)
			return;

		var prevDirection = _currentDirection;
		_currentDirection = newDirection;

		// Crossover detected
		if (newDirection == 1 && prevDirection == -1)
		{
			// Bullish crossover
			if (Position < 0)
				BuyMarket(); // Close short
			if (Position <= 0)
				BuyMarket(); // Open long
		}
		else if (newDirection == -1 && prevDirection == 1)
		{
			// Bearish crossover
			if (Position > 0)
				SellMarket(); // Close long
			if (Position >= 0)
				SellMarket(); // Open short
		}
	}
}