Открыть на GitHub

Стратегия RVI Histogram Reversal

Обзор

Стратегия торгует против экстремальных значений индикатора RVI (Relative Vigor Index). Позиции открываются, когда RVI выходит из зоны перекупленности или перепроданности, либо когда линия RVI пересекает сигнальную линию. Поддерживаются два режима сигналов:

  • Levels – реагирует на пересечение верхнего или нижнего порога RVI.
  • Cross – реагирует на пересечение RVI и его сигнальной линии.

Логика контртрендовая: если RVI был выше верхнего уровня и затем опустился ниже, открывается длинная позиция. Если RVI был ниже нижнего уровня и затем поднялся выше, открывается короткая позиция.

Параметры

Название Описание
RviPeriod Период расчёта RVI.
HighLevel Верхний порог RVI.
LowLevel Нижний порог RVI.
Mode Режим сигналов (Levels или Cross).
EnableBuyOpen Разрешить открытие длинных позиций.
EnableSellOpen Разрешить открытие коротких позиций.
EnableBuyClose Разрешить закрытие длинных позиций.
EnableSellClose Разрешить закрытие коротких позиций.
CandleType Таймфрейм свечей.

Как работает

  1. На каждой завершённой свече вычисляются RVI и его скользящая средняя.
  2. В зависимости от выбранного режима проверяется:
    • выход RVI из экстремальной зоны, либо
    • пересечение RVI с сигнальной линией.
  3. При сигнале на покупку закрываются короткие позиции и открывается длинная. При сигнале на продажу закрываются длинные позиции и открывается короткая.

Таймфрейм по умолчанию — четыре часа.

Примечания

  • Используются рыночные заявки.
  • При необходимости можно дополнить стратегию управлением стоп-лоссом и тейк-профитом.
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;
	}
}