GitHub で見る

カルマンフィルター・ローソク足戦略

この戦略は各ローソク足の始値と終値にカルマンフィルターを適用します。生成された平滑化ローソク足は、平滑化された終値が平滑化された始値の上にあるか下にあるかに応じて、強気または弱気に分類されます。ポジションはローソク足の色が変わったときに開かれます。

  • 強気(ピンク) → ロングポジションを開き、ショートポジションを閉じる。
  • 弱気(青) → ショートポジションを開き、ロングポジションを閉じる。

パラメーター

  • Process Noise – カルマンフィルターの平滑化係数。
  • Candle Type – 戦略で使用するローソク足の時間軸。

動作原理

  1. 完成した各ローソク足について、始値と終値をそれぞれ別のカルマンフィルターで個別に平滑化します。
  2. 平滑化された終値が平滑化された始値を超えると強気シグナルが生成されます。平滑化された終値が平滑化された始値を下回ると弱気シグナルが発生します。
  3. 戦略は強気シグナルでロングポジションに入り、弱気シグナルでショートポジションに入ります。反対のポジションは自動的に閉じられます。

この戦略は、複数のカルマンフィルターを組み合わせてシンプルなトレンドフォローシステムを形成する例として意図されています。

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