Auf GitHub ansehen

Kalman-Filter-Kerzen-Strategie

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

  1. Für jede abgeschlossene Kerze werden die Eröffnungs- und Schlusskurse individuell mit separaten Kalman-Filtern geglättet.
  2. 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.
  3. 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;
	}
}