Ver en GitHub

Estrategia de Histograma RSI

Esta estrategia utiliza el histograma del Índice de Fuerza Relativa (RSI) para detectar reversiones cuando el oscilador abandona zonas extremas. El histograma colorea el valor del RSI en función de dos umbrales: un nivel alto que marca la zona de sobrecompra y un nivel bajo que marca la zona de sobreventa. Cuando el color cambia de verde (sobrecompra) a gris o rojo, la estrategia cierra posiciones cortas y abre una posición larga. Cuando el color cambia de rojo (sobreventa) a gris o verde, cierra posiciones largas y abre una posición corta.

La implementación está construida con la API de alto nivel de StockSharp y se suscribe a datos de velas de un marco temporal seleccionado. Un indicador RSI procesa las velas y genera señales siempre que su valor salga de las zonas definidas. Los parámetros opcionales permiten habilitar o deshabilitar entradas y salidas para cada lado por separado.

La estrategia tiene fines educativos y demuestra cómo convertir un asesor experto MQL al framework StockSharp.

Detalles

  • Criterios de entrada:
    • Largo: La barra anterior estaba por encima del nivel alto y la última barra cayó por debajo de él.
    • Corto: La barra anterior estaba por debajo del nivel bajo y la última barra subió por encima de él.
  • Largo/Corto: Ambos lados.
  • Criterios de salida:
    • La señal opuesta cierra la posición actual si está permitido.
  • Stops: Sin stops integrados; el framework StartProtection está preparado para añadirlos.
  • Valores predeterminados:
    • RSI period = 14
    • High level = 60
    • Low level = 40
    • Timeframe = 4 hours
  • Filtros:
    • Categoría: Reversión a la media
    • Dirección: Ambos
    • Indicadores: Único
    • Stops: Opcional
    • Complejidad: Simple
    • Marco temporal: Medio plazo
    • Estacionalidad: No
    • Redes neuronales: No
    • Divergencia: No
    • Nivel de riesgo: Moderado
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>
/// Strategy based on RSI histogram color changes.
/// Opens long when RSI leaves the overbought zone.
/// Opens short when RSI leaves the oversold zone.
/// </summary>
public class RsiHistogramStrategy : Strategy
{
	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<decimal> _highLevel;
	private readonly StrategyParam<decimal> _lowLevel;
	private readonly StrategyParam<bool> _buyPosOpen;
	private readonly StrategyParam<bool> _sellPosOpen;
	private readonly StrategyParam<bool> _buyPosClose;
	private readonly StrategyParam<bool> _sellPosClose;
	private readonly StrategyParam<DataType> _candleType;

	private int _prevClass = -1;
	private int _prevPrevClass = -1;

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

	/// <summary>
	/// Overbought threshold.
	/// </summary>
	public decimal HighLevel
	{
		get => _highLevel.Value;
		set => _highLevel.Value = value;
	}

	/// <summary>
	/// Oversold threshold.
	/// </summary>
	public decimal LowLevel
	{
		get => _lowLevel.Value;
		set => _lowLevel.Value = value;
	}

	/// <summary>
	/// Allow opening long positions.
	/// </summary>
	public bool BuyPosOpen
	{
		get => _buyPosOpen.Value;
		set => _buyPosOpen.Value = value;
	}

	/// <summary>
	/// Allow opening short positions.
	/// </summary>
	public bool SellPosOpen
	{
		get => _sellPosOpen.Value;
		set => _sellPosOpen.Value = value;
	}

	/// <summary>
	/// Allow closing long positions on opposite signal.
	/// </summary>
	public bool BuyPosClose
	{
		get => _buyPosClose.Value;
		set => _buyPosClose.Value = value;
	}

	/// <summary>
	/// Allow closing short positions on opposite signal.
	/// </summary>
	public bool SellPosClose
	{
		get => _sellPosClose.Value;
		set => _sellPosClose.Value = value;
	}

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

	/// <summary>
	/// Constructor.
	/// </summary>
	public RsiHistogramStrategy()
	{
		_rsiPeriod = Param(nameof(RsiPeriod), 14)
			.SetDisplay("RSI Period", "Length of the RSI indicator", "RSI")
			.SetGreaterThanZero()
			
			.SetOptimize(5, 30, 1);

		_highLevel = Param(nameof(HighLevel), 60m)
			.SetDisplay("High Level", "Overbought threshold", "RSI")
			
			.SetOptimize(55m, 70m, 5m);

		_lowLevel = Param(nameof(LowLevel), 40m)
			.SetDisplay("Low Level", "Oversold threshold", "RSI")
			
			.SetOptimize(30m, 45m, 5m);

		_buyPosOpen = Param(nameof(BuyPosOpen), true)
			.SetDisplay("Enable Buy Entry", "Allow long entries when signal appears", "Trading");

		_sellPosOpen = Param(nameof(SellPosOpen), true)
			.SetDisplay("Enable Sell Entry", "Allow short entries when signal appears", "Trading");

		_buyPosClose = Param(nameof(BuyPosClose), true)
			.SetDisplay("Close Buy Positions", "Allow closing longs on opposite signal", "Trading");

		_sellPosClose = Param(nameof(SellPosClose), true)
			.SetDisplay("Close Sell Positions", "Allow closing shorts on opposite signal", "Trading");

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

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevClass = -1;
		_prevPrevClass = -1;
	}

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

		var rsi = new RelativeStrengthIndex { Length = RsiPeriod };

		var subscription = SubscribeCandles(CandleType);

		subscription.Bind(rsi, (candle, rsiValue) =>
		{
			if (candle.State != CandleStates.Finished)
				return;

			var currentClass = rsiValue > HighLevel ? 0 : rsiValue < LowLevel ? 2 : 1;

			if (!IsFormedAndOnlineAndAllowTrading())
			{
				_prevPrevClass = _prevClass;
				_prevClass = currentClass;
				return;
			}

			if (_prevPrevClass == 0 && _prevClass > 0)
			{
				if (SellPosClose && Position < 0)
					BuyMarket();

				if (BuyPosOpen && Position <= 0)
					BuyMarket();
			}
			else if (_prevPrevClass == 2 && _prevClass < 2)
			{
				if (BuyPosClose && Position > 0)
					SellMarket();

				if (SellPosOpen && Position >= 0)
					SellMarket();
			}

			_prevPrevClass = _prevClass;
			_prevClass = currentClass;
		})
		.Start();

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