Kalman Filter Trend 策略
此趋势跟随方法使用卡尔曼滤波器平滑价格波动,并估计潜在方向。滤波器可动态适应市场噪声,比常规均线更能反映趋势力度。
测试表明年均收益约为 112%,该策略在外汇市场表现最佳。
当收盘价上穿卡尔曼估算值时做多;收盘价下穿时做空。由于滤波器每根K线更新,价格穿越即转仓,可持续参与趋势行情。ATR止损防止趋势突然反转带来的损失。
细节
- 入场条件:
- 多头:
Close > Kalman Filter - 空头:
Close < Kalman Filter
- 多头:
- 多/空: 双向
- 离场条件:
- 多头: 收盘价跌破卡尔曼滤波
- 空头: 收盘价升破卡尔曼滤波
- 止损: 基于ATR
- 默认值:
ProcessNoise= 0.01MeasurementNoise= 0.1AtrPeriod= 14AtrMultiplier= 2 价格收在线的另一侧会反转持仓。止损距入场收盘价AtrMultiplier倍AtrPeriod周期ATR,按K线收盘检查;止损后下一次收在线外侧会重新开仓。设为0则关闭。CandleType= TimeSpan.FromMinutes(5)
- 过滤器:
- 类别: Trend
- 方向: 双向
- 指标: Kalman Filter
- 止损: 是
- 复杂度: 中等
- 时间框架: 日内
- 季节性: 否
- 神经网络: 否
- 背离: 否
- 风险等级: 中等
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()