Ver no GitHub

Estratégia Laguerre ROC

Esta estratégia usa o oscilador de taxa de variação Laguerre para capturar reversões de tendência.

O oscilador Laguerre ROC suaviza a taxa de variação por meio de um filtro Laguerre de quatro estágios. Os valores são normalizados entre 0 e 1. Dois limiares definem as zonas de sobrecompra e sobrevenda:

  • Up Level – valores acima deste nível indicam forte momentum de alta.
  • Down Level – valores abaixo deste nível indicam forte momentum de baixa.

Lógica de trading:

  1. Quando o oscilador cai da zona de sobrecompra (valor anterior acima de Up Level e valor atual abaixo) a estratégia entra em uma posição comprada.
  2. Quando o oscilador sobe da zona de sobrevenda (valor anterior abaixo de Down Level e valor atual acima) a estratégia entra em uma posição vendida.
  3. Se uma posição comprada estiver aberta e o oscilador ficar de baixa (valor anterior abaixo do nível neutro de 0.5) a posição é fechada.
  4. Se uma posição vendida estiver aberta e o oscilador ficar de alta (valor anterior acima de 0.5) a posição é fechada.

Parâmetros:

  • Period – comprimento do período de retrovisão para o cálculo da taxa de variação.
  • Gamma – fator de suavização para o filtro Laguerre.
  • Up Level – limiar de sobrecompra.
  • Down Level – limiar de sobrevenda.
  • Candle Type – período utilizado para os dados de velas.

O exemplo demonstra como a lógica de indicador personalizado pode ser recriada dentro de uma estratégia StockSharp de alto nível usando taxa de variação embutida e filtragem Laguerre manual.

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