Ver en GitHub

Estrategia Rsi Stochastic

Estrategia que combina el RSI y el Oscilador Stochastic para doble confirmación de condiciones de sobreventa y sobrecompra.

Las pruebas indican un retorno anual promedio de aproximadamente 181%. Funciona mejor en el mercado de criptomonedas.

El RSI proporciona una visión más amplia del momentum, mientras que el Stochastic da señales más rápidas cerca de los extremos. Las operaciones cambian cuando el oscilador cruza niveles dentro del contexto del RSI.

Ideal para traders ágiles que prefieren configuraciones de osciladores. La estrategia se apoya en un stop de ATR para contener el riesgo.

Detalles

  • Criterios de entrada:
    • Largo: RSI < RsiOversold && StochK < StochOversold
    • Corto: RSI > RsiOverbought && StochK > StochOverbought
  • Largo/Corto: Ambos
  • Criterios de salida:
    • Largo: RSI > 50
    • Corto: RSI < 50
  • Stops: Basado en porcentaje en StopLossPercent
  • Valores predeterminados:
    • RsiPeriod = 14
    • RsiOversold = 30m
    • RsiOverbought = 70m
    • StochPeriod = 14
    • StochK = 3
    • StochD = 3
    • StochOversold = 20m
    • StochOverbought = 80m
    • StopLossPercent = 2.0m
    • CandleType = TimeSpan.FromMinutes(5).TimeFrame()
  • Filtros:
    • Categoría: Reversión a la media
    • Dirección: Ambos
    • Indicadores: RSI, Stochastic Oscillator
    • Stops: Sí
    • Complejidad: Intermedio
    • Marco temporal: Medio plazo
    • Estacionalidad: No
    • Redes neuronales: No
    • Divergencia: No
    • Nivel de riesgo: Medio
using System;
using System.Collections.Generic;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Strategy combining RSI with EMA trend filter for oversold/overbought trading.
/// </summary>
public class RsiStochasticStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<decimal> _rsiOversold;
	private readonly StrategyParam<decimal> _rsiOverbought;
	private readonly StrategyParam<int> _emaPeriod;
	private readonly StrategyParam<int> _cooldownBars;

	private decimal _emaValue;
	private int _cooldown;

	/// <summary>
	/// Candle type for strategy calculation.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

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

	/// <summary>
	/// RSI oversold level.
	/// </summary>
	public decimal RsiOversold
	{
		get => _rsiOversold.Value;
		set => _rsiOversold.Value = value;
	}

	/// <summary>
	/// RSI overbought level.
	/// </summary>
	public decimal RsiOverbought
	{
		get => _rsiOverbought.Value;
		set => _rsiOverbought.Value = value;
	}

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

	/// <summary>
	/// Cooldown bars between trades.
	/// </summary>
	public int CooldownBars
	{
		get => _cooldownBars.Value;
		set => _cooldownBars.Value = value;
	}

	/// <summary>
	/// Strategy constructor.
	/// </summary>
	public RsiStochasticStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Type of candles to use", "General");

		_rsiPeriod = Param(nameof(RsiPeriod), 14)
			.SetRange(7, 21)
			.SetDisplay("RSI Period", "Period of the RSI indicator", "Indicators");

		_rsiOversold = Param(nameof(RsiOversold), 30m)
			.SetDisplay("RSI Oversold", "RSI oversold level", "Indicators");

		_rsiOverbought = Param(nameof(RsiOverbought), 70m)
			.SetDisplay("RSI Overbought", "RSI overbought level", "Indicators");

		_emaPeriod = Param(nameof(EmaPeriod), 20)
			.SetRange(10, 50)
			.SetDisplay("EMA Period", "EMA period for trend filter", "Indicators");

		_cooldownBars = Param(nameof(CooldownBars), 100)
			.SetDisplay("Cooldown Bars", "Bars between trades", "General")
			.SetRange(5, 500);
	}

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_emaValue = 0;
		_cooldown = 0;
	}

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

		var ema = new ExponentialMovingAverage { Length = EmaPeriod };
		var rsi = new RelativeStrengthIndex { Length = RsiPeriod };

		var subscription = SubscribeCandles(CandleType);

		// Bind EMA to capture value
		subscription.Bind(ema, OnEma);

		// Bind RSI for main logic
		subscription
			.Bind(rsi, ProcessCandle)
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, ema);
			DrawOwnTrades(area);

			var rsiArea = CreateChartArea();
			if (rsiArea != null)
				DrawIndicator(rsiArea, rsi);
		}
	}

	private void OnEma(ICandleMessage candle, decimal ema)
	{
		_emaValue = ema;
	}

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

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		if (_emaValue == 0)
			return;

		var close = candle.ClosePrice;

		if (_cooldown > 0)
		{
			_cooldown--;
			return;
		}

		// Long: RSI oversold
		if (rsiValue < RsiOversold && Position == 0)
		{
			BuyMarket();
			_cooldown = CooldownBars;
		}
		// Short: RSI overbought
		else if (rsiValue > RsiOverbought && Position == 0)
		{
			SellMarket();
			_cooldown = CooldownBars;
		}

		// Exit long: RSI returns to neutral
		if (Position > 0 && rsiValue > 50)
		{
			SellMarket();
			_cooldown = CooldownBars;
		}
		// Exit short: RSI returns to neutral
		else if (Position < 0 && rsiValue < 50)
		{
			BuyMarket();
			_cooldown = CooldownBars;
		}
	}
}