Ver en GitHub

Estrategia Voss Predictor

Esta estrategia implementa el filtro predictivo Voss de John Ehlers con un filtro de paso de banda para anticipar el movimiento del precio. Una posición larga se abre cuando el filtro predictivo sube por encima de la salida del paso de banda, mientras que una posición corta se abre cuando cae por debajo.

Detalles

  • Entrada: El filtro predictivo Voss cruza por encima del filtro de paso de banda.
  • Salida: El filtro predictivo Voss cruza por debajo del filtro de paso de banda.
  • Tipo: Seguimiento de tendencia.
  • Stops: Ninguno.
using System;
using System.Linq;
using System.Collections.Generic;

using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Strategy based on John Ehlers' Voss predictor.
/// Buys when the predictive filter crosses above the band-pass filter and sells on the opposite cross.
/// </summary>
public class VossPredictorStrategy : Strategy
{
	private readonly StrategyParam<int> _periodBandpass;
	private readonly StrategyParam<decimal> _bandWidth;
	private readonly StrategyParam<decimal> _barsPrediction;
	private readonly StrategyParam<DataType> _candleType;

	private decimal? _pricePrev1;
	private decimal? _pricePrev2;
	private decimal _bandPassPrev1;
	private decimal _bandPassPrev2;
	private readonly decimal[] _vossBuffer = new decimal[9];
	private decimal _prevVpf;
	private decimal _prevBpf;

	/// <summary>
	/// Band-pass period.
	/// </summary>
	public int PeriodBandpass { get => _periodBandpass.Value; set => _periodBandpass.Value = value; }

	/// <summary>
	/// Bandwidth coefficient.
	/// </summary>
	public decimal BandWidth { get => _bandWidth.Value; set => _bandWidth.Value = value; }

	/// <summary>
	/// Bars of prediction.
	/// </summary>
	public decimal BarsPrediction { get => _barsPrediction.Value; set => _barsPrediction.Value = value; }

	/// <summary>
	/// Candle type used by the strategy.
	/// </summary>
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	/// <summary>
	/// Constructor.
	/// </summary>
	public VossPredictorStrategy()
	{
		_periodBandpass = Param(nameof(PeriodBandpass), 20)
		.SetGreaterThanZero()
		.SetDisplay("Bandpass Period", "Period for band-pass filter", "Settings")
		
		.SetOptimize(10, 40, 5);

		_bandWidth = Param(nameof(BandWidth), 0.25m)
		.SetGreaterThanZero()
		.SetDisplay("Bandwidth", "Bandwidth coefficient", "Settings")
		
		.SetOptimize(0.05m, 1.0m, 0.05m);

		_barsPrediction = Param(nameof(BarsPrediction), 3.0m)
		.SetGreaterThanZero()
		.SetDisplay("Bars of Prediction", "Look ahead bars", "Settings")
		
		.SetOptimize(0.5m, 3.0m, 0.5m);

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
		.SetDisplay("Candle Type", "Type of candles", "General");
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_pricePrev1 = null;
		_pricePrev2 = null;
		_bandPassPrev1 = 0m;
		_bandPassPrev2 = 0m;
		Array.Clear(_vossBuffer, 0, _vossBuffer.Length);
		_prevVpf = 0m;
		_prevBpf = 0m;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		var alpha = 2.0 * Math.PI / PeriodBandpass;
		var cosAlpha = (decimal)Math.Cos(alpha);
		var gamma = Math.Cos(alpha * (double)BandWidth);
		var delta = 1.0 / gamma - Math.Sqrt(1.0 / (gamma * gamma) - 1.0);
		var deltaDec = (decimal)delta;
		var order = (int)(3m * Math.Min(3m, BarsPrediction));

		var subscription = SubscribeCandles(CandleType);

		subscription
		.Bind(candle =>
		{
			if (candle.State != CandleStates.Finished)
			return;

			if (!IsFormedAndOnlineAndAllowTrading())
			return;

			var price = candle.ClosePrice;

			var prev2 = _pricePrev2 ?? _pricePrev1 ?? price;
			var whiten = 0.5m * (price - prev2);
			_pricePrev2 = _pricePrev1;
			_pricePrev1 = price;

			var bandPass = (1m - deltaDec) * whiten
			+ cosAlpha * (1m + deltaDec) * _bandPassPrev1
			- deltaDec * _bandPassPrev2;

			_bandPassPrev2 = _bandPassPrev1;
			_bandPassPrev1 = bandPass;

			decimal e = 0m;
			for (var i = 0; i < order; i++)
			{
			e += _vossBuffer[order - i - 1] * (1m + i) / order;
			}

			var vpf = 0.5m * (3m + order) * bandPass - e;

			for (var i = order - 1; i > 0; i--)
			_vossBuffer[i] = _vossBuffer[i - 1];
			_vossBuffer[0] = vpf;

			var crossUp = _prevVpf <= _prevBpf && vpf > bandPass;
			var crossDown = _prevVpf >= _prevBpf && vpf < bandPass;

			if (crossUp && Position <= 0)
			{
			BuyMarket(Volume + Math.Abs(Position));
			}
			else if (crossDown && Position >= 0)
			{
			SellMarket(Volume + Math.Abs(Position));
			}

			_prevVpf = vpf;
			_prevBpf = bandPass;
		})
		.Start();

		var area = CreateChartArea();
		if (area != null)
		{
		DrawCandles(area, subscription);
		DrawOwnTrades(area);
		}
	}
}