Auf GitHub ansehen

EMA RSI Volatilitätsadaptive Crossover-Strategie

Diese Strategie ist eine direkte Portierung des MetaTrader Expert Advisors EA_MARSI_1-02. Es handelt sich um Crossovers zwischen zwei Kopien von Der benutzerdefinierte EMA_RSI_VA-Indikator von Integer, ein volatilitätsadaptiver gleitender Durchschnitt, der vom Relative Strength Index (RSI) gesteuert wird. Immer wenn die langsame Linie die schnelle Linie kreuzt, kehrt der Motor die Nettoposition um und reproduziert so den ursprünglichen „Flip-on-Crossover“. Verhalten unter Beachtung der Best Practices für die Auftragsabwicklung von StockSharp.

Anzeigemechanik

Das Originalpaket MQL wird mit einem benutzerdefinierten Indikator namens EMA_RSI_VA geliefert. Es berechnet einen preisgeglätteten EMA, dessen Effektivwert Die Länge wird durch den Abstand von RSI von seinem neutralen Wert moduliert. Der StockSharp-Port führt das ein EmaRsiVolatilityAdaptiveIndicator-Klasse, die die Formel genau repliziert:

  1. Berechnen Sie RSI für die ausgewählte AppliedPrice-Quelle mit dem Zeitraum RSIPeriod.
  2. Messen Sie den RSI-Abstand von 50 (|RSI - 50| + 1), der als Volatilitäts-Proxy dient.
  3. Leiten Sie einen adaptiven Multiplikator ab multi = (5 + 100 / RSIPeriod) / (0.06 + 0.92 * dist + 0.02 * dist^2).
  4. Multiplizieren Sie den konfigurierten Zeitraum EMA mit diesem Multiplikator, um eine dynamische Länge pdsx zu erhalten.
  5. Wenden Sie die Standardrekursion EMA mit dem Glättungsfaktor 2 / (pdsx + 1) an und verwenden Sie dabei den angewendeten Preis der Kerze als Eingabe.

Große RSI-Auslenkungen verkürzen das Glättungsfenster und sorgen dafür, dass die Leitung schneller reagiert. Ein flacher RSI verlängert das Fenster und dämpft Lärm. Sowohl die langsame als auch die schnelle Zeile stellen den gesamten Satz der von StockSharp.Messages.AppliedPrice unterstützten Preismodi bereit.

Handelsregeln

  • Signalerkennung
    • Verkaufen / Leerverkauf: vorheriges langsames < vorheriges schnelles und aktuelles langsames ≥ aktuelles schnelles.
    • Kauf / Long: vorheriges langsames > vorheriges schnelles und aktuelles langsames ≤ aktuelles schnelles.
  • Ausführung
    • Die Strategie analysiert nur fertige Kerzen aus der konfigurierten Kerzenserie.
    • Wenn ein Signal auftritt, wird eine Marktorder übermittelt, die sowohl das bestehende Engagement schließt als auch die neue Richtung eröffnet.
    • Umtauschlimits werden durch Security.MinVolume, Security.VolumeStep und Security.MaxVolume eingehalten.
  • Umbuchungen
    • Aufträge werden saldiert, sodass ein einzelner SellMarket- oder BuyMarket-Aufruf die Position über der Nulllinie annimmt und mit der übereinstimmt MQL-Verhalten, bei dem ein entgegengesetztes Signal den Handel sofort umkehrt.

Risikomanagement

  • TakeProfitPoints und StopLossPoints replizieren die TP/SL-Felder des Expert Advisors (ausgedrückt in Preispunkten). Wenn entweder Der Wert ist ungleich Null. Die Strategie startet den Schutzmanager von StockSharp mit absoluten Preisversätzen und useMarketOrders = true. um die ursprüngliche OrderSend Stopp-/Limit-Änderungsschleife widerzuspiegeln.
  • UseBalanceMultiplier implementiert den use_Multpl-Schalter. Bei Aktivierung wird das effektive Auftragsvolumen erhöht Volume * PortfolioEquity / MaxDrawdown mit einer defensiven Klammer, um Einschränkungen auszutauschen.
  • Der Aufruf der Basisklasse StartProtection() wird weiterhin ausgeführt, sodass externe Risikomodule Trailing- oder Break-Even-Anhänge anhängen können Logik, falls erforderlich.

Parameter

Parameter Standard Beschreibung
Volume 0.1 Basis-Market-Order-Größe vor Anwendung eines Saldomultiplikators.
TakeProfitPoints 0 Take-Profit-Distanz in Instrumentenpunkten; 0 deaktiviert die Take-Profit-Komponente.
StopLossPoints 0 Stop-Loss-Distanz in Instrumentenpunkten; 0 deaktiviert den Schutzstopp.
UseBalanceMultiplier false Ermöglicht die Balance-proportionale Positionsgröße, identisch mit use_Multpl im EA.
MaxDrawdown 10000 Nenner für den Saldomultiplikator; entspricht dem Max_drawdown von EA.
SlowRsiPeriod 310 RSI-Lookback für die langsame EMA_RSI_VA-Zeile.
SlowEmaPeriod 40 Basislänge von EMA für die langsame Zeile vor der Adaption von RSI.
SlowAppliedPrice Close Der Preismodus wird an den langsamen Indikator weitergeleitet.
FastRsiPeriod 200 RSI-Lookback für die schnelle EMA_RSI_VA-Zeile.
FastEmaPeriod 50 Basislänge EMA für die schnelle Linie vor der RSI-Anpassung.
FastAppliedPrice Close Preismodus wird an den Schnellindikator weitergeleitet.
CandleType TimeFrame(1m) Für Berechnungen verwendete Kerzenreihe.

Hinweise zur Implementierung

  • Der Port wird mit StockSharps High-Level-API (SubscribeCandles().Bind(...)) geschrieben, um manuelle Indikatorschleifen zu vermeiden.
  • Es werden nur abgeschlossene Kerzen verarbeitet, die mit CopyBuffer(..., 1, 2, ...)-Aufrufen in der MQL-Quelle übereinstimmen.
  • Die Volumennormalisierung verwendet Security.MinVolume, Security.VolumeStep und Security.MaxVolume und verhindert so ungültige Bestellungen echter Austausch.
  • Auf eine Python-Version wird wie gewünscht bewusst verzichtet; Das Verzeichnis enthält nur die C#-Implementierung und Dokumentation.

Das resultierende Verhalten spiegelt die Quelle EA wider und legt gleichzeitig StockSharp-freundliche Parameter und geeignete Risikokontrollen offen Designer, Runner oder jeder benutzerdefinierte Host, der auf StockSharp API basiert.

using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// EMA RSI VA Crossover: Fast/slow EMA crossover with RSI volatility filter.
/// </summary>
public class EmaRsiVaCrossStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastEmaLength;
	private readonly StrategyParam<int> _slowEmaLength;
	private readonly StrategyParam<int> _rsiLength;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;

	public EmaRsiVaCrossStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe.", "General");

		_fastEmaLength = Param(nameof(FastEmaLength), 10)
			.SetDisplay("Fast EMA", "Fast EMA period.", "Indicators");

		_slowEmaLength = Param(nameof(SlowEmaLength), 40)
			.SetDisplay("Slow EMA", "Slow EMA period.", "Indicators");

		_rsiLength = Param(nameof(RsiLength), 14)
			.SetDisplay("RSI Length", "RSI period.", "Indicators");
	}

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

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

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

	public int RsiLength
	{
		get => _rsiLength.Value;
		set => _rsiLength.Value = value;
	}

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

		_prevFast = 0;
		_prevSlow = 0;
		_entryPrice = 0;
	}

		protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_prevFast = 0;
		_prevSlow = 0;
		_entryPrice = 0;

		var fastEma = new ExponentialMovingAverage { Length = FastEmaLength };
		var slowEma = new ExponentialMovingAverage { Length = SlowEmaLength };
		var rsi = new RelativeStrengthIndex { Length = RsiLength };
		var atr = new AverageTrueRange { Length = 14 };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(fastEma, slowEma, rsi, atr, 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 fastVal, decimal slowVal, decimal rsiVal, decimal atrVal)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (_prevFast == 0 || _prevSlow == 0 || atrVal <= 0)
		{
			_prevFast = fastVal;
			_prevSlow = slowVal;
			return;
		}

		var close = candle.ClosePrice;

		if (Position > 0)
		{
			if (close >= _entryPrice + atrVal * 3m || close <= _entryPrice - atrVal * 2m || (fastVal < slowVal && _prevFast >= _prevSlow))
			{
				SellMarket();
				_entryPrice = 0;
			}
		}
		else if (Position < 0)
		{
			if (close <= _entryPrice - atrVal * 3m || close >= _entryPrice + atrVal * 2m || (fastVal > slowVal && _prevFast <= _prevSlow))
			{
				BuyMarket();
				_entryPrice = 0;
			}
		}

		if (Position == 0)
		{
			if (fastVal > slowVal && _prevFast <= _prevSlow && rsiVal > 40 && rsiVal < 70)
			{
				_entryPrice = close;
				BuyMarket();
			}
			else if (fastVal < slowVal && _prevFast >= _prevSlow && rsiVal > 30 && rsiVal < 60)
			{
				_entryPrice = close;
				SellMarket();
			}
		}

		_prevFast = fastVal;
		_prevSlow = slowVal;
	}
}