在 GitHub 上查看

Laguerre ROC策略

该策略利用Laguerre变化率振荡器寻找趋势反转。

振荡器先计算价格变化率,再通过四阶Laguerre滤波平滑, 输出值归一化到0到1之间。

  • Up Level:高于此阈值表示强劲上升。
  • Down Level:低于此阈值表示强劲下降。

交易逻辑:

  1. 当振荡器从超买区向下离开时进入多头。
  2. 当振荡器从超卖区向上离开时进入空头。
  3. 振荡器跌破0.5时平多头。
  4. 振荡器升破0.5时平空头。

参数:

  • Period:变化率计算周期。
  • Gamma:Laguerre滤波平滑系数。
  • Up Level:超买阈值。
  • Down Level:超卖阈值。
  • Candle Type:所用K线类型。

该示例展示如何在StockSharp中结合内置变化率指示器和手动Laguerre滤波实现自定义指标逻辑。

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 the Laguerre rate of change oscillator.
/// </summary>
public class LaguerreRocStrategy : Strategy
{
	private readonly StrategyParam<int> _period;
	private readonly StrategyParam<decimal> _gamma;
	private readonly StrategyParam<decimal> _upLevel;
	private readonly StrategyParam<decimal> _downLevel;
	private readonly StrategyParam<DataType> _candleType;

	private RateOfChange _roc;
	private decimal _l0, _l1, _l2, _l3;
	private bool _isFirst = true;
	private int _prevColor = 2;

	public int Period { get => _period.Value; set => _period.Value = value; }
	public decimal Gamma { get => _gamma.Value; set => _gamma.Value = value; }
	public decimal UpLevel { get => _upLevel.Value; set => _upLevel.Value = value; }
	public decimal DownLevel { get => _downLevel.Value; set => _downLevel.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public LaguerreRocStrategy()
	{
		_period = Param(nameof(Period), 5)
			.SetGreaterThanZero()
			.SetDisplay("Period", "Rate of change lookback", "Indicators");

		_gamma = Param(nameof(Gamma), 0.5m)
			.SetDisplay("Gamma", "Laguerre smoothing factor", "Indicators");

		_upLevel = Param(nameof(UpLevel), 0.75m)
			.SetDisplay("Up Level", "Overbought threshold", "Indicators");

		_downLevel = Param(nameof(DownLevel), 0.25m)
			.SetDisplay("Down Level", "Oversold threshold", "Indicators");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Type of candles", "General");
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_isFirst = true;
		_prevColor = 2;
		_l0 = _l1 = _l2 = _l3 = 0m;
		_roc = default;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_roc = new RateOfChange { Length = Period };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(_roc, ProcessCandle)
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, _roc);
			DrawOwnTrades(area);
		}
	}

	private void ProcessCandle(ICandleMessage candle, decimal rocValue)
	{
		if (candle.State != CandleStates.Finished)
			return;

		decimal l0, l1, l2, l3;

		if (_isFirst)
		{
			l0 = l1 = l2 = l3 = rocValue;
			_isFirst = false;
		}
		else
		{
			l0 = (1 - Gamma) * rocValue + Gamma * _l0;
			l1 = -Gamma * l0 + _l0 + Gamma * _l1;
			l2 = -Gamma * l1 + _l1 + Gamma * _l2;
			l3 = -Gamma * l2 + _l2 + Gamma * _l3;
		}

		var cu = 0m;
		var cd = 0m;

		if (l0 >= l1) cu += l0 - l1; else cd += l1 - l0;
		if (l1 >= l2) cu += l1 - l2; else cd += l2 - l1;
		if (l2 >= l3) cu += l2 - l3; else cd += l3 - l2;

		var denom = cu + cd;
		var lroc = denom != 0m ? cu / denom : 0m;

		int color = 2;
		if (lroc > UpLevel) color = 4;
		else if (lroc > 0.5m) color = 3;
		if (lroc < DownLevel) color = 0;
		else if (lroc < 0.5m) color = 1;

		if (_prevColor > 2 && color <= 2 && Position < 0)
			BuyMarket();
		if (_prevColor < 2 && color >= 2 && Position > 0)
			SellMarket();

		if (_prevColor == 4 && color < 4 && Position <= 0)
			BuyMarket();
		if (_prevColor == 0 && color > 0 && Position >= 0)
			SellMarket();

		_prevColor = color;
		_l0 = l0; _l1 = l1; _l2 = l2; _l3 = l3;
	}
}