Auf GitHub ansehen

RSI Benachrichtigungsstrategie

Überblick

Die RSI Alert Strategy reproduziert das Verhalten des Expertenberaters MetaTrader 5 „RSI Alert“ innerhalb des StockSharp-Frameworks. Der ursprüngliche Bot achtete auf Messwerte des Relative Strength Index (RSI), die stark überverkaufte (≤20) oder überkaufte (≥80) Werte kreuzten, und sendete beim Öffnen von Marktpositionen sofort Warnmeldungen. Die konvertierte Version behält diese ereignisgesteuerte Philosophie bei: Sie wartet auf abgeschlossene Kerzen, wertet den RSI aus und dreht die Position automatisch um, indem sie Marktaufträge sendet, wenn die konfigurierten Schwellenwerte erreicht werden.

Handelslogik

  1. Abonnieren Sie die konfigurierte Kerzenserie (Standard: 1-Minuten-Zeitrahmen) und geben Sie die Schlusskurse in einen RelativeStrengthIndex-Indikator ein.
  2. Ignorieren Sie unvollständige Kerzen und warten Sie, bis der Indikator RSI vollständig gebildet ist. Dies spiegelt den MQL-Experten wider, der die Bedingungen nur einmal pro neuem Balken bewertete.
  3. Handelssignale generieren:
    • Kaufsignal – RSI ≤ OversoldLevel. Die Strategie schließt jegliche Short-Position und eröffnet eine Long-Position mit dem konfigurierten Volumen.
    • Verkaufssignal – RSI ≥ OverboughtLevel. Die Strategie schließt jegliche Long-Position und eröffnet eine Short-Position mit dem konfigurierten Volumen.
  4. Aufträge werden immer mit BuyMarket/SellMarket aufgegeben, es gibt also keine ausstehenden Aufträge, Stop-Loss- oder Take-Profit-Werte. Die MetaTrader-Implementierung erlaubte optionale SL/TP-Eingaben, war jedoch standardmäßig auf manuelle Verwaltung angewiesen. Der StockSharp-Port konzentriert sich auf die Konvertierung von Alert-to-Trade und überlässt das Risikomanagement externen Modulen (z. B. StartProtection() oder Kontrollen auf Portfolioebene).

Die Strategie bleibt zwischen den Signalen flach. Wenn ein entgegengesetzter Auslöser erscheint, kehrt er die Position um, indem er genug Volumen hinzufügt, um die bestehende Exposition abzuflachen, bevor er in die neue Richtung übergeht, genau wie es der ursprüngliche EA tat, als er aufeinanderfolgende Alarme auslöste.

Parameter

Parameter Standard Beschreibung
OrderVolume 0,01 Handelsgröße für Marktaufträge. Beim Umkehren fügt die Strategie den erforderlichen Betrag hinzu, um die bestehende Position abzudecken, bevor sie erneut einsteigt.
RsiPeriod 30 RSI Mittelungszeitraum. Muss eine positive ganze Zahl sein.
OverboughtLevel 80 RSI Schwellenwert, der ein Verkaufssignal ausgibt. Kann optimiert werden, um die Aggressivität abzustimmen.
OversoldLevel 20 RSI Schwellenwert, der ein Kaufsignal ausgibt.
CandleType 1 Minute TimeFrameCandle Kerzendatenquelle, die für die RSI-Berechnung verwendet wird. Ändern Sie es, um höhere Zeitrahmen zu analysieren.

Alle Parameter werden über StrategyParam<T> verfügbar gemacht, sodass sie im StockSharp-Designer angezeigt werden, in XML-Voreinstellungen gespeichert werden können und Optimierungsszenarien unterstützen.

Implementierungshinweise

  • Das übergeordnete StockSharp API wird durchgehend verwendet: Kerzen werden über SubscribeCandles() abgerufen, und RSI wird über subscription.Bind(indicator, callback) aktualisiert. Es ist keine manuelle Pufferverwaltung oder historisches Kopieren erforderlich.
  • Die Basiseigenschaft Strategy.Volume wird mit dem Parameter OrderVolume synchronisiert, sodass die Positionsumkehr auch dann korrekt funktioniert, wenn der Benutzer die Losgröße zur Laufzeit ändert.
  • Inline-Kommentare und XML-Dokumentation werden entsprechend den Projektanforderungen in englischer Sprache verfasst.
  • Die Diagrammausgabe ist optional, wird aber unterstützt: Wenn die Strategie im Designer ausgeführt wird, werden die Preiskerzen, ausgeführten Trades und die Indikatorwerte RSI dargestellt.

Nutzungstipps

  • Kombinieren Sie die Strategie mit externen Stop-Loss-/Take-Profit-Modulen, wenn eine automatisierte Risikokontrolle erforderlich ist.
  • Optimieren Sie die RSI-Schwellenwerte bei der Anpassung an Märkte mit unterschiedlichen Volatilitätsregimen.
  • Erhöhen Sie den Kerzenzeitrahmen für Swing-Setups oder behalten Sie die standardmäßige 1-Minuten-Serie für Warnungen im Scalping-Stil bei, wie im Originalskript.
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;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// RSI alert strategy converted from the original MetaTrader expert advisor.
/// Buys when RSI drops below the oversold threshold and sells when RSI rises above the overbought threshold.
/// </summary>
public class RsiAlertStrategy : Strategy
{
	private readonly StrategyParam<decimal> _orderVolume;
	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<decimal> _overboughtLevel;
	private readonly StrategyParam<decimal> _oversoldLevel;
	private readonly StrategyParam<DataType> _candleType;

	private RelativeStrengthIndex _rsi;

	/// <summary>
	/// Order volume used for market trades.
	/// </summary>
	public decimal OrderVolume
	{
		get => _orderVolume.Value;
		set
		{
			_orderVolume.Value = value;
			Volume = value; // Keep the base strategy volume aligned with the parameter value.
		}
	}

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

	/// <summary>
	/// RSI level that triggers short trades.
	/// </summary>
	public decimal OverboughtLevel
	{
		get => _overboughtLevel.Value;
		set => _overboughtLevel.Value = value;
	}

	/// <summary>
	/// RSI level that triggers long trades.
	/// </summary>
	public decimal OversoldLevel
	{
		get => _oversoldLevel.Value;
		set => _oversoldLevel.Value = value;
	}

	/// <summary>
	/// Candle type supplying prices to the RSI indicator.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Initializes a new instance of <see cref="RsiAlertStrategy"/> with default parameters.
	/// </summary>
	public RsiAlertStrategy()
	{
		_orderVolume = Param(nameof(OrderVolume), 0.01m)
			.SetGreaterThanZero()
			.SetDisplay("Order Volume", "Order size used for market trades", "Trading")
			;

		_rsiPeriod = Param(nameof(RsiPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("RSI Period", "Number of bars for RSI calculation", "Indicator")
			;

		_overboughtLevel = Param(nameof(OverboughtLevel), 70m)
			.SetDisplay("Overbought Level", "RSI threshold that triggers short signals", "Indicator")
			;

		_oversoldLevel = Param(nameof(OversoldLevel), 30m)
			.SetDisplay("Oversold Level", "RSI threshold that triggers long signals", "Indicator")
			;

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Type of candles that feed the RSI", "General");

		Volume = OrderVolume;
	}

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

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

		_rsi = null;
	}

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

		Volume = OrderVolume; // Ensure the strategy uses the configured volume on each start.

		_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 rsiValue)
	{
		if (candle.State != CandleStates.Finished)
			return;

		var buySignal = rsiValue <= OversoldLevel;
		var sellSignal = rsiValue >= OverboughtLevel;

		if (buySignal && Position == 0)
		{
			BuyMarket();
		}
		else if (sellSignal && Position == 0)
		{
			SellMarket();
		}
	}
}