Стратегия "Kalman Filter Candles"
Стратегия использует фильтр Калмана для сглаживания цены открытия и закрытия каждой свечи. На основе сглажённых значений формируется новая свеча, цвет которой определяет торговый сигнал:
- Бычья свеча (розовая) – сглажённая цена закрытия выше сглажённой цены открытия, закрывает короткую позицию и открывает длинную.
- Медвежья свеча (синяя) – сглажённая цена закрытия ниже сглажённой цены открытия, закрывает длинную позицию и открывает короткую.
Параметры
Process Noise– коэффициент сглаживания фильтра Калмана.Candle Type– таймфрейм свечей, используемых в стратегии.
Как работает
- Для каждой завершённой свечи цены открытия и закрытия обрабатываются отдельными фильтрами Калмана.
- Сравнение сглажённых цен определяет цвет свечи и направление сигнала.
- При смене цвета на бычий стратегия открывает покупку, при смене на медвежий – продажу; противоположные позиции закрываются автоматически.
Стратегия демонстрирует применение нескольких фильтров Калмана для построения простого трендового подхода.
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()