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 = 20
BbMultiplier = 2.0
WvfPeriod = 20
WvfLookback = 50
HighestPercentile = 0.85
LowestPercentile = 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.
/// Uses Bollinger Bands width as volatility proxy.
/// Buys when price touches lower BB during high volatility (wide bands).
/// Sells when price touches upper BB during high volatility.
/// </summary>
public class WilliamsVixFixStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _bbLength;
private readonly StrategyParam<decimal> _bbMultiplier;
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<int> _cooldownBars;
private BollingerBands _bb;
private RelativeStrengthIndex _rsi;
private int _cooldownRemaining;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int BbLength
{
get => _bbLength.Value;
set => _bbLength.Value = value;
}
public decimal BbMultiplier
{
get => _bbMultiplier.Value;
set => _bbMultiplier.Value = value;
}
public int RsiLength
{
get => _rsiLength.Value;
set => _rsiLength.Value = value;
}
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
public WilliamsVixFixStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_bbLength = Param(nameof(BbLength), 20)
.SetGreaterThanZero()
.SetDisplay("BB Length", "Bollinger Bands period", "Bollinger Bands");
_bbMultiplier = Param(nameof(BbMultiplier), 2.0m)
.SetDisplay("BB Multiplier", "BB standard deviation multiplier", "Bollinger Bands");
_rsiLength = Param(nameof(RsiLength), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Length", "RSI period", "RSI");
_cooldownBars = Param(nameof(CooldownBars), 10)
.SetDisplay("Cooldown Bars", "Bars to wait between trades", "Risk");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_bb = null;
_rsi = null;
_cooldownRemaining = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_bb = new BollingerBands { Length = BbLength, Width = BbMultiplier };
_rsi = new RelativeStrengthIndex { Length = RsiLength };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(_bb, _rsi, OnProcess)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _bb);
DrawOwnTrades(area);
}
}
private void OnProcess(ICandleMessage candle, IIndicatorValue bbValue, IIndicatorValue rsiValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_bb.IsFormed || !_rsi.IsFormed)
return;
if (bbValue.IsEmpty || rsiValue.IsEmpty)
return;
var bb = (BollingerBandsValue)bbValue;
if (bb.UpBand is not decimal upper || bb.LowBand is not decimal lower || bb.MovingAverage is not decimal mid)
return;
var rsiVal = rsiValue.ToDecimal();
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (_cooldownRemaining > 0)
{
_cooldownRemaining--;
return;
}
// Buy: price at or below lower BB + RSI oversold
if (candle.ClosePrice <= lower && rsiVal < 35 && Position <= 0)
{
if (Position < 0)
BuyMarket(Math.Abs(Position));
BuyMarket(Volume);
_cooldownRemaining = CooldownBars;
}
// Sell: price at or above upper BB + RSI overbought
else if (candle.ClosePrice >= upper && rsiVal > 65 && Position >= 0)
{
if (Position > 0)
SellMarket(Math.Abs(Position));
SellMarket(Volume);
_cooldownRemaining = CooldownBars;
}
// Exit long: price reaches middle BB or RSI > 70
else if (Position > 0 && (candle.ClosePrice >= mid || rsiVal > 70))
{
SellMarket(Math.Abs(Position));
_cooldownRemaining = CooldownBars;
}
// Exit short: price reaches middle BB or RSI < 30
else if (Position < 0 && (candle.ClosePrice <= mid || rsiVal < 30))
{
BuyMarket(Math.Abs(Position));
_cooldownRemaining = CooldownBars;
}
}
}
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 BollingerBands, RelativeStrengthIndex, IndicatorHelper
from StockSharp.Algo.Strategies import Strategy
class williams_vix_fix_strategy(Strategy):
"""Williams VIX Fix Strategy."""
def __init__(self):
super(williams_vix_fix_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._bb_length = self.Param("BbLength", 20) \
.SetDisplay("BB Length", "Bollinger Bands period", "Bollinger Bands")
self._bb_multiplier = self.Param("BbMultiplier", 2.0) \
.SetDisplay("BB Multiplier", "BB standard deviation multiplier", "Bollinger Bands")
self._rsi_length = self.Param("RsiLength", 14) \
.SetDisplay("RSI Length", "RSI period", "RSI")
self._cooldown_bars = self.Param("CooldownBars", 10) \
.SetDisplay("Cooldown Bars", "Bars to wait between trades", "Risk")
self._bb = None
self._rsi = None
self._cooldown_remaining = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(williams_vix_fix_strategy, self).OnReseted()
self._bb = None
self._rsi = None
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(williams_vix_fix_strategy, self).OnStarted2(time)
self._bb = BollingerBands()
self._bb.Length = int(self._bb_length.Value)
self._bb.Width = float(self._bb_multiplier.Value)
self._rsi = RelativeStrengthIndex()
self._rsi.Length = int(self._rsi_length.Value)
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(self._bb, self._rsi, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, self._bb)
self.DrawOwnTrades(area)
def _on_process(self, candle, bb_value, rsi_value):
if candle.State != CandleStates.Finished:
return
if not self._bb.IsFormed or not self._rsi.IsFormed:
return
if bb_value.IsEmpty or rsi_value.IsEmpty:
return
if bb_value.UpBand is None or bb_value.LowBand is None or bb_value.MovingAverage is None:
return
upper = float(bb_value.UpBand)
lower = float(bb_value.LowBand)
mid = float(bb_value.MovingAverage)
rsi_val = float(IndicatorHelper.ToDecimal(rsi_value))
if not self.IsFormedAndOnlineAndAllowTrading():
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
return
close = float(candle.ClosePrice)
cooldown = int(self._cooldown_bars.Value)
if close <= lower and rsi_val < 35 and self.Position <= 0:
if self.Position < 0:
self.BuyMarket(Math.Abs(self.Position))
self.BuyMarket(self.Volume)
self._cooldown_remaining = cooldown
elif close >= upper and rsi_val > 65 and self.Position >= 0:
if self.Position > 0:
self.SellMarket(Math.Abs(self.Position))
self.SellMarket(self.Volume)
self._cooldown_remaining = cooldown
elif self.Position > 0 and (close >= mid or rsi_val > 70):
self.SellMarket(Math.Abs(self.Position))
self._cooldown_remaining = cooldown
elif self.Position < 0 and (close <= mid or rsi_val < 30):
self.BuyMarket(Math.Abs(self.Position))
self._cooldown_remaining = cooldown
def CreateClone(self):
return williams_vix_fix_strategy()