Ver no GitHub

Larry Connors RSI-2 Strategy

A faithful StockSharp port of the classic Larry Connors RSI-2 system. The strategy combines a fast 2-period RSI oscillator with moving-average filters on the hourly timeframe to capture short-term mean-reversion setups while staying aligned with the higher timeframe trend. Optional stop-loss and take-profit levels, expressed in pips, replicate the original MetaTrader money-management rules.

Concept Overview

  • Type: Mean reversion with trend filter.
  • Market: Designed for Forex pairs traded on the H1 chart.
  • Direction: Trades both long and short, but only in the direction of the slow SMA filter.
  • Core Indicators: 5-period SMA (exit timing), 200-period SMA (trend filter), 2-period RSI (signal trigger).

Trading Rules

Long Entries

  • RSI value drops below RSI Long Entry (default 6).
  • The closing price of the completed candle stays above the Slow SMA (default 200 periods).
  • No open position is present.

Short Entries

  • RSI value rises above RSI Short Entry (default 95).
  • The closing price is below the Slow SMA.
  • No open position is present.

Exit Conditions

  • Long positions close when the close moves above the Fast SMA (default 5). Optional stop-loss and take-profit levels measured in pips can also close the trade if enabled.
  • Short positions close when the close moves below the Fast SMA. Optional stop-loss and take-profit levels measured in pips apply symmetrically.

Risk Management

  • Use Stop Loss toggles a fixed stop distance in pips relative to the entry price.
  • Use Take Profit enables a symmetric profit target in pips.
  • Pip distances are converted to absolute prices via the instrument's PriceStep and decimal precision, mirroring the MT5 logic for 4/5-digit quotes.

Default Parameters

Parameter Default Description
Trade Volume 1 Base order volume for every entry.
Fast SMA Period 5 Exit timing average.
Slow SMA Period 200 Trend direction filter.
RSI Period 2 Lookback for the RSI oscillator.
RSI Long Entry 6 Oversold threshold for long trades.
RSI Short Entry 95 Overbought threshold for short trades.
Use Stop Loss true Enable/disable protective stop.
Stop Loss (pips) 30 Stop-loss distance in pips.
Use Take Profit true Enable/disable fixed profit target.
Take Profit (pips) 60 Profit target distance in pips.
Candle Type 1 hour Timeframe of the working candles.

All tunable parameters expose .SetCanOptimize(true) allowing batch optimization inside Designer/Tester.

Execution Notes

  • Signals are evaluated on closed candles to match the original MetaTrader implementation.
  • Protective levels are tracked internally, closing the entire position with market orders when breached.
  • The strategy resets internal state (pipSize, entry anchors) on each restart to guarantee reproducible backtests.
  • Add the strategy to a project alongside reliable Forex data to replicate the published performance results.

Suggested Use

  1. Connect a Forex data feed that supplies 1-hour candles.
  2. Add the strategy to Designer or run it programmatically through StockSharp API.
  3. Adjust pip-based risk parameters to match the broker's contract specifications if necessary.
  4. Optionally optimize RSI thresholds or moving-average lengths to adapt the model to other symbols.

By preserving the exact RSI and moving-average logic, this port allows MT5 users to evaluate the Larry Connors RSI-2 methodology within the StockSharp ecosystem.

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;
	}
}