Diese Strategie wendet den Kalman-Filter auf die Eröffnungs- und Schlusskurse jeder Kerze an. Die resultierenden geglätteten Kerzen werden als bullisch oder bärisch klassifiziert, je nachdem ob der geglättete Schlusskurs über oder unter dem geglätteten Eröffnungskurs liegt. Positionen werden geöffnet, wenn sich die Kerzenfarbe ändert:
Bullisch (rosa) → öffnet eine Long-Position und schließt jede Short-Position.
Bärisch (blau) → öffnet eine Short-Position und schließt jede Long-Position.
Parameter
Process Noise – Glättungsfaktor für den Kalman-Filter.
Candle Type – Zeitrahmen der in der Strategie verwendeten Kerzen.
Funktionsweise
Für jede abgeschlossene Kerze werden die Eröffnungs- und Schlusskurse individuell mit separaten Kalman-Filtern geglättet.
Ein bullisches Signal wird erzeugt, wenn der geglättete Schlusskurs den geglätteten Eröffnungskurs übersteigt. Ein bärisches Signal tritt auf, wenn der geglättete Schlusskurs unter dem geglätteten Eröffnungskurs liegt.
Die Strategie geht bei einem bullischen Signal eine Long-Position ein und bei einem bärischen Signal eine Short-Position. Entgegengesetzte Positionen werden automatisch geschlossen.
Die Strategie ist als Beispiel für die Kombination mehrerer Kalman-Filter zur Bildung eines einfachen Trendfolge-Systems gedacht.
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()