Стратегия использует Relative Vigor Index (RVI) на трёх различных таймфреймах. Два старших таймфрейма выступают фильтрами тренда, а самый младший таймфрейм генерирует точки входа. Длинная позиция открывается, когда на младшем таймфрейме RVI пересекает свою сигнальную линию сверху вниз и оба старших таймфрейма остаются бычьими. Короткая позиция открывается, когда на младшем таймфрейме RVI пересекает сигнальную линию снизу вверх при медвежьем тренде на старших таймфреймах. Позиции закрываются при появлении противоположного сигнала на любом из таймфреймов.
Параметры
RviPeriod – период расчёта RVI.
CandleType1 – таймфрейм старшего фильтра.
CandleType2 – таймфрейм среднего фильтра.
CandleType3 – рабочий таймфрейм, на котором генерируются сигналы.
Volume – объём заявок при входе по рынку.
Примечания
Обрабатываются только завершённые свечи.
Стратегия реализована с использованием высокоуровневого API StockSharp.
Таймфреймы по умолчанию соответствуют 30, 15 и 5 минутам.
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 that trades using Relative Vigor Index with multiple period confirmations.
/// Uses a long-period RVI for trend, mid-period for confirmation, and short for entry.
/// </summary>
public class TripleRviStrategy : Strategy
{
private readonly StrategyParam<int> _rviPeriod;
private readonly StrategyParam<DataType> _candleType;
private int _trend1;
private int _trend2;
private decimal _prevSignal = decimal.MinValue;
public int RviPeriod { get => _rviPeriod.Value; set => _rviPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public TripleRviStrategy()
{
_rviPeriod = Param(nameof(RviPeriod), 10)
.SetGreaterThanZero()
.SetDisplay("RVI Period", "Base period of RVI", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Trading timeframe", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_trend1 = 0;
_trend2 = 0;
_prevSignal = decimal.MinValue;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_trend1 = 0;
_trend2 = 0;
_prevSignal = decimal.MinValue;
var trendRsi = new RelativeStrengthIndex { Length = RviPeriod * 3 };
var midRsi = new RelativeStrengthIndex { Length = RviPeriod * 2 };
var signalRsi = new RelativeStrengthIndex { Length = RviPeriod };
var sub = SubscribeCandles(CandleType);
sub.Bind(trendRsi, midRsi, signalRsi, ProcessCandle).Start();
StartProtection(
new Unit(2000m, UnitTypes.Absolute),
new Unit(1000m, UnitTypes.Absolute));
}
private void ProcessCandle(ICandleMessage candle, decimal trendValue, decimal midValue, decimal signalValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
_trend1 = trendValue > 55m ? 1 : trendValue < 45m ? -1 : 0;
_trend2 = midValue > 55m ? 1 : midValue < 45m ? -1 : 0;
if (_prevSignal == decimal.MinValue)
{
_prevSignal = signalValue;
return;
}
var crossUp = _prevSignal <= 50m && signalValue > 50m;
var crossDown = _prevSignal >= 50m && signalValue < 50m;
if (crossUp && _trend1 > 0 && _trend2 > 0 && Position <= 0)
BuyMarket();
else if (crossDown && _trend1 < 0 && _trend2 < 0 && Position >= 0)
SellMarket();
if (Position > 0 && (_trend1 < 0 || _trend2 < 0))
SellMarket();
else if (Position < 0 && (_trend1 > 0 || _trend2 > 0))
BuyMarket();
_prevSignal = signalValue;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes
from StockSharp.Algo.Indicators import RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
class triple_rvi_strategy(Strategy):
def __init__(self):
super(triple_rvi_strategy, self).__init__()
self._rvi_period = self.Param("RviPeriod", 10)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1)))
self._trend1 = 0
self._trend2 = 0
self._prev_signal = None
@property
def RviPeriod(self): return self._rvi_period.Value
@RviPeriod.setter
def RviPeriod(self, v): self._rvi_period.Value = v
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, v): self._candle_type.Value = v
def OnStarted2(self, time):
super(triple_rvi_strategy, self).OnStarted2(time)
self._trend1 = 0
self._trend2 = 0
self._prev_signal = None
trend_rsi = RelativeStrengthIndex()
trend_rsi.Length = self.RviPeriod * 3
mid_rsi = RelativeStrengthIndex()
mid_rsi.Length = self.RviPeriod * 2
signal_rsi = RelativeStrengthIndex()
signal_rsi.Length = self.RviPeriod
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(trend_rsi, mid_rsi, signal_rsi, self.ProcessCandle).Start()
self.StartProtection(Unit(2000, UnitTypes.Absolute), Unit(1000, UnitTypes.Absolute))
def ProcessCandle(self, candle, trend_value, mid_value, signal_value):
if candle.State != CandleStates.Finished: return
if not self.IsFormedAndOnlineAndAllowTrading(): return
tv = float(trend_value)
mv = float(mid_value)
sv = float(signal_value)
self._trend1 = 1 if tv > 55.0 else (-1 if tv < 45.0 else 0)
self._trend2 = 1 if mv > 55.0 else (-1 if mv < 45.0 else 0)
if self._prev_signal is None:
self._prev_signal = sv
return
cross_up = self._prev_signal <= 50.0 and sv > 50.0
cross_down = self._prev_signal >= 50.0 and sv < 50.0
if cross_up and self._trend1 > 0 and self._trend2 > 0 and self.Position <= 0:
self.BuyMarket()
elif cross_down and self._trend1 < 0 and self._trend2 < 0 and self.Position >= 0:
self.SellMarket()
if self.Position > 0 and (self._trend1 < 0 or self._trend2 < 0):
self.SellMarket()
elif self.Position < 0 and (self._trend1 > 0 or self._trend2 > 0):
self.BuyMarket()
self._prev_signal = sv
def OnReseted(self):
super(triple_rvi_strategy, self).OnReseted()
self._trend1 = 0
self._trend2 = 0
self._prev_signal = None
def CreateClone(self):
return triple_rvi_strategy()