Ver en GitHub

Estrategia de Reversión del Histograma RVI

Descripción general

Esta estrategia opera contra valores extremos del RVI. Opera con el Índice de Vigor Relativo (RVI) y abre posiciones cuando el indicador abandona zonas de sobrecompra o sobreventa, o cuando el RVI cruza su línea de señal. Se admiten dos modos de señal:

  • Levels – reacciona cuando el RVI cruza umbrales superiores o inferiores predefinidos.
  • Cross – reacciona cuando el RVI cruza su línea de señal.

La lógica es contraria a la tendencia: si el RVI estaba por encima del nivel alto y luego cae, se abre una posición larga. Si el RVI estaba por debajo del nivel bajo y luego sube, se abre una posición corta.

Parámetros

Nombre Descripción
RviPeriod Período de cálculo del RVI.
HighLevel Umbral superior del RVI.
LowLevel Umbral inferior del RVI.
Mode Modo de generación de señales (Levels o Cross).
EnableBuyOpen Permitir abrir posiciones largas.
EnableSellOpen Permitir abrir posiciones cortas.
EnableBuyClose Permitir cerrar posiciones largas.
EnableSellClose Permitir cerrar posiciones cortas.
CandleType Marco temporal de las velas.

Cómo funciona

  1. El RVI y su media móvil simple se calculan en cada vela completada.
  2. Dependiendo del modo seleccionado, la estrategia verifica:
    • si el RVI abandona un nivel extremo, o
    • si el RVI cruza su línea de señal.
  3. Con una señal larga, la estrategia cierra posiciones cortas y abre una posición larga. Con una señal corta, cierra posiciones largas y abre una posición corta.

El marco temporal por defecto es de cuatro horas.

Notas

  • Las órdenes se ejecutan con órdenes de mercado.
  • La gestión de stop-loss y take-profit puede añadirse por separado si es necesario.
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>
/// RVI histogram reversal strategy.
/// Opens long when RVI Average crosses above Signal.
/// Opens short when RVI Average crosses below Signal.
/// </summary>
public class RviHistogramReversalStrategy : Strategy
{
	private readonly StrategyParam<int> _rviPeriod;
	private readonly StrategyParam<DataType> _candleType;

	private decimal? _prevAvg;
	private decimal? _prevSig;

	public int RviPeriod { get => _rviPeriod.Value; set => _rviPeriod.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public RviHistogramReversalStrategy()
	{
		_rviPeriod = Param(nameof(RviPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("RVI Period", "Period of RVI indicator", "General");

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

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
		=> [(Security, CandleType)];

	protected override void OnReseted()
	{
		base.OnReseted();
		_prevAvg = null;
		_prevSig = null;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		var rvi = new RelativeVigorIndex();
		rvi.Average.Length = RviPeriod;
		rvi.Signal.Length = RviPeriod;

		var subscription = SubscribeCandles(CandleType);
		subscription
			.BindEx(rvi, ProcessCandle)
			.Start();

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

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

		var rviVal = value as IRelativeVigorIndexValue;
		if (rviVal?.Average is not decimal avg || rviVal?.Signal is not decimal sig)
			return;

		if (!IsFormedAndOnlineAndAllowTrading())
		{
			_prevAvg = avg;
			_prevSig = sig;
			return;
		}

		if (_prevAvg is decimal pa && _prevSig is decimal ps)
		{
			// Average crosses above Signal - buy
			if (pa <= ps && avg > sig && Position <= 0)
				BuyMarket();
			// Average crosses below Signal - sell
			else if (pa >= ps && avg < sig && Position >= 0)
				SellMarket();
		}

		_prevAvg = avg;
		_prevSig = sig;
	}
}