Larry Conners Vix Reversal II 策略
该策略基于 VIX 指数的 RSI。当 VIX RSI 向上突破超买水平时做多;当 RSI 向下跌破超卖水平时做空。持仓至少维持指定天数后平仓。
细节
- 入场条件:
- 多头: RSI(VIX) 向上突破
Overbought level。 - 空头: RSI(VIX) 向下跌破
Oversold level。
- 多头: 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()