Strategy uses Kalman Filter indicator to detect direction changes. The filter output is compared either with price or its slope depending on selected signal mode. When the signal turns bullish, the strategy opens a long position; when bearish, opens a short. Positions are reversed on opposite signals. Stop loss and take profit are applied using absolute distances.
Details
Entry Criteria:
Long: signal changes to bullish
Short: signal changes to bearish
Long/Short: Both
Exit Criteria: Opposite signal
Stops: Absolute stop loss and take profit
Default Values:
ProcessNoise = 1.0
MeasurementNoise = 1.0
CandleType = TimeSpan.FromHours(3).TimeFrame()
Mode = SignalModes.Kalman
StopLoss = 1000m
TakeProfit = 2000m
Filters:
Category: Trend following
Direction: Both
Indicators: Kalman Filter
Stops: Yes
Complexity: Intermediate
Timeframe: Intraday
Seasonality: No
Neural Networks: No
Divergence: No
Risk Level: Medium
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>
/// Strategy based on Kalman Filter direction signals.
/// Opens long when price crosses above filter, short when below.
/// </summary>
public class KalmanFilterSignalStrategy : Strategy
{
private readonly StrategyParam<decimal> _stopLossPct;
private readonly StrategyParam<decimal> _takeProfitPct;
private readonly StrategyParam<DataType> _candleType;
private decimal? _prevFilter;
private decimal? _prevSignal;
public decimal StopLossPct { get => _stopLossPct.Value; set => _stopLossPct.Value = value; }
public decimal TakeProfitPct { get => _takeProfitPct.Value; set => _takeProfitPct.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public KalmanFilterSignalStrategy()
{
_stopLossPct = Param(nameof(StopLossPct), 2m)
.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk");
_takeProfitPct = Param(nameof(TakeProfitPct), 3m)
.SetDisplay("Take Profit %", "Take profit percentage", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for calculations", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevFilter = null;
_prevSignal = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var kalman = new KalmanFilter();
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(kalman, ProcessCandle)
.Start();
StartProtection(
stopLoss: new Unit(StopLossPct, UnitTypes.Percent),
takeProfit: new Unit(TakeProfitPct, UnitTypes.Percent),
useMarketOrders: true);
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, kalman);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal filterValue)
{
if (candle.State != CandleStates.Finished)
return;
var signal = candle.ClosePrice > filterValue ? 1m : 0m;
if (_prevSignal.HasValue && signal != _prevSignal.Value)
{
if (signal > 0 && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
}
else if (signal == 0 && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
}
}
_prevFilter = filterValue;
_prevSignal = signal;
}
}
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
from StockSharp.Algo.Strategies import Strategy
class kalman_filter_signal_strategy(Strategy):
def __init__(self):
super(kalman_filter_signal_strategy, self).__init__()
self._stop_loss_pct = self.Param("StopLossPct", 2.0) \
.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk")
self._take_profit_pct = self.Param("TakeProfitPct", 3.0) \
.SetDisplay("Take Profit %", "Take profit percentage", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe for calculations", "General")
self._prev_filter = None
self._prev_signal = None
@property
def stop_loss_pct(self):
return self._stop_loss_pct.Value
@property
def take_profit_pct(self):
return self._take_profit_pct.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(kalman_filter_signal_strategy, self).OnReseted()
self._prev_filter = None
self._prev_signal = None
def OnStarted2(self, time):
super(kalman_filter_signal_strategy, self).OnStarted2(time)
kalman = KalmanFilter()
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(kalman, self.process_candle).Start()
self.StartProtection(
stopLoss=Unit(self.stop_loss_pct, UnitTypes.Percent),
takeProfit=Unit(self.take_profit_pct, UnitTypes.Percent),
useMarketOrders=True)
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, kalman)
self.DrawOwnTrades(area)
def process_candle(self, candle, filter_value):
if candle.State != CandleStates.Finished:
return
filter_value = float(filter_value)
signal = 1.0 if float(candle.ClosePrice) > filter_value else 0.0
if self._prev_signal is not None and signal != self._prev_signal:
if signal > 0 and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif signal == 0 and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_filter = filter_value
self._prev_signal = signal
def CreateClone(self):
return kalman_filter_signal_strategy()