卡尔曼滤波蜡烛策略
该策略对每根K线的开盘价和收盘价分别应用卡尔曼滤波器。生成的平滑价格构成新的蜡烛,通过比较平滑后的收盘价与开盘价确定蜡烛颜色:
- 多头蜡烛(粉色):平滑收盘价高于平滑开盘价,平仓空头并开多头。
- 空头蜡烛(蓝色):平滑收盘价低于平滑开盘价,平仓多头并开空头。
参数
Process Noise– 卡尔曼滤波的平滑系数。Candle Type– 策略使用的K线周期。
工作原理
- 每根完成的K线都会将开盘价和收盘价分别送入两个卡尔曼滤波器。
- 根据平滑后的开盘价和收盘价比较结果确定多头或空头信号。
- 多头信号时策略做多,空头信号时做空,同时自动平掉相反方向的持仓。
该策略展示了如何结合多个卡尔曼滤波器构建简单的趋势跟随系统。
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 Kalman filtered candle colors.
/// </summary>
public class KalmanFilterCandlesStrategy : Strategy
{
private readonly StrategyParam<decimal> _processNoise;
private readonly StrategyParam<DataType> _candleType;
private KalmanFilter _openFilter;
private KalmanFilter _closeFilter;
private int _prevColor;
private bool _hasPrev;
public decimal ProcessNoise { get => _processNoise.Value; set => _processNoise.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public KalmanFilterCandlesStrategy()
{
_processNoise = Param(nameof(ProcessNoise), 1m)
.SetDisplay("Process Noise", "Kalman filter smoothing factor", "Parameters");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Time frame for candles", "Common");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevColor = 1;
_hasPrev = false;
_openFilter = default;
_closeFilter = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_openFilter = new KalmanFilter { ProcessNoise = ProcessNoise, MeasurementNoise = ProcessNoise };
_closeFilter = new KalmanFilter { ProcessNoise = ProcessNoise, MeasurementNoise = ProcessNoise };
Indicators.Add(_openFilter);
Indicators.Add(_closeFilter);
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ProcessCandle).Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var openInput = new DecimalIndicatorValue(_openFilter, candle.OpenPrice, candle.OpenTime) { IsFinal = true };
var closeInput = new DecimalIndicatorValue(_closeFilter, candle.ClosePrice, candle.OpenTime) { IsFinal = true };
var openRes = _openFilter.Process(openInput);
var closeRes = _closeFilter.Process(closeInput);
if (!IsFormedAndOnlineAndAllowTrading())
return;
var openVal = openRes.ToDecimal();
var closeVal = closeRes.ToDecimal();
var color = openVal < closeVal ? 2 : openVal > closeVal ? 0 : 1;
if (_hasPrev)
{
if (color == 2 && _prevColor != 2)
{
if (Position < 0)
BuyMarket();
if (Position <= 0)
BuyMarket();
}
else if (color == 0 && _prevColor != 0)
{
if (Position > 0)
SellMarket();
if (Position >= 0)
SellMarket();
}
}
_prevColor = color;
_hasPrev = true;
}
}
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
from StockSharp.Algo.Indicators import KalmanFilter
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class kalman_filter_candles_strategy(Strategy):
def __init__(self):
super(kalman_filter_candles_strategy, self).__init__()
self._process_noise = self.Param("ProcessNoise", 1.0) \
.SetDisplay("Process Noise", "Kalman filter smoothing factor", "Parameters")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Time frame for candles", "Common")
self._open_filter = None
self._close_filter = None
self._prev_color = 1
self._has_prev = False
@property
def process_noise(self):
return self._process_noise.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(kalman_filter_candles_strategy, self).OnReseted()
self._prev_color = 1
self._has_prev = False
self._open_filter = None
self._close_filter = None
def OnStarted2(self, time):
super(kalman_filter_candles_strategy, self).OnStarted2(time)
self._open_filter = KalmanFilter()
self._open_filter.ProcessNoise = self.process_noise
self._open_filter.MeasurementNoise = self.process_noise
self._close_filter = KalmanFilter()
self._close_filter.ProcessNoise = self.process_noise
self._close_filter.MeasurementNoise = self.process_noise
self.Indicators.Add(self._open_filter)
self.Indicators.Add(self._close_filter)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
open_res = process_float(self._open_filter, candle.OpenPrice, candle.OpenTime, True)
close_res = process_float(self._close_filter, candle.ClosePrice, candle.OpenTime, True)
open_val = float(open_res)
close_val = float(close_res)
if open_val < close_val:
color = 2
elif open_val > close_val:
color = 0
else:
color = 1
if self._has_prev:
if color == 2 and self._prev_color != 2:
if self.Position < 0:
self.BuyMarket()
if self.Position <= 0:
self.BuyMarket()
elif color == 0 and self._prev_color != 0:
if self.Position > 0:
self.SellMarket()
if self.Position >= 0:
self.SellMarket()
self._prev_color = color
self._has_prev = True
def CreateClone(self):
return kalman_filter_candles_strategy()