Ver en GitHub

Estrategia Larry Connors RSI-2

Un port fiel al sistema clásico Larry Connors RSI-2 para StockSharp. La estrategia combina un oscilador RSI rápido de 2 períodos con filtros de media móvil en el marco temporal horario para capturar configuraciones de reversión a la media a corto plazo mientras se mantiene alineada con la tendencia del marco temporal superior. Los niveles opcionales de stop-loss y take-profit, expresados en pips, replican las reglas originales de gestión monetaria de MetaTrader.

Descripción General

  • Tipo: Reversión a la media con filtro de tendencia.
  • Mercado: Diseñada para pares Forex en el gráfico H1.
  • Dirección: Opera tanto largo como corto, pero solo en la dirección del filtro de SMA lenta.
  • Indicadores principales: SMA de 5 períodos (momento de salida), SMA de 200 períodos (filtro de tendencia), RSI de 2 períodos (disparador de señal).

Reglas de Trading

Entradas Largo

  • El valor RSI cae por debajo de RSI Long Entry (por defecto 6).
  • El precio de cierre de la vela completada se mantiene por encima de Slow SMA (por defecto 200 períodos).
  • No hay posición abierta.

Entradas Corto

  • El valor RSI sube por encima de RSI Short Entry (por defecto 95).
  • El precio de cierre está por debajo de Slow SMA.
  • No hay posición abierta.

Condiciones de Salida

  • Posiciones largo se cierran cuando el cierre supera la Fast SMA (por defecto 5). Los niveles opcionales de stop-loss y take-profit medidos en pips también pueden cerrar la operación si están activados.
  • Posiciones corto se cierran cuando el cierre cae por debajo de la Fast SMA. Los niveles opcionales de stop-loss y take-profit en pips se aplican simétricamente.

Gestión de Riesgo

  • Use Stop Loss activa una distancia fija de stop en pips relativa al precio de entrada.
  • Use Take Profit habilita un objetivo de beneficio simétrico en pips.
  • Las distancias en pips se convierten a precios absolutos mediante el PriceStep del instrumento y la precisión decimal, replicando la lógica MT5 para cotizaciones de 4/5 dígitos.

Valores Predeterminados

Parámetro Por defecto Descripción
Trade Volume 1 Volumen de orden base para cada entrada.
Fast SMA Period 5 Media de temporización de salida.
Slow SMA Period 200 Filtro de dirección de tendencia.
RSI Period 2 Período de observación del oscilador RSI.
RSI Long Entry 6 Umbral de sobreventa para operaciones largo.
RSI Short Entry 95 Umbral de sobrecompra para operaciones corto.
Use Stop Loss true Activar/desactivar stop protector.
Stop Loss (pips) 30 Distancia del stop-loss en pips.
Use Take Profit true Activar/desactivar objetivo fijo de beneficio.
Take Profit (pips) 60 Distancia del objetivo de beneficio en pips.
Candle Type 1 hora Marco temporal de las velas de trabajo.

Todos los parámetros ajustables exponen .SetCanOptimize(true) permitiendo la optimización por lotes dentro de Designer/Tester.

Notas de Ejecución

  • Las señales se evalúan en velas cerradas para coincidir con la implementación original de MetaTrader.
  • Los niveles protectores se rastrean internamente, cerrando toda la posición con órdenes de mercado cuando se superan.
  • La estrategia restablece el estado interno (pipSize, anclas de entrada) en cada reinicio para garantizar backtests reproducibles.
  • Añada la estrategia a un proyecto junto con datos Forex fiables para replicar los resultados de rendimiento publicados.

Uso Sugerido

  1. Conecte un feed de datos Forex que suministre velas de 1 hora.
  2. Añada la estrategia a Designer o ejecútela programáticamente a través de StockSharp API.
  3. Ajuste los parámetros de riesgo basados en pips para que coincidan con las especificaciones del contrato del broker si es necesario.
  4. Opcionalmente optimice los umbrales RSI o las longitudes de medias móviles para adaptar el modelo a otros símbolos.

Al preservar la lógica exacta de RSI y medias móviles, este port permite a los usuarios de MT5 evaluar la metodología Larry Connors RSI-2 dentro del ecosistema StockSharp.

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