GitHub で見る

Kalman Filter Signal Strategy

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;
	}
}