Estrategia de Reversión VIX II de Larry Conners
La estrategia opera basándose en el RSI del índice VIX. Se abre una posición larga cuando el RSI del VIX cruza por encima del nivel de sobrecompra. Se abre una posición corta cuando el RSI cruza por debajo del nivel de sobreventa. Las posiciones se cierran tras mantenerse durante un número mínimo de días.
Detalles
- Criterios de entrada:
- Largo: RSI(VIX) cruza por encima del
Overbought level. - Corto: RSI(VIX) cruza por debajo del
Oversold level.
- Largo: RSI(VIX) cruza por encima del
- Largo/Corto: Ambos lados.
- Criterios de salida: Cerrar posición después de
Min holding daysaMax holding days. - Stops: Ninguno.
- Valores predeterminados:
RSI period= 25Overbought level= 61Oversold level= 42Min holding days= 7Max holding days= 12
- Filtros:
- Categoría: Reversión a la media
- Dirección: Ambos
- Indicadores: RSI
- Stops: No
- Complejidad: Básico
- Marco temporal: Diario
- Estacionalidad: No
- Redes neuronales: No
- Divergencia: No
- Nivel de riesgo: Medio
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()