Ver en GitHub

Estrategia Laguerre ROC

Esta estrategia utiliza el oscilador de tasa de cambio Laguerre para capturar reversiones de tendencia.

El oscilador Laguerre ROC suaviza la tasa de cambio mediante un filtro Laguerre de cuatro etapas. Los valores se normalizan entre 0 y 1. Dos umbrales definen las zonas de sobrecompra y sobreventa:

  • Up Level – los valores por encima de este nivel indican un fuerte impulso alcista.
  • Down Level – los valores por debajo de este nivel indican un fuerte impulso bajista.

Lógica de trading:

  1. Cuando el oscilador cae desde la zona de sobrecompra (valor anterior por encima de Up Level y el valor actual por debajo) la estrategia entra en una posición larga.
  2. Cuando el oscilador sube desde la zona de sobreventa (valor anterior por debajo de Down Level y el valor actual por encima) la estrategia entra en una posición corta.
  3. Si hay una posición larga abierta y el oscilador se vuelve bajista (valor anterior por debajo del nivel neutro de 0.5) la posición se cierra.
  4. Si hay una posición corta abierta y el oscilador se vuelve alcista (valor anterior por encima de 0.5) la posición se cierra.

Parámetros:

  • Period – longitud del período de retroceso para el cálculo de la tasa de cambio.
  • Gamma – factor de suavizado para el filtro Laguerre.
  • Up Level – umbral de sobrecompra.
  • Down Level – umbral de sobreventa.
  • Candle Type – marco temporal utilizado para los datos de velas.

El ejemplo demuestra cómo se puede recrear la lógica de un indicador personalizado dentro de una estrategia StockSharp de alto nivel usando la tasa de cambio incorporada y el filtrado 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;
	}
}