Kalman Filter Trend Strategy
此趋势跟随方法使用卡尔曼滤波器平滑价格波动,并估计潜在方向。滤波器可动态适应市场噪声,比常规均线更能反映趋势力度。
测试表明年均收益约为 112%,该策略在外汇市场表现最佳。
当收盘价上穿卡尔曼估算值时做多;收盘价下穿时做空。由于滤波器每根K线更新,价格穿越即转仓,可持续参与趋势行情。ATR止损防止趋势突然反转带来的损失。
细节
- 入场条件:
- 多头:
Close > Kalman Filter - 空头:
Close < Kalman Filter
- 多头:
- 多/空: 双向
- 离场条件:
- 多头: 收盘价跌破卡尔曼滤波
- 空头: 收盘价升破卡尔曼滤波
- 止损: 基于ATR
- 默认值:
ProcessNoise= 0.01mMeasurementNoise= 0.1mCandleType= TimeSpan.FromMinutes(5)
- 过滤器:
- 类别: Trend
- 方向: 双向
- 指标: Kalman Filter
- 止损: 是
- 复杂度: 中等
- 时间框架: 日内
- 季节性: 否
- 神经网络: 否
- 背离: 否
- 风险等级: 中等
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>
/// Kalman Filter Trend strategy.
/// Uses a custom Kalman Filter indicator to track price trend.
/// </summary>
public class KalmanFilterTrendStrategy : Strategy
{
private readonly StrategyParam<decimal> _processNoiseParam;
private readonly StrategyParam<decimal> _measurementNoiseParam;
private readonly StrategyParam<DataType> _candleTypeParam;
private KalmanFilter _kalmanFilter;
private AverageTrueRange _atr;
/// <summary>
/// Process noise coefficient for Kalman filter.
/// </summary>
public decimal ProcessNoise
{
get => _processNoiseParam.Value;
set => _processNoiseParam.Value = value;
}
/// <summary>
/// Measurement noise coefficient for Kalman filter.
/// </summary>
public decimal MeasurementNoise
{
get => _measurementNoiseParam.Value;
set => _measurementNoiseParam.Value = value;
}
/// <summary>
/// Candle type for strategy.
/// </summary>
public DataType CandleType
{
get => _candleTypeParam.Value;
set => _candleTypeParam.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public KalmanFilterTrendStrategy()
{
_processNoiseParam = Param(nameof(ProcessNoise), 0.01m)
.SetRange(0.0001m, 1)
.SetDisplay("Process Noise", "Process noise coefficient for Kalman filter", "Parameters")
.SetOptimize(0.001m, 0.1m, 0.005m);
_measurementNoiseParam = Param(nameof(MeasurementNoise), 0.1m)
.SetRange(0.0001m, 1)
.SetDisplay("Measurement Noise", "Measurement noise coefficient for Kalman filter", "Parameters")
.SetOptimize(0.01m, 1.0m, 0.1m);
_candleTypeParam = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Candle type for strategy", "Common");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_kalmanFilter = null;
_atr = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
// Create indicators
_kalmanFilter = new KalmanFilter
{
ProcessNoise = ProcessNoise,
MeasurementNoise = MeasurementNoise
};
_atr = new AverageTrueRange { Length = 14 };
// Create subscription and bind indicators
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_kalmanFilter, _atr, ProcessCandle)
.Start();
// Setup chart visualization if available
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _kalmanFilter);
DrawOwnTrades(area);
}
// Enable position protection
StartProtection(
takeProfit: new Unit(0, UnitTypes.Absolute), // No take profit
stopLoss: new Unit(2, UnitTypes.Absolute) // Stop loss at 2*ATR
);
}
private void ProcessCandle(ICandleMessage candle, decimal kalmanValue, decimal atrValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
// Calculate trend direction
var trend = candle.ClosePrice > kalmanValue ? 1 : -1;
// Trading logic based on price position relative to Kalman filter
if (trend > 0 && Position <= 0)
{
// Buy when price is above Kalman filter (uptrend)
BuyMarket(Volume + Math.Abs(Position));
}
else if (trend < 0 && Position >= 0)
{
// Sell when price is below Kalman filter (downtrend)
SellMarket(Volume + Math.Abs(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
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes
from StockSharp.Algo.Indicators import KalmanFilter, AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class kalman_filter_trend_strategy(Strategy):
"""
Kalman Filter trend: trades based on price position relative to Kalman filter.
"""
def __init__(self):
super(kalman_filter_trend_strategy, self).__init__()
self._process_noise = self.Param("ProcessNoise", 0.01).SetDisplay("Process Noise", "Kalman process noise", "Parameters")
self._measurement_noise = self.Param("MeasurementNoise", 0.1).SetDisplay("Measurement Noise", "Kalman measurement noise", "Parameters")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Timeframe", "General")
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(kalman_filter_trend_strategy, self).OnReseted()
def OnStarted2(self, time):
super(kalman_filter_trend_strategy, self).OnStarted2(time)
kf = KalmanFilter()
kf.ProcessNoise = self._process_noise.Value
kf.MeasurementNoise = self._measurement_noise.Value
atr = AverageTrueRange()
atr.Length = 14
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(kf, atr, self._process_candle).Start()
self.StartProtection(None, Unit(2, UnitTypes.Absolute))
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, kf)
self.DrawOwnTrades(area)
def _process_candle(self, candle, kf_val, atr_val):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
kf = float(kf_val)
if close > kf and self.Position <= 0:
self.BuyMarket()
elif close < kf and self.Position >= 0:
self.SellMarket()
def CreateClone(self):
return kalman_filter_trend_strategy()