Auf GitHub ansehen

Larry Connors RSI-2-Strategie

Ein getreuer StockSharp-Port des klassischen Larry Connors RSI-2-Systems. Die Strategie kombiniert einen schnellen 2-Perioden-RSI-Oszillator mit gleitenden Durchschnittsfiltern im Stundenzeittrahmen, um kurzfristige Mean-Reversion-Setups zu erfassen und gleichzeitig mit dem übergeordneten Trend ausgerichtet zu bleiben. Optionale Stop-Loss- und Take-Profit-Niveaus in Pips replizieren die originalen MetaTrader-Geldverwaltungsregeln.

Konzeptübersicht

  • Typ: Mean Reversion mit Trendfilter.
  • Markt: Entwickelt für Forex-Paare auf dem H1-Chart.
  • Richtung: Handelt sowohl Long als auch Short, aber nur in Richtung des langsamen SMA-Filters.
  • Kernindikatoren: 5-Perioden-SMA (Ausstiegszeitpunkt), 200-Perioden-SMA (Trendfilter), 2-Perioden-RSI (Signalauslöser).

Handelsregeln

Long-Einstiege

  • RSI-Wert fällt unter RSI Long Entry (Standard 6).
  • Der Schlusskurs der abgeschlossenen Kerze bleibt über dem Slow SMA (Standard 200 Perioden).
  • Keine offene Position vorhanden.

Short-Einstiege

  • RSI-Wert steigt über RSI Short Entry (Standard 95).
  • Der Schlusskurs liegt unter dem Slow SMA.
  • Keine offene Position vorhanden.

Ausstiegsbedingungen

  • Long-Positionen schließen, wenn der Schlusskurs über den Fast SMA (Standard 5) steigt. Optionale Stop-Loss- und Take-Profit-Niveaus in Pips können den Trade ebenfalls schließen, wenn aktiviert.
  • Short-Positionen schließen, wenn der Schlusskurs unter den Fast SMA fällt. Optionale Stop-Loss- und Take-Profit-Niveaus in Pips gelten symmetrisch.

Risikomanagement

  • Use Stop Loss schaltet eine feste Stop-Distanz in Pips relativ zum Einstandspreis um.
  • Use Take Profit aktiviert ein symmetrisches Gewinnziel in Pips.
  • Pip-Distanzen werden über den PriceStep des Instruments und die Dezimalpräzision in absolute Preise umgerechnet, entsprechend der MT5-Logik für 4/5-stellige Kurse.

Standardwerte

Parameter Standard Beschreibung
Trade Volume 1 Basis-Ordervolumen für jeden Einstieg.
Fast SMA Period 5 Ausstiegs-Timing-Durchschnitt.
Slow SMA Period 200 Trendrichtungsfilter.
RSI Period 2 Lookback für den RSI-Oszillator.
RSI Long Entry 6 Überverkauft-Schwelle für Long-Trades.
RSI Short Entry 95 Überkauft-Schwelle für Short-Trades.
Use Stop Loss true Schutzstop aktivieren/deaktivieren.
Stop Loss (pips) 30 Stop-Loss-Distanz in Pips.
Use Take Profit true Festes Gewinnziel aktivieren/deaktivieren.
Take Profit (pips) 60 Gewinnziel-Distanz in Pips.
Candle Type 1 Stunde Zeitrahmen der Arbeitskerzen.

Alle einstellbaren Parameter stellen .SetCanOptimize(true) bereit und ermöglichen die Batch-Optimierung in Designer/Tester.

Ausführungshinweise

  • Signale werden auf geschlossenen Kerzen ausgewertet, um der ursprünglichen MetaTrader-Implementierung zu entsprechen.
  • Schutzlevel werden intern verfolgt und schließen die gesamte Position mit Marktorders, wenn sie verletzt werden.
  • Die Strategie setzt den internen Zustand (pipSize, Einstiegsanker) bei jedem Neustart zurück, um reproduzierbare Backtests zu gewährleisten.
  • Fügen Sie die Strategie einem Projekt zusammen mit zuverlässigen Forex-Daten hinzu, um die veröffentlichten Performance-Ergebnisse zu replizieren.

Empfohlene Verwendung

  1. Verbinden Sie einen Forex-Datenfeed, der 1-Stunden-Kerzen liefert.
  2. Fügen Sie die Strategie zu Designer hinzu oder führen Sie sie programmgesteuert über die StockSharp API aus.
  3. Passen Sie pip-basierte Risikoparameter an die Kontraktspezifikationen des Brokers an, falls erforderlich.
  4. Optimieren Sie optional RSI-Schwellenwerte oder gleitende Durchschnittslängen, um das Modell an andere Symbole anzupassen.

Durch die Beibehaltung der exakten RSI- und gleitenden Durchschnittslogik ermöglicht dieser Port MT5-Nutzern, die Larry Connors RSI-2-Methodik innerhalb des StockSharp-Ökosystems zu evaluieren.

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>
/// Larry Connors RSI-2 strategy with a 200-period SMA filter and optional stop management.
/// </summary>
public class LarryConnersRsi2Strategy : Strategy
{
	private readonly StrategyParam<decimal> _tradeVolume;
	private readonly StrategyParam<int> _fastSmaPeriod;
	private readonly StrategyParam<int> _slowSmaPeriod;
	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<decimal> _rsiLongEntry;
	private readonly StrategyParam<decimal> _rsiShortEntry;
	private readonly StrategyParam<bool> _useStopLoss;
	private readonly StrategyParam<decimal> _stopLossPips;
	private readonly StrategyParam<bool> _useTakeProfit;
	private readonly StrategyParam<decimal> _takeProfitPips;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _pipSize;
	private decimal? _longEntryPrice;
	private decimal? _shortEntryPrice;

	/// <summary>
	/// Order volume for entries.
	/// </summary>
	public decimal TradeVolume
	{
		get => _tradeVolume.Value;
		set
		{
			_tradeVolume.Value = value;
			Volume = value;
		}
	}

	/// <summary>
	/// Fast SMA period used for timing exits.
	/// </summary>
	public int FastSmaPeriod
	{
		get => _fastSmaPeriod.Value;
		set => _fastSmaPeriod.Value = value;
	}

	/// <summary>
	/// Slow SMA period used as a trend filter.
	/// </summary>
	public int SlowSmaPeriod
	{
		get => _slowSmaPeriod.Value;
		set => _slowSmaPeriod.Value = value;
	}

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

	/// <summary>
	/// RSI threshold for long entries.
	/// </summary>
	public decimal RsiLongEntry
	{
		get => _rsiLongEntry.Value;
		set => _rsiLongEntry.Value = value;
	}

	/// <summary>
	/// RSI threshold for short entries.
	/// </summary>
	public decimal RsiShortEntry
	{
		get => _rsiShortEntry.Value;
		set => _rsiShortEntry.Value = value;
	}

	/// <summary>
	/// Enables stop-loss handling in price pips.
	/// </summary>
	public bool UseStopLoss
	{
		get => _useStopLoss.Value;
		set => _useStopLoss.Value = value;
	}

	/// <summary>
	/// Stop-loss size expressed in pips.
	/// </summary>
	public decimal StopLossPips
	{
		get => _stopLossPips.Value;
		set => _stopLossPips.Value = value;
	}

	/// <summary>
	/// Enables take-profit handling in price pips.
	/// </summary>
	public bool UseTakeProfit
	{
		get => _useTakeProfit.Value;
		set => _useTakeProfit.Value = value;
	}

	/// <summary>
	/// Take-profit size expressed in pips.
	/// </summary>
	public decimal TakeProfitPips
	{
		get => _takeProfitPips.Value;
		set => _takeProfitPips.Value = value;
	}

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

	/// <summary>
	/// Initializes <see cref="LarryConnersRsi2Strategy"/>.
	/// </summary>
	public LarryConnersRsi2Strategy()
	{
		_tradeVolume = Param(nameof(TradeVolume), 1m)
			.SetGreaterThanZero()
			.SetDisplay("Trade Volume", "Order volume", "Trading")
			;

		_fastSmaPeriod = Param(nameof(FastSmaPeriod), 5)
			.SetGreaterThanZero()
			.SetDisplay("Fast SMA Period", "Fast SMA length", "Indicators")
			;

		_slowSmaPeriod = Param(nameof(SlowSmaPeriod), 50)
			.SetGreaterThanZero()
			.SetDisplay("Slow SMA Period", "Slow SMA length", "Indicators")
			;

		_rsiPeriod = Param(nameof(RsiPeriod), 2)
			.SetGreaterThanZero()
			.SetDisplay("RSI Period", "RSI lookback", "Indicators")
			;

		_rsiLongEntry = Param(nameof(RsiLongEntry), 6m)
			.SetDisplay("RSI Long Entry", "RSI threshold for longs", "Signals")
			;

		_rsiShortEntry = Param(nameof(RsiShortEntry), 95m)
			.SetDisplay("RSI Short Entry", "RSI threshold for shorts", "Signals")
			;

		_useStopLoss = Param(nameof(UseStopLoss), true)
			.SetDisplay("Use Stop Loss", "Enable stop-loss management", "Risk");

		_stopLossPips = Param(nameof(StopLossPips), 30m)
			.SetGreaterThanZero()
			.SetDisplay("Stop Loss (pips)", "Stop-loss distance in pips", "Risk")
			;

		_useTakeProfit = Param(nameof(UseTakeProfit), true)
			.SetDisplay("Use Take Profit", "Enable take-profit management", "Risk");

		_takeProfitPips = Param(nameof(TakeProfitPips), 60m)
			.SetGreaterThanZero()
			.SetDisplay("Take Profit (pips)", "Take-profit distance in pips", "Risk")
			;

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

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

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

		// Clear internal state between runs.
		_pipSize = 0m;
		_longEntryPrice = null;
		_shortEntryPrice = null;
	}

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

		// Use configured trade volume for default order size.
		Volume = TradeVolume;

		// Pre-compute pip size multiplier for risk management calculations.
		var priceStep = Security?.PriceStep ?? 1m;
		var decimals = Security?.Decimals ?? 0;
		var pipMultiplier = decimals is 1 or 3 or 5 ? 10m : 1m;
		_pipSize = priceStep * pipMultiplier;
		if (_pipSize <= 0m)
			_pipSize = priceStep;

		// Prepare technical indicators.
		var fastSma = new SMA { Length = FastSmaPeriod };
		var slowSma = new SMA { Length = SlowSmaPeriod };
		var rsi = new RelativeStrengthIndex { Length = RsiPeriod };

		// Subscribe to candles and bind indicators for combined processing.
		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(fastSma, slowSma, rsi, ProcessCandle)
			.Start();

		// Build optional chart visuals to monitor the strategy.
		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, fastSma);
			DrawIndicator(area, slowSma);
			DrawIndicator(area, rsi);
			DrawOwnTrades(area);
		}
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastSma, decimal slowSma, decimal rsi)
	{
		// Act only on fully formed candles to mimic MQL bar-close execution.
		if (candle.State != CandleStates.Finished)
			return;

		// Manage open long position exits before generating new signals.
		if (Position > 0)
		{
			if (UseStopLoss && _longEntryPrice.HasValue)
			{
				var stopPrice = _longEntryPrice.Value - StopLossPips * _pipSize;
				if (candle.LowPrice <= stopPrice)
				{
					SellMarket();
					ResetLongState();
					return;
				}
			}

			if (UseTakeProfit && _longEntryPrice.HasValue)
			{
				var targetPrice = _longEntryPrice.Value + TakeProfitPips * _pipSize;
				if (candle.HighPrice >= targetPrice)
				{
					SellMarket();
					ResetLongState();
					return;
				}
			}

			if (candle.ClosePrice > fastSma)
			{
				SellMarket();
				ResetLongState();
				return;
			}
		}
		else if (Position < 0)
		{
			if (UseStopLoss && _shortEntryPrice.HasValue)
			{
				var stopPrice = _shortEntryPrice.Value + StopLossPips * _pipSize;
				if (candle.HighPrice >= stopPrice)
				{
					BuyMarket();
					ResetShortState();
					return;
				}
			}

			if (UseTakeProfit && _shortEntryPrice.HasValue)
			{
				var targetPrice = _shortEntryPrice.Value - TakeProfitPips * _pipSize;
				if (candle.LowPrice <= targetPrice)
				{
					BuyMarket();
					ResetShortState();
					return;
				}
			}

			if (candle.ClosePrice < fastSma)
			{
				BuyMarket();
				ResetShortState();
				return;
			}
		}

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		// Generate new entries only when flat to match the MQL logic.
		if (Position == 0)
		{
			var canGoLong = rsi < RsiLongEntry && candle.ClosePrice > slowSma;
			if (canGoLong)
			{
				BuyMarket();
				_longEntryPrice = candle.ClosePrice;
				_shortEntryPrice = null;
				return;
			}

			var canGoShort = rsi > RsiShortEntry && candle.ClosePrice < slowSma;
			if (canGoShort)
			{
				SellMarket();
				_shortEntryPrice = candle.ClosePrice;
				_longEntryPrice = null;
			}
		}
	}

	private void ResetLongState()
	{
		// Drop long tracking data after an exit.
		_longEntryPrice = null;
	}

	private void ResetShortState()
	{
		// Drop short tracking data after an exit.
		_shortEntryPrice = null;
	}
}