Этот метод следования за трендом использует фильтр Калмана для сглаживания ценовых колебаний и оценки базового направления. Фильтр динамически адаптируется к рыночному шуму, предоставляя более точное представление силы тренда по сравнению со стандартными скользящими средними.
Тестирование показывает среднегодичную доходность около 112%. Стратегию лучше запускать на рынке Форекс.
Длинная позиция открывается, когда цена закрытия поднимается выше оценки фильтра Калмана. Соответственно, короткая позиция открывается при закрытии ниже значения фильтра. Поскольку фильтр обновляется на каждой свечке, сделки разворачиваются всякий раз, когда цена пересекает линию, обеспечивая постоянное участие в трендовых движениях.
Трейдеры, предпочитающие системные подходы, могут найти фильтр Калмана полезным для уменьшения «пилы». Защитный стоп на основе ATR ограничивает риск в случае резкого разворота тренда.
Подробности
Условия входа:
Long: закрытие > фильтр Калмана
Short: закрытие < фильтр Калмана
Long/Short: обе стороны.
Условия выхода:
Long: выход при закрытии < фильтр Калмана
Short: выход при закрытии > фильтр Калмана
Стопы: да, стоп‑лосс на основе ATR.
Параметры по умолчанию:
ProcessNoise = 0.01m
MeasurementNoise = 0.1m
CandleType = TimeSpan.FromMinutes(5)
Фильтры:
Категория: Тренд
Направление: Обе стороны
Индикаторы: Фильтр Калмана
Стопы: Да
Сложность: Средняя
Таймфрейм: Внутридневной
Сезонность: Нет
Нейросети: Нет
Дивергенция: Нет
Уровень риска: Средний
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()