Estratégia Williams VIX Fix
A estratégia Williams VIX Fix adapta o indicador de volatilidade de Larry Williams para instrumentos que não possuem um VIX publicado. Ela calcula um valor VIX sintético usando a distância entre o fechamento mais alto durante um período de referência e a mínima atual. Quando esse valor sobe acima de um limiar de Bollinger Band ou o preço fecha abaixo da banda inferior de Bollinger, a estratégia considera uma oportunidade de sobrevenda. Um cálculo invertido mede os extremos de sobrecompra.
A abordagem busca reversão à média após picos de volatilidade. Quando o VIX Fix sinaliza alto medo e o preço está abaixo da banda inferior, uma operação comprada é aberta. Por outro lado, quando o VIX Fix inverso aponta para complacência extrema e o preço está acima da banda superior, as posições compradas existentes são fechadas. Limites de percentil controlam a sensibilidade.
Detalhes
- Critérios de entrada:
- VIX Fix ≥ banda superior ou percentil e preço < banda inferior de Bollinger.
- Comprado/Vendido: Entradas compradas com saídas em sinal oposto.
- Critérios de saída:
- VIX Fix invertido ≥ banda superior ou percentil e preço > banda superior de Bollinger.
- Stops: Nenhum.
- Valores padrão:
BbLength= 20BbMultiplier= 2.0WvfPeriod= 20WvfLookback= 50HighestPercentile= 0.85LowestPercentile= 0.99
- Filtros:
- Categoria: Reversão à média de volatilidade
- Direção: Comprado
- Indicadores: Bollinger Bands, Williams VIX Fix
- Stops: Não
- Complexidade: Médio
- Período: Qualquer
- Sazonalidade: Não
- Redes neurais: Não
- Divergência: Não
- Nível de risco: Médio
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()