Diese Trendfolge-Methode verwendet einen Kalman Filter, um Preisschwankungen zu glätten und die zugrunde liegende Richtung zu schätzen. Der Filter passt sich dynamisch an das Marktrauschen an und bietet eine verfeinerte Ansicht der Trendstärke im Vergleich zu Standard-Gleitenden Durchschnitten.
Tests zeigen eine durchschnittliche jährliche Rendite von etwa 112%. Sie funktioniert am besten auf dem Forex-Markt.
Eine Long-Position wird eröffnet, wenn der Schlusskurs über die Kalman-Filter-Schätzung steigt. Umgekehrt wird eine Short-Position eingegangen, wenn der Schluss unter den Filterwert fällt. Da der Filter bei jeder Bar aktualisiert wird, wechseln Trades immer dann, wenn der Preis die Linie kreuzt, was eine kontinuierliche Teilnahme an Trendmärkten ermöglicht.
Trader, die systematische Ansätze bevorzugen, können den Kalman Filter nützlich finden, um Whipsaws zu reduzieren. Ein Schutz-Stop basierend auf ATR hält das Risiko begrenzt, falls der Trend schnell umkehrt.
Details
Einstiegskriterien:
Long: Schluss > Kalman Filter
Short: Schluss < Kalman Filter
Long/Short: Beide Seiten.
Ausstiegskriterien:
Long: Ausstieg bei Schluss < Kalman Filter
Short: Ausstieg bei Schluss > Kalman Filter
Stops: Ja, ATR-basierter Stop-Loss.
Standardwerte:
ProcessNoise = 0.01
MeasurementNoise = 0.1
AtrPeriod = 14
AtrMultiplier = 2
Ein Einstieg auf der anderen Seite der Linie dreht die Position. Der Stop liegt AtrMultiplier mal die ATR über AtrPeriod vom Einstiegsschluss entfernt und wird bei Kerzenschluss geprüft; danach eröffnet der nächste Schluss jenseits der Linie eine neue Position. 0 schaltet ihn ab.
CandleType = TimeSpan.FromMinutes(5)
Filter:
Kategorie: Trend
Richtung: Beide
Indikatoren: Kalman Filter
Stops: Ja
Komplexität: Mittel
Zeitrahmen: Intraday
Saisonalität: Nein
Neuronale Netze: Nein
Divergenz: Nein
Risikolevel: Mittel
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Kalman Filter Trend strategy.
/// A close above the Kalman filter estimate goes long and a close below it goes short, reversing an opposite position, so the position flips
/// whenever price crosses the line. The stop lies AtrMultiplier times the AtrPeriod ATR from the entry close and is checked on candle closes.
/// </summary>
public class KalmanFilterTrendStrategy : Strategy
{
private readonly StrategyParam<decimal> _processNoise;
private readonly StrategyParam<decimal> _measurementNoise;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<decimal> _atrMultiplier;
private readonly StrategyParam<DataType> _candleType;
private decimal _stopPrice;
/// <summary>
/// Process noise of the Kalman filter.
/// </summary>
public decimal ProcessNoise
{
get => _processNoise.Value;
set => _processNoise.Value = value;
}
/// <summary>
/// Measurement noise of the Kalman filter.
/// </summary>
public decimal MeasurementNoise
{
get => _measurementNoise.Value;
set => _measurementNoise.Value = value;
}
/// <summary>
/// Period of the stop ATR.
/// </summary>
public int AtrPeriod
{
get => _atrPeriod.Value;
set => _atrPeriod.Value = value;
}
/// <summary>
/// Stop distance from the entry in ATRs.
/// </summary>
public decimal AtrMultiplier
{
get => _atrMultiplier.Value;
set => _atrMultiplier.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public KalmanFilterTrendStrategy()
{
_processNoise = Param(nameof(ProcessNoise), 0.01m)
.SetGreaterThanZero()
.SetDisplay("Process Noise", "Process noise of the Kalman filter", "Kalman Filter");
_measurementNoise = Param(nameof(MeasurementNoise), 0.1m)
.SetGreaterThanZero()
.SetDisplay("Measurement Noise", "Measurement noise of the Kalman filter", "Kalman Filter");
_atrPeriod = Param(nameof(AtrPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("ATR Period", "Period of the stop ATR", "Risk");
_atrMultiplier = Param(nameof(AtrMultiplier), 2m)
.SetNotNegative()
.SetDisplay("ATR Multiplier", "Stop distance from the entry in ATRs", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_stopPrice = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_stopPrice = default;
var kalman = new KalmanFilter
{
ProcessNoise = ProcessNoise,
MeasurementNoise = MeasurementNoise,
};
var atr = new AverageTrueRange { Length = AtrPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(kalman, atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, kalman);
DrawOwnTrades(area);
var oscillators = CreateChartArea();
if (oscillators != null)
{
DrawIndicator(oscillators, atr);
}
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue kalmanValue, IIndicatorValue atrValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!kalmanValue.IsFormed || !atrValue.IsFormed)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var line = kalmanValue.GetValue<decimal>();
var atr = atrValue.GetValue<decimal>();
var close = candle.ClosePrice;
if (close > line && Position <= 0)
{
BuyMarket(Volume + Math.Abs(Position));
_stopPrice = close - AtrMultiplier * atr;
}
else if (close < line && Position >= 0)
{
SellMarket(Volume + Math.Abs(Position));
_stopPrice = close + AtrMultiplier * atr;
}
else if (Position > 0 && AtrMultiplier > 0 && close <= _stopPrice)
{
SellMarket(Position);
}
else if (Position < 0 && AtrMultiplier > 0 && close >= _stopPrice)
{
BuyMarket(-Position);
}
}
}
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 KalmanFilter, AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class kalman_filter_trend_strategy(Strategy):
"""
Kalman Filter Trend strategy.
A close above the Kalman filter estimate goes long and a close below it goes short, reversing an opposite position, so the position flips
whenever price crosses the line. The stop lies AtrMultiplier times the AtrPeriod ATR from the entry close and is checked on candle closes.
"""
def __init__(self):
super(kalman_filter_trend_strategy, self).__init__()
self._process_noise = self.Param("ProcessNoise", 0.01).SetGreaterThanZero().SetDisplay("Process Noise", "Process noise of the Kalman filter", "Kalman Filter")
self._measurement_noise = self.Param("MeasurementNoise", 0.1).SetGreaterThanZero().SetDisplay("Measurement Noise", "Measurement noise of the Kalman filter", "Kalman Filter")
self._atr_period = self.Param("AtrPeriod", 14).SetGreaterThanZero().SetDisplay("ATR Period", "Period of the stop ATR", "Risk")
self._atr_multiplier = self.Param("AtrMultiplier", 2.0).SetNotNegative().SetDisplay("ATR Multiplier", "Stop distance from the entry in ATRs", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._reset_state()
@property
def candle_type(self):
return self._candle_type.Value
def _reset_state(self):
self._stop_price = Decimal(0)
def OnReseted(self):
super(kalman_filter_trend_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(kalman_filter_trend_strategy, self).OnStarted2(time)
self._reset_state()
kalman = KalmanFilter()
kalman.ProcessNoise = Decimal(self._process_noise.Value)
kalman.MeasurementNoise = Decimal(self._measurement_noise.Value)
atr = AverageTrueRange()
atr.Length = self._atr_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(kalman, atr, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, kalman)
self.DrawOwnTrades(area)
oscillators = self.CreateChartArea()
if oscillators is not None:
self.DrawIndicator(oscillators, atr)
def _process_candle(self, candle, kalman_value, atr_value):
if candle.State != CandleStates.Finished:
return
if not kalman_value.IsFormed or not atr_value.IsFormed:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
line = kalman_value.GetValue[Decimal](None)
atr = atr_value.GetValue[Decimal](None)
close = candle.ClosePrice
stop_atr = Decimal(self._atr_multiplier.Value)
if close > line and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
self._stop_price = close - stop_atr * atr
elif close < line and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
self._stop_price = close + stop_atr * atr
elif self.Position > 0 and stop_atr > 0 and close <= self._stop_price:
self.SellMarket(self.Position)
elif self.Position < 0 and stop_atr > 0 and close >= self._stop_price:
self.BuyMarket(-self.Position)
def CreateClone(self):
return kalman_filter_trend_strategy()