Всплеск подразумеваемой волатильности
Стратегия отслеживает резкие скачки подразумеваемой волатильности относительно предыдущего значения. Сильный всплеск вместе с ценой, идущей против скользящей средней, может указывать на краткосрочный разворот.
Тестирование показывает среднегодичную доходность около 163%. Стратегию лучше запускать на фондовом рынке.
Когда подразумеваемая волатильность повышается выше заданного порога, система входит в противоположную сторону движения цены, рассчитывая на возврат волатильности.
Позиции закрываются, когда волатильность начинает падать или срабатывает стоп-лосс.
Подробности
- Критерий входа: всплеск IV выше
IVSpikeThresholdи положение цены относительно MA. - Длинные/короткие: обе стороны.
- Критерий выхода: снижение IV или стоп.
- Стопы: да.
- Значения по умолчанию:
MAPeriod= 20IVPeriod= 20IVSpikeThreshold= 1.5mCandleType= TimeSpan.FromMinutes(5)
- Фильтры:
- Категория: Волатильность
- Направление: Обе стороны
- Индикаторы: IV, MA
- Стопы: Да
- Сложность: Средняя
- Таймфрейм: Внутридневной
- Сезонность: Нет
- Нейросети: Нет
- Дивергенция: Нет
- Уровень риска: Средний
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>
/// IV Spike strategy based on implied volatility spikes.
/// Enters long when IV increases above threshold and price is below MA,
/// or short when IV increases and price is above MA.
/// </summary>
public class IvSpikeStrategy : Strategy
{
private readonly StrategyParam<int> _maPeriod;
private readonly StrategyParam<int> _ivPeriod;
private readonly StrategyParam<decimal> _ivSpikeThreshold;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _cooldownBars;
private decimal _previousIV;
private int _cooldown;
/// <summary>
/// MA Period.
/// </summary>
public int MAPeriod
{
get => _maPeriod.Value;
set => _maPeriod.Value = value;
}
/// <summary>
/// IV Period (for historical volatility calculation).
/// </summary>
public int IVPeriod
{
get => _ivPeriod.Value;
set => _ivPeriod.Value = value;
}
/// <summary>
/// IV Spike Threshold (minimum IV increase for signal).
/// </summary>
public decimal IVSpikeThreshold
{
get => _ivSpikeThreshold.Value;
set => _ivSpikeThreshold.Value = value;
}
/// <summary>
/// Candle type for strategy calculation.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Cooldown bars between trades.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Initialize the IV Spike strategy.
/// </summary>
public IvSpikeStrategy()
{
_maPeriod = Param(nameof(MAPeriod), 20)
.SetDisplay("MA Period", "Period for Moving Average calculation", "Indicators")
.SetOptimize(10, 50, 10);
_ivPeriod = Param(nameof(IVPeriod), 20)
.SetDisplay("IV Period", "Period for volatility calculation", "Indicators")
.SetOptimize(10, 30, 5);
_ivSpikeThreshold = Param(nameof(IVSpikeThreshold), 1.5m)
.SetDisplay("IV Spike Threshold", "Minimum IV increase multiplier", "Entry")
.SetOptimize(1.2m, 2.0m, 0.1m);
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_cooldownBars = Param(nameof(CooldownBars), 500)
.SetRange(1, 1000)
.SetDisplay("Cooldown Bars", "Bars to wait between trades", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_previousIV = default;
_cooldown = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_previousIV = 0;
_cooldown = 0;
var ma = new SimpleMovingAverage { Length = MAPeriod };
var hv = new StandardDeviation { Length = IVPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ma, hv, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal maValue, decimal ivValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (_previousIV == 0 && ivValue > 0)
{
_previousIV = ivValue;
return;
}
if (_cooldown > 0)
{
_cooldown--;
_previousIV = ivValue;
return;
}
var ivChange = _previousIV > 0 ? ivValue / _previousIV : 0;
if (Position == 0 && ivChange >= IVSpikeThreshold)
{
if (candle.ClosePrice < maValue)
{
BuyMarket();
_cooldown = CooldownBars;
}
else if (candle.ClosePrice > maValue)
{
SellMarket();
_cooldown = CooldownBars;
}
}
else if (Position > 0 && ivValue < _previousIV)
{
SellMarket();
_cooldown = CooldownBars;
}
else if (Position < 0 && ivValue < _previousIV)
{
BuyMarket();
_cooldown = CooldownBars;
}
_previousIV = ivValue;
}
}
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
from StockSharp.Algo.Indicators import SimpleMovingAverage, StandardDeviation
from StockSharp.Algo.Strategies import Strategy
class iv_spike_strategy(Strategy):
"""
IV Spike strategy based on implied volatility spikes.
Enters long when IV increases above threshold and price is below MA,
or short when IV increases and price is above MA.
"""
def __init__(self):
super(iv_spike_strategy, self).__init__()
self._ma_period = self.Param("MAPeriod", 20).SetDisplay("MA Period", "Period for Moving Average calculation", "Indicators")
self._iv_period = self.Param("IVPeriod", 20).SetDisplay("IV Period", "Period for volatility calculation", "Indicators")
self._iv_spike_threshold = self.Param("IVSpikeThreshold", 1.5).SetDisplay("IV Spike Threshold", "Minimum IV increase multiplier", "Entry")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(1))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._cooldown_bars = self.Param("CooldownBars", 500).SetDisplay("Cooldown Bars", "Bars to wait between trades", "General")
self._previous_iv = 0.0
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(iv_spike_strategy, self).OnReseted()
self._previous_iv = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(iv_spike_strategy, self).OnStarted2(time)
self._previous_iv = 0.0
self._cooldown = 0
ma = SimpleMovingAverage()
ma.Length = self._ma_period.Value
hv = StandardDeviation()
hv.Length = self._iv_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(ma, hv, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ma)
self.DrawOwnTrades(area)
def _process_candle(self, candle, ma_val, iv_val):
if candle.State != CandleStates.Finished:
return
iv = float(iv_val)
if self._previous_iv == 0 and iv > 0:
self._previous_iv = iv
return
if self._cooldown > 0:
self._cooldown -= 1
self._previous_iv = iv
return
iv_change = iv / self._previous_iv if self._previous_iv > 0 else 0.0
close = float(candle.ClosePrice)
mv = float(ma_val)
threshold = float(self._iv_spike_threshold.Value)
cd = self._cooldown_bars.Value
if self.Position == 0 and iv_change >= threshold:
if close < mv:
self.BuyMarket()
self._cooldown = cd
elif close > mv:
self.SellMarket()
self._cooldown = cd
elif self.Position > 0 and iv < self._previous_iv:
self.SellMarket()
self._cooldown = cd
elif self.Position < 0 and iv < self._previous_iv:
self.BuyMarket()
self._cooldown = cd
self._previous_iv = iv
def CreateClone(self):
return iv_spike_strategy()