Auf GitHub ansehen

RSI RFTL Strategie

Diese Strategie portiert den RSI RFTL EA von MetaTrader 5 zur StockSharp-High-Level-API. Sie behält die ursprüngliche Idee bei, RSI-Swing-Trendlinien zu handeln, ergänzt durch die Recursive Filter Trend Line (RFTL) als Richtungsfilter. Die Implementierung reproduziert die Balken-für-Balken-Entscheidungsfindung des Experten und verwendet dabei idiomatische StockSharp-Konstrukte wie StrategyParam, Indikator-Bindungen und Kerzen-Abonnements.

Funktionsweise

  1. RSI-Swing-Erkennung – die letzten 500 RSI-Werte werden auf lokale Hochs und Tiefs gescannt. Gipfel müssen über 40 und 60 steigen, während Täler unter 60 und 40 fallen müssen, entsprechend der MQL-Wendepunktlogik.
  2. Trendlinienprojektion – sobald zwei gültige Hochs oder Tiefs gefunden wurden, projiziert die Strategie die entsprechende RSI-Trendlinie auf den aktuellen und den vorherigen Balken. Zwischenliegende Swings, die die 40/60-Schwellen brechen, machen die Linie ungültig, genau wie im Experten.
  3. RFTL-Bestätigung – der vorherige Wert der Recursive Filter Trend Line (berechnet mit der originalen Koeffiziententabelle) muss für Shorts über dem vorherigen Schlusskurs oder für Longs darunter liegen. Dies hält Einstiege mit dem RFTL-Filter ausgerichtet.
  4. Einstiegsfilterung – RSI muss sich auch auf der richtigen Seite des Neutralpunkts befinden: Shorts erfordern RSI über 47/50, während Longs RSI unter 55/50 erfordern.
  5. Risikoschicht – Schutz-Stop, Take-Profit und Trailing-Stop-Abstände werden in Pips ausgedrückt und bei jeder abgeschlossenen Kerze aktualisiert, womit die MQL-Trailing-Modifikationsroutine imitiert wird. Zusätzliche Ausstiege erfolgen, wenn RSI 70 überschreitet (Longs schließen) oder unter 30 fällt (Shorts schließen).

Einstiegslogik

  • Short-Aufstellung
    • Zwei RSI-Tiefs unter 60/40 definieren eine steigende Trendlinie, deren Projektion jetzt nach unten gebrochen wird (RSI[1] < Linie, RSI[2] > Linie(vorherige)).
    • Der vorherige RFTL-Wert liegt über dem vorherigen Schluss, was Abwärtsdruck bestätigt.
    • RSI bleibt auf der bullischen Seite (RSI[2] > 50, RSI[0] > 47) und die erkannten Hochs liegen weiter zurück in der Geschichte als die Tiefs (pos₂ > pos₄), entsprechend der MQL-Reihenfolgebeschränkung.
  • Long-Aufstellung
    • Zwei RSI-Hochs über 40/60 definieren eine fallende Trendlinie, deren Projektion jetzt nach oben gebrochen wird (RSI[1] > Linie, RSI[2] < Linie(vorherige)).
    • Der vorherige RFTL-Wert liegt unter dem vorherigen Schluss.
    • RSI bleibt auf der bärischen Seite (RSI[2] < 50, RSI[0] < 55) und die jüngsten Tiefs sind aktueller als die Hochs (pos₄ > pos₂).

Signale werden nur ausgewertet, nachdem alle Indikatoren gebildet sind und die notwendige Geschichte angesammelt wurde, was vorzeitige Trades auf unvollständigen Daten verhindert.

Risikomanagement

  • Stop Loss / Take Profit – in Pips konfigurierbar. Wenn die aktuelle Kerze über das jeweilige Preisniveau hinaus handelt, wird die Position sofort geschlossen und der Trailing-Zustand zurückgesetzt.
  • Trailing Stop – optional. Sobald sich der Preis um TrailingStopPips + TrailingStepPips zugunsten des Trades bewegt, folgt der Stop dem Schlusskurs, wobei vor dem nächsten Anziehen dieselbe Mindestvorwärtsbewegung (TrailingStepPips) durchgesetzt wird.
  • RSI-Notausstieg – Longs schließen, wenn RSI 70 kreuzt; Shorts schließen, wenn er unter 30 fällt. Dies spiegelt die Hard-Exits im Original-EA wider.

Parameter

Parameter Standard Beschreibung
CandleType 1 Stunde Zeitrahmen für RSI- und RFTL-Berechnungen.
TradeVolume 1 Ordervolumen bei jedem Einstieg.
RsiPeriod 30 Lookback-Periode des RSI-Oszillators.
StopLossPips 50 Schutz-Stop-Abstand in Pips (0 deaktiviert den Stop).
TakeProfitPips 50 Take-Profit-Abstand in Pips (0 deaktiviert das Ziel).
TrailingStopPips 5 Trailing-Stop-Offset in Pips (0 deaktiviert das Trailing).
TrailingStepPips 5 Zusätzliche Pip-Verbesserung vor dem Trailing-Update.

Alle Abstände werden mit dem Instrument-PriceStep multipliziert, entsprechend der Punkt/Pip-Handhabung der MQL-Version.

Verwendung

  1. Die Strategie an ein Wertpapier anhängen und CandleType auf die in MetaTrader-Tests verwendete Balkengröße setzen.
  2. Die Risikoparameter (Stop, Take, Trailing) auf die zuvor verwendeten Pip-Abstände anpassen. Ein Parameter auf 0 zu setzen deaktiviert diesen Schutz.
  3. Strategie starten; sie wird die angegebenen Kerzen abonnieren, RSI und RFTL berechnen und mit der Signalüberwachung beginnen, sobald genug Geschichte gesammelt wurde.
  4. Die Diagramm-Widgets überwachen – der Preisbereich zeigt Kerzen und die RFTL-Linie, während das zweite Fenster den RSI-Oszillator anzeigt.

Hinweise und Unterschiede

  • Der RFTL-Indikator ist direkt in C# mit der originalen Koeffiziententabelle implementiert; keine externen Dateien erforderlich.
  • Das Trade-Management bleibt auf eine einzelne Position beschränkt: Die Strategie wechselt zwischen Long, Short und Flat, genau wie der EA, der nur eine Position pro Symbol/Magic verfolgte.
  • Da Stop- und Trailing-Ausstiege innerhalb der Strategie gehandhabt werden (StockSharp führt MT5-Stops nicht automatisch aus), werden Wiedereinstiege auf dem Balken übersprungen, wo ein Schutzausstieg ausgelöst wird, was eine konservative, aber sichere Näherung ist.
  • Historische Puffer sind auf 600 Datensätze begrenzt, um die 500-Element-Arrays im Quellcode zu spiegeln und gleichzeitig unbegrenztes Speicherwachstum zu vermeiden.
  • Alle Inline-Kommentare wurden in Englisch umgeschrieben und der Code folgt den StockSharp-High-Level-API-Stilrichtlinien.
namespace StockSharp.Samples.Strategies;

using System;
using System.Collections.Generic;

using Ecng.Common;

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

/// <summary>
/// RSI-based trend strategy with a simple recursive filter (EMA) as trend confirmation.
/// Buys when RSI crosses above oversold level and EMA confirms uptrend.
/// Sells when RSI crosses below overbought level and EMA confirms downtrend.
/// </summary>
public class RsiRftlStrategy : Strategy
{
	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<int> _emaPeriod;
	private readonly StrategyParam<decimal> _overbought;
	private readonly StrategyParam<decimal> _oversold;

	private RelativeStrengthIndex _rsi;
	private ExponentialMovingAverage _ema;

	private decimal _prevRsi;
	private decimal _entryPrice;
	private int _cooldown;

	/// <summary>
	/// RSI lookback period.
	/// </summary>
	public int RsiPeriod
	{
		get => _rsiPeriod.Value;
		set => _rsiPeriod.Value = value;
	}

	/// <summary>
	/// EMA period for trend filter.
	/// </summary>
	public int EmaPeriod
	{
		get => _emaPeriod.Value;
		set => _emaPeriod.Value = value;
	}

	/// <summary>
	/// Overbought RSI level.
	/// </summary>
	public decimal Overbought
	{
		get => _overbought.Value;
		set => _overbought.Value = value;
	}

	/// <summary>
	/// Oversold RSI level.
	/// </summary>
	public decimal Oversold
	{
		get => _oversold.Value;
		set => _oversold.Value = value;
	}

	/// <summary>
	/// Initializes a new instance of the strategy.
	/// </summary>
	public RsiRftlStrategy()
	{
		_rsiPeriod = Param(nameof(RsiPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("RSI Period", "Length of the RSI oscillator", "Indicator");

		_emaPeriod = Param(nameof(EmaPeriod), 44)
			.SetGreaterThanZero()
			.SetDisplay("EMA Period", "Length of the trend EMA filter", "Indicator");

		_overbought = Param(nameof(Overbought), 75m)
			.SetDisplay("Overbought", "RSI overbought level", "Levels");

		_oversold = Param(nameof(Oversold), 25m)
			.SetDisplay("Oversold", "RSI oversold level", "Levels");
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
	}

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

		_rsi = null;
		_ema = null;
		_prevRsi = 0;
		_entryPrice = 0;
		_cooldown = 0;
	}

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

		_rsi = new RelativeStrengthIndex { Length = RsiPeriod };
		_ema = new ExponentialMovingAverage { Length = EmaPeriod };

		var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
		subscription.Bind(_rsi, _ema, OnProcess);
		subscription.Start();
	}

	private void OnProcess(ICandleMessage candle, decimal rsiValue, decimal emaValue)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (!_rsi.IsFormed || !_ema.IsFormed)
		{
			_prevRsi = rsiValue;
			return;
		}

		if (_cooldown > 0)
		{
			_cooldown--;
			_prevRsi = rsiValue;
			return;
		}

		var close = candle.ClosePrice;
		var trendUp = close > emaValue;
		var trendDown = close < emaValue;

		// Buy: RSI crosses above oversold + uptrend
		if (_prevRsi < Oversold && rsiValue >= Oversold && trendUp && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();

			BuyMarket();
			_entryPrice = close;
			_cooldown = 10;
		}
		// Sell: RSI crosses below overbought + downtrend
		else if (_prevRsi > Overbought && rsiValue <= Overbought && trendDown && Position >= 0)
		{
			if (Position > 0)
				SellMarket();

			SellMarket();
			_entryPrice = close;
			_cooldown = 10;
		}

		// Exit long on overbought
		if (Position > 0 && rsiValue > 80m)
		{
			SellMarket();
			_entryPrice = 0;
			_cooldown = 10;
		}
		// Exit short on oversold
		else if (Position < 0 && rsiValue < 20m)
		{
			BuyMarket();
			_entryPrice = 0;
			_cooldown = 10;
		}

		_prevRsi = rsiValue;
	}
}