Стратегия Voss Predictor
Стратегия использует предсказательный фильтр Voss Джона Эйлерса вместе с полосовым фильтром для оценки будущего движения цены. Длинная позиция открывается, когда предсказательный фильтр поднимается выше полосового, а короткая — когда он опускается ниже.
Детали
- Вход: предсказательный фильтр пересекает полосовой фильтр снизу вверх.
- Выход: предсказательный фильтр пересекает полосовой фильтр сверху вниз.
- Тип: трендовая.
- Стоп: нет.
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);
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
import math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Strategies import Strategy
class voss_predictor_strategy(Strategy):
def __init__(self):
super(voss_predictor_strategy, self).__init__()
self._period_bandpass = self.Param("PeriodBandpass", 20) \
.SetDisplay("Bandpass Period", "Period for band-pass filter", "Settings")
self._band_width = self.Param("BandWidth", 0.25) \
.SetDisplay("Bandwidth", "Bandwidth coefficient", "Settings")
self._bars_prediction = self.Param("BarsPrediction", 3.0) \
.SetDisplay("Bars of Prediction", "Look ahead bars", "Settings")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._price_prev1 = None
self._price_prev2 = None
self._band_pass_prev1 = 0.0
self._band_pass_prev2 = 0.0
self._prev_vpf = 0.0
self._prev_bpf = 0.0
self._voss_buffer = [0.0] * 9
@property
def period_bandpass(self):
return self._period_bandpass.Value
@property
def band_width(self):
return self._band_width.Value
@property
def bars_prediction(self):
return self._bars_prediction.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(voss_predictor_strategy, self).OnReseted()
self._price_prev1 = None
self._price_prev2 = None
self._band_pass_prev1 = 0.0
self._band_pass_prev2 = 0.0
self._prev_vpf = 0.0
self._prev_bpf = 0.0
self._voss_buffer = [0.0] * 9
def OnStarted2(self, time):
super(voss_predictor_strategy, self).OnStarted2(time)
alpha = 2.0 * math.pi / float(self.period_bandpass)
self._cos_alpha = math.cos(alpha)
gamma = math.cos(alpha * float(self.band_width))
delta = 1.0 / gamma - math.sqrt(1.0 / (gamma * gamma) - 1.0)
self._delta_dec = delta
self._order = int(3.0 * min(3.0, float(self.bars_prediction)))
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self.on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def on_process(self, candle):
if candle.State != CandleStates.Finished:
return
price = float(candle.ClosePrice)
prev2 = self._price_prev2 if self._price_prev2 is not None else (self._price_prev1 if self._price_prev1 is not None else price)
whiten = 0.5 * (price - prev2)
self._price_prev2 = self._price_prev1
self._price_prev1 = price
band_pass = (1.0 - self._delta_dec) * whiten \
+ self._cos_alpha * (1.0 + self._delta_dec) * self._band_pass_prev1 \
- self._delta_dec * self._band_pass_prev2
self._band_pass_prev2 = self._band_pass_prev1
self._band_pass_prev1 = band_pass
e = 0.0
for i in range(self._order):
e += self._voss_buffer[self._order - i - 1] * (1.0 + i) / self._order
vpf = 0.5 * (3.0 + self._order) * band_pass - e
i = self._order - 1
while i > 0:
self._voss_buffer[i] = self._voss_buffer[i - 1]
i -= 1
self._voss_buffer[0] = vpf
cross_up = self._prev_vpf <= self._prev_bpf and vpf > band_pass
cross_down = self._prev_vpf >= self._prev_bpf and vpf < band_pass
if cross_up and self.Position <= 0:
self.BuyMarket()
elif cross_down and self.Position >= 0:
self.SellMarket()
self._prev_vpf = vpf
self._prev_bpf = band_pass
def CreateClone(self):
return voss_predictor_strategy()