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:
- 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.
- 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.
- 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.
- 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;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import RateOfChange
from StockSharp.Algo.Strategies import Strategy
class laguerre_roc_strategy(Strategy):
def __init__(self):
super(laguerre_roc_strategy, self).__init__()
self._period = self.Param("Period", 5) \
.SetDisplay("Period", "Rate of change lookback", "Indicators")
self._gamma = self.Param("Gamma", 0.5) \
.SetDisplay("Gamma", "Laguerre smoothing factor", "Indicators")
self._up_level = self.Param("UpLevel", 0.75) \
.SetDisplay("Up Level", "Overbought threshold", "Indicators")
self._down_level = self.Param("DownLevel", 0.25) \
.SetDisplay("Down Level", "Oversold threshold", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._roc = None
self._l0 = 0.0
self._l1 = 0.0
self._l2 = 0.0
self._l3 = 0.0
self._is_first = True
self._prev_color = 2
@property
def period(self):
return self._period.Value
@property
def gamma(self):
return self._gamma.Value
@property
def up_level(self):
return self._up_level.Value
@property
def down_level(self):
return self._down_level.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(laguerre_roc_strategy, self).OnReseted()
self._is_first = True
self._prev_color = 2
self._l0 = self._l1 = self._l2 = self._l3 = 0.0
self._roc = None
def OnStarted2(self, time):
super(laguerre_roc_strategy, self).OnStarted2(time)
self._roc = RateOfChange()
self._roc.Length = self.period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._roc, self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, self._roc)
self.DrawOwnTrades(area)
def process_candle(self, candle, roc_value):
if candle.State != CandleStates.Finished:
return
roc_value = float(roc_value)
g = float(self.gamma)
if self._is_first:
l0 = l1 = l2 = l3 = roc_value
self._is_first = False
else:
l0 = (1.0 - g) * roc_value + g * self._l0
l1 = -g * l0 + self._l0 + g * self._l1
l2 = -g * l1 + self._l1 + g * self._l2
l3 = -g * l2 + self._l2 + g * self._l3
cu = 0.0
cd = 0.0
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
denom = cu + cd
lroc = cu / denom if denom != 0 else 0.0
up = float(self.up_level)
down = float(self.down_level)
color = 2
if lroc > up:
color = 4
elif lroc > 0.5:
color = 3
if lroc < down:
color = 0
elif lroc < 0.5:
color = 1
if self._prev_color > 2 and color <= 2 and self.Position < 0:
self.BuyMarket()
if self._prev_color < 2 and color >= 2 and self.Position > 0:
self.SellMarket()
if self._prev_color == 4 and color < 4 and self.Position <= 0:
self.BuyMarket()
if self._prev_color == 0 and color > 0 and self.Position >= 0:
self.SellMarket()
self._prev_color = color
self._l0 = l0
self._l1 = l1
self._l2 = l2
self._l3 = l3
def CreateClone(self):
return laguerre_roc_strategy()