Williams VIX Fix-Strategie
Die Williams VIX Fix-Strategie passt Larry Williams' Volatilitätsindikator für Instrumente an, denen ein veröffentlichter VIX fehlt. Sie berechnet einen synthetischen VIX-Wert anhand der Distanz zwischen dem höchsten Schlusskurs über einen Rückblickzeitraum und dem aktuellen Tief. Wenn dieser Wert über einen Bollinger-Band-Schwellenwert ansteigt oder der Kurs unter die untere Bollinger Band schließt, betrachtet die Strategie dies als überverkaufte Gelegenheit. Eine umgekehrte Berechnung misst überkaufte Extreme.
Der Ansatz sucht nach Mean Reversion nach Volatilitätsspitzen. Wenn der VIX Fix hohe Angst signalisiert und der Kurs unter der unteren Band liegt, wird eine Long-Position eröffnet. Umgekehrt, wenn der inverse VIX Fix auf extreme Selbstgefälligkeit hinweist und der Kurs über der oberen Band liegt, werden bestehende Long-Positionen geschlossen. Perzentil-Schwellenwerte kontrollieren die Empfindlichkeit.
Details
- Einstiegskriterien:
- VIX Fix ≥ obere Band oder Perzentil und Kurs < untere Bollinger Band.
- Long/Short: Long-Einstiege mit Ausstiegen bei entgegengesetztem Signal.
- Ausstiegskriterien:
- Invertierter VIX Fix ≥ obere Band oder Perzentil und Kurs > obere Bollinger Band.
- Stops: Keine.
- Standardwerte:
BbLength= 20BbMultiplier= 2.0WvfPeriod= 20WvfLookback= 50HighestPercentile= 0.85LowestPercentile= 0.99
- Filter:
- Kategorie: Volatilitäts-Mean Reversion
- Richtung: Long
- Indikatoren: Bollinger Bands, Williams VIX Fix
- Stops: Nein
- Komplexität: Mittel
- Zeitrahmen: Beliebig
- Saisonalität: Nein
- Neuronale Netze: Nein
- Divergenz: Nein
- Risikolevel: Mittel
namespace StockSharp.Samples.Strategies;
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
/// <summary>
/// Williams VIX Fix Strategy.
/// VIX Fix = (highest close of WvfPeriod - low) / highest close * 100; the inverted VIX Fix uses (high - lowest close) / lowest close.
/// Each value is extreme when it reaches its own Bollinger upper band (BbLength, BbMultiplier) or the highest value of the last
/// WvfLookback bars times its percentile (HighestPercentile for the VIX Fix, LowestPercentile for the inverted one).
/// A long opens on an extreme VIX Fix with the close below the lower price Bollinger Band and closes on an extreme inverted
/// VIX Fix with the close above the upper band.
/// </summary>
public class WilliamsVixFixStrategy : Strategy
{
private readonly StrategyParam<int> _bbLength;
private readonly StrategyParam<decimal> _bbMultiplier;
private readonly StrategyParam<int> _wvfPeriod;
private readonly StrategyParam<int> _wvfLookback;
private readonly StrategyParam<decimal> _highestPercentile;
private readonly StrategyParam<decimal> _lowestPercentile;
private readonly StrategyParam<DataType> _candleType;
private Highest _highestClose;
private Lowest _lowestClose;
private SimpleMovingAverage _wvfMean;
private StandardDeviation _wvfDeviation;
private Highest _wvfRange;
private SimpleMovingAverage _invMean;
private StandardDeviation _invDeviation;
private Highest _invRange;
/// <summary>
/// Bollinger Bands period.
/// </summary>
public int BbLength
{
get => _bbLength.Value;
set => _bbLength.Value = value;
}
/// <summary>
/// Bollinger Bands standard deviation multiplier.
/// </summary>
public decimal BbMultiplier
{
get => _bbMultiplier.Value;
set => _bbMultiplier.Value = value;
}
/// <summary>
/// Lookback of the highest and lowest close in the VIX Fix.
/// </summary>
public int WvfPeriod
{
get => _wvfPeriod.Value;
set => _wvfPeriod.Value = value;
}
/// <summary>
/// Lookback of the percentile thresholds.
/// </summary>
public int WvfLookback
{
get => _wvfLookback.Value;
set => _wvfLookback.Value = value;
}
/// <summary>
/// Percentile of the highest VIX Fix value that marks fear.
/// </summary>
public decimal HighestPercentile
{
get => _highestPercentile.Value;
set => _highestPercentile.Value = value;
}
/// <summary>
/// Percentile of the highest inverted VIX Fix value that marks complacency.
/// </summary>
public decimal LowestPercentile
{
get => _lowestPercentile.Value;
set => _lowestPercentile.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public WilliamsVixFixStrategy()
{
_bbLength = Param(nameof(BbLength), 20)
.SetGreaterThanZero()
.SetDisplay("BB Length", "Bollinger Bands period", "Bollinger");
_bbMultiplier = Param(nameof(BbMultiplier), 2.0m)
.SetGreaterThanZero()
.SetDisplay("BB Multiplier", "Bollinger Bands standard deviation multiplier", "Bollinger");
_wvfPeriod = Param(nameof(WvfPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("WVF Period", "Lookback of the highest and lowest close", "VIX Fix");
_wvfLookback = Param(nameof(WvfLookback), 50)
.SetGreaterThanZero()
.SetDisplay("WVF Lookback", "Lookback of the percentile thresholds", "VIX Fix");
_highestPercentile = Param(nameof(HighestPercentile), 0.85m)
.SetGreaterThanZero()
.SetDisplay("Highest Percentile", "Percentile of the highest VIX Fix value", "VIX Fix");
_lowestPercentile = Param(nameof(LowestPercentile), 0.99m)
.SetGreaterThanZero()
.SetDisplay("Lowest Percentile", "Percentile of the highest inverted VIX Fix value", "VIX Fix");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var bollinger = new BollingerBands { Length = BbLength, Width = BbMultiplier };
_highestClose = new Highest { Length = WvfPeriod };
_lowestClose = new Lowest { Length = WvfPeriod };
_wvfMean = new SimpleMovingAverage { Length = BbLength };
_wvfDeviation = new StandardDeviation { Length = BbLength };
_wvfRange = new Highest { Length = WvfLookback };
_invMean = new SimpleMovingAverage { Length = BbLength };
_invDeviation = new StandardDeviation { Length = BbLength };
_invRange = new Highest { Length = WvfLookback };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(bollinger, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, bollinger);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue bollingerValue)
{
if (candle.State != CandleStates.Finished)
return;
var time = candle.OpenTime;
var highestClose = _highestClose.Process(candle.ClosePrice, time, true).ToDecimal();
var lowestClose = _lowestClose.Process(candle.ClosePrice, time, true).ToDecimal();
if (!_highestClose.IsFormed || !_lowestClose.IsFormed || highestClose <= 0m || lowestClose <= 0m)
return;
var wvf = (highestClose - candle.LowPrice) / highestClose * 100m;
var inv = (candle.HighPrice - lowestClose) / lowestClose * 100m;
var fear = IsExtreme(wvf, time, _wvfMean, _wvfDeviation, _wvfRange, HighestPercentile);
var complacency = IsExtreme(inv, time, _invMean, _invDeviation, _invRange, LowestPercentile);
if (fear is null || complacency is null)
return;
if (bollingerValue is not IBollingerBandsValue { UpBand: decimal upper, LowBand: decimal lower })
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var close = candle.ClosePrice;
if (fear == true && close < lower && Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
else if (complacency == true && close > upper && Position > 0)
SellMarket(Position);
}
private bool? IsExtreme(decimal value, DateTime time, SimpleMovingAverage mean, StandardDeviation deviation, Highest range, decimal percentile)
{
var middle = mean.Process(value, time, true).ToDecimal();
var spread = deviation.Process(value, time, true).ToDecimal();
var highest = range.Process(value, time, true).ToDecimal();
if (!mean.IsFormed || !deviation.IsFormed || !range.IsFormed)
return null;
var upperBand = middle + BbMultiplier * spread;
return value >= upperBand || value >= highest * percentile;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import BollingerBands, Highest, Lowest, SimpleMovingAverage, StandardDeviation
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class williams_vix_fix_strategy(Strategy):
"""
Williams VIX Fix Strategy.
VIX Fix = (highest close of WvfPeriod - low) / highest close * 100; the inverted VIX Fix uses (high - lowest close) / lowest close.
Each value is extreme when it reaches its own Bollinger upper band (BbLength, BbMultiplier) or the highest value of the last
WvfLookback bars times its percentile (HighestPercentile for the VIX Fix, LowestPercentile for the inverted one).
A long opens on an extreme VIX Fix with the close below the lower price Bollinger Band and closes on an extreme inverted
VIX Fix with the close above the upper band.
"""
def __init__(self):
super(williams_vix_fix_strategy, self).__init__()
self._bb_length = self.Param("BbLength", 20).SetGreaterThanZero().SetDisplay("BB Length", "Bollinger Bands period", "Bollinger")
self._bb_multiplier = self.Param("BbMultiplier", 2.0).SetGreaterThanZero().SetDisplay("BB Multiplier", "Bollinger Bands standard deviation multiplier", "Bollinger")
self._wvf_period = self.Param("WvfPeriod", 20).SetGreaterThanZero().SetDisplay("WVF Period", "Lookback of the highest and lowest close", "VIX Fix")
self._wvf_lookback = self.Param("WvfLookback", 50).SetGreaterThanZero().SetDisplay("WVF Lookback", "Lookback of the percentile thresholds", "VIX Fix")
self._highest_percentile = self.Param("HighestPercentile", 0.85).SetGreaterThanZero().SetDisplay("Highest Percentile", "Percentile of the highest VIX Fix value", "VIX Fix")
self._lowest_percentile = self.Param("LowestPercentile", 0.99).SetGreaterThanZero().SetDisplay("Lowest Percentile", "Percentile of the highest inverted VIX Fix value", "VIX Fix")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._highest_close = None
self._lowest_close = None
self._wvf_set = None
self._inv_set = None
@property
def candle_type(self):
return self._candle_type.Value
def _create_set(self):
mean = SimpleMovingAverage()
mean.Length = self._bb_length.Value
deviation = StandardDeviation()
deviation.Length = self._bb_length.Value
rng = Highest()
rng.Length = self._wvf_lookback.Value
return (mean, deviation, rng)
def OnStarted2(self, time):
super(williams_vix_fix_strategy, self).OnStarted2(time)
bollinger = BollingerBands()
bollinger.Length = self._bb_length.Value
bollinger.Width = Decimal(float(self._bb_multiplier.Value))
self._highest_close = Highest()
self._highest_close.Length = self._wvf_period.Value
self._lowest_close = Lowest()
self._lowest_close.Length = self._wvf_period.Value
self._wvf_set = self._create_set()
self._inv_set = self._create_set()
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(bollinger, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, bollinger)
self.DrawOwnTrades(area)
def _process_candle(self, candle, bollinger_value):
if candle.State != CandleStates.Finished:
return
time = candle.OpenTime
highest_close = float(process_float(self._highest_close, candle.ClosePrice, time, True).GetValue[Decimal](None))
lowest_close = float(process_float(self._lowest_close, candle.ClosePrice, time, True).GetValue[Decimal](None))
if not self._highest_close.IsFormed or not self._lowest_close.IsFormed or highest_close <= 0 or lowest_close <= 0:
return
wvf = (highest_close - float(candle.LowPrice)) / highest_close * 100.0
inv = (float(candle.HighPrice) - lowest_close) / lowest_close * 100.0
fear = self._is_extreme(wvf, time, self._wvf_set, float(self._highest_percentile.Value))
complacency = self._is_extreme(inv, time, self._inv_set, float(self._lowest_percentile.Value))
if fear is None or complacency is None:
return
if bollinger_value.UpBand is None or bollinger_value.LowBand is None:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
close = float(candle.ClosePrice)
upper = float(bollinger_value.UpBand)
lower = float(bollinger_value.LowBand)
if fear and close < lower and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
elif complacency and close > upper and self.Position > 0:
self.SellMarket(self.Position)
def _is_extreme(self, value, time, indicators, percentile):
mean, deviation, rng = indicators
middle = float(process_float(mean, value, time, True).GetValue[Decimal](None))
spread = float(process_float(deviation, value, time, True).GetValue[Decimal](None))
highest = float(process_float(rng, value, time, True).GetValue[Decimal](None))
if not mean.IsFormed or not deviation.IsFormed or not rng.IsFormed:
return None
upper_band = middle + float(self._bb_multiplier.Value) * spread
return value >= upper_band or value >= highest * percentile
def CreateClone(self):
return williams_vix_fix_strategy()