Auf GitHub ansehen

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 = 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;
		}
	}
}