Стратегия Larry Conners Vix Reversal II
Стратегия торгует на основе RSI индекса VIX. Длинная позиция открывается, когда RSI VIX пересекает уровень перекупленности снизу вверх. Короткая позиция открывается, когда RSI пересекает уровень перепроданности сверху вниз. Позиции закрываются после удержания не менее заданного числа дней.
Подробности
- Условия входа:
- Long: RSI(VIX) пересекает уровень
Overbought levelснизу вверх. - Short: RSI(VIX) пересекает уровень
Oversold levelсверху вниз.
- Long: RSI(VIX) пересекает уровень
- Направление: Обе стороны.
- Условия выхода: Закрытие позиции после
Min holding daysдоMax holding days. - Стопы: Нет.
- Значения по умолчанию:
RSI period= 25Overbought level= 61Oversold level= 42Min holding days= 7Max holding days= 12
- Фильтры:
- Категория: Средняя обратка
- Направление: Обе
- Индикаторы: RSI
- Стопы: Нет
- Сложность: Низкая
- Таймфрейм: Дневной
- Сезонность: Нет
- Нейросети: Нет
- Дивергенция: Нет
- Уровень риска: Средний
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>
/// Larry Conners VIX Reversal II strategy based on VIX RSI.
/// </summary>
public class LarryConnersVixReversalIIStrategy : Strategy
{
private readonly StrategyParam<Security> _vix;
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<int> _overbought;
private readonly StrategyParam<int> _oversold;
private readonly StrategyParam<int> _minHolding;
private readonly StrategyParam<int> _maxHolding;
private readonly StrategyParam<int> _maxEntries;
private readonly StrategyParam<int> _cooldownBars;
private readonly StrategyParam<DataType> _candleType;
private RelativeStrengthIndex _rsi;
private decimal _prevRsi;
private int _holdingDays;
private int _entriesExecuted;
private int _barsSinceSignal;
/// <summary>
/// VIX security used for RSI calculation.
/// </summary>
public Security Vix
{
get => _vix.Value;
set => _vix.Value = value;
}
/// <summary>
/// RSI period.
/// </summary>
public int RsiPeriod
{
get => _rsiPeriod.Value;
set => _rsiPeriod.Value = value;
}
/// <summary>
/// RSI overbought level.
/// </summary>
public int OverboughtLevel
{
get => _overbought.Value;
set => _overbought.Value = value;
}
/// <summary>
/// RSI oversold level.
/// </summary>
public int OversoldLevel
{
get => _oversold.Value;
set => _oversold.Value = value;
}
/// <summary>
/// Minimum holding bars.
/// </summary>
public int MinHoldingDays
{
get => _minHolding.Value;
set => _minHolding.Value = value;
}
/// <summary>
/// Maximum holding bars.
/// </summary>
public int MaxHoldingDays
{
get => _maxHolding.Value;
set => _maxHolding.Value = value;
}
/// <summary>
/// Maximum entries per run.
/// </summary>
public int MaxEntries
{
get => _maxEntries.Value;
set => _maxEntries.Value = value;
}
/// <summary>
/// Minimum bars between entries.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Candle type for both instruments.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public LarryConnersVixReversalIIStrategy()
{
_vix = Param<Security>(nameof(Vix), null)
.SetDisplay("VIX Security", "VIX symbol", "Universe");
_rsiPeriod = Param(nameof(RsiPeriod), 25)
.SetDisplay("RSI Period", "RSI length", "Parameters");
_overbought = Param(nameof(OverboughtLevel), 61)
.SetDisplay("Overbought Level", "RSI overbought level", "Parameters");
_oversold = Param(nameof(OversoldLevel), 42)
.SetDisplay("Oversold Level", "RSI oversold level", "Parameters");
_minHolding = Param(nameof(MinHoldingDays), 7)
.SetDisplay("Min Holding Days", "Minimum holding period", "Risk Management");
_maxHolding = Param(nameof(MaxHoldingDays), 12)
.SetDisplay("Max Holding Days", "Maximum holding period", "Risk Management");
_maxEntries = Param(nameof(MaxEntries), 45)
.SetDisplay("Max Entries", "Maximum entries per run", "Risk Management");
_cooldownBars = Param(nameof(CooldownBars), 240)
.SetDisplay("Cooldown Bars", "Minimum bars between entries", "Risk Management");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "Data");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
if (Security == null || Vix == null)
throw new InvalidOperationException("Securities must be set.");
yield return (Security, CandleType);
yield return (Vix, CandleType);
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_rsi = null;
_prevRsi = 0m;
_holdingDays = 0;
_entriesExecuted = 0;
_barsSinceSignal = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_rsi = new RelativeStrengthIndex { Length = RsiPeriod };
_prevRsi = 0m;
_holdingDays = 0;
_entriesExecuted = 0;
_barsSinceSignal = CooldownBars;
SubscribeCandles(CandleType, true, Security).Start();
SubscribeCandles(CandleType, true, Vix)
.Bind(_rsi, ProcessVix)
.Start();
}
private void ProcessVix(ICandleMessage candle, decimal rsiValue)
{
if (candle.State != CandleStates.Finished)
return;
_barsSinceSignal++;
if (!_rsi.IsFormed)
{
_prevRsi = rsiValue;
return;
}
if (_holdingDays > 0)
{
_holdingDays++;
if (_holdingDays >= MinHoldingDays && _holdingDays >= MaxHoldingDays)
{
if (Position > 0)
SellMarket(Math.Abs(Position));
else if (Position < 0)
BuyMarket(Math.Abs(Position));
_holdingDays = 0;
_barsSinceSignal = 0;
}
}
var crossOver = _prevRsi < OverboughtLevel && rsiValue >= OverboughtLevel;
var crossUnder = _prevRsi > OversoldLevel && rsiValue <= OversoldLevel;
if (_holdingDays == 0 && _entriesExecuted < MaxEntries && _barsSinceSignal >= CooldownBars)
{
if (crossOver && Position <= 0)
{
BuyMarket();
_holdingDays = 1;
_entriesExecuted++;
_barsSinceSignal = 0;
}
else if (crossUnder && Position >= 0)
{
SellMarket();
_holdingDays = 1;
_entriesExecuted++;
_barsSinceSignal = 0;
}
}
_prevRsi = rsiValue;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
class larry_conners_vix_reversal_ii_strategy(Strategy):
def __init__(self):
super(larry_conners_vix_reversal_ii_strategy, self).__init__()
self._vix = self.Param("Vix", None) \
.SetDisplay("VIX Security", "VIX symbol", "Universe")
self._rsi_period = self.Param("RsiPeriod", 25) \
.SetDisplay("RSI Period", "RSI length", "Parameters")
self._overbought = self.Param("OverboughtLevel", 61) \
.SetDisplay("Overbought Level", "RSI overbought level", "Parameters")
self._oversold = self.Param("OversoldLevel", 42) \
.SetDisplay("Oversold Level", "RSI oversold level", "Parameters")
self._min_holding = self.Param("MinHoldingDays", 7) \
.SetDisplay("Min Holding Days", "Minimum holding period", "Risk Management")
self._max_holding = self.Param("MaxHoldingDays", 12) \
.SetDisplay("Max Holding Days", "Maximum holding period", "Risk Management")
self._max_entries = self.Param("MaxEntries", 45) \
.SetDisplay("Max Entries", "Maximum entries per run", "Risk Management")
self._cooldown_bars = self.Param("CooldownBars", 240) \
.SetDisplay("Cooldown Bars", "Minimum bars between entries", "Risk Management")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Type of candles", "Data")
self._rsi = None
self._prev_rsi = 0.0
self._holding_days = 0
self._entries_executed = 0
self._bars_since_signal = 0
@property
def vix(self):
return self._vix.Value
@property
def rsi_period(self):
return self._rsi_period.Value
@property
def overbought_level(self):
return self._overbought.Value
@property
def oversold_level(self):
return self._oversold.Value
@property
def min_holding_days(self):
return self._min_holding.Value
@property
def max_holding_days(self):
return self._max_holding.Value
@property
def max_entries(self):
return self._max_entries.Value
@property
def cooldown_bars(self):
return self._cooldown_bars.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(larry_conners_vix_reversal_ii_strategy, self).OnReseted()
self._rsi = None
self._prev_rsi = 0.0
self._holding_days = 0
self._entries_executed = 0
self._bars_since_signal = 0
def OnStarted2(self, time):
super(larry_conners_vix_reversal_ii_strategy, self).OnStarted2(time)
self._rsi = RelativeStrengthIndex()
self._rsi.Length = self.rsi_period
self._prev_rsi = 0.0
self._holding_days = 0
self._entries_executed = 0
self._bars_since_signal = int(self.cooldown_bars)
self.SubscribeCandles(self.candle_type, True, self.Security).Start()
self.SubscribeCandles(self.candle_type, True, self.vix) \
.Bind(self._rsi, self._process_vix).Start()
def _process_vix(self, candle, rsi_value):
if candle.State != CandleStates.Finished:
return
rsi_val = float(rsi_value)
self._bars_since_signal += 1
if not self._rsi.IsFormed:
self._prev_rsi = rsi_val
return
ob = float(self.overbought_level)
os_level = float(self.oversold_level)
if self._holding_days > 0:
self._holding_days += 1
if self._holding_days >= int(self.min_holding_days) and self._holding_days >= int(self.max_holding_days):
if self.Position > 0:
self.SellMarket(abs(self.Position))
elif self.Position < 0:
self.BuyMarket(abs(self.Position))
self._holding_days = 0
self._bars_since_signal = 0
cross_over = self._prev_rsi < ob and rsi_val >= ob
cross_under = self._prev_rsi > os_level and rsi_val <= os_level
if self._holding_days == 0 and self._entries_executed < int(self.max_entries) and self._bars_since_signal >= int(self.cooldown_bars):
if cross_over and self.Position <= 0:
self.BuyMarket()
self._holding_days = 1
self._entries_executed += 1
self._bars_since_signal = 0
elif cross_under and self.Position >= 0:
self.SellMarket()
self._holding_days = 1
self._entries_executed += 1
self._bars_since_signal = 0
self._prev_rsi = rsi_val
def CreateClone(self):
return larry_conners_vix_reversal_ii_strategy()