Estrategia Laguerre ADX
Esta estrategia aplica un filtro Laguerre a los componentes +DI y -DI del indicador Average Directional Index (ADX). El suavizado reduce el ruido en el movimiento direccional y destaca los cambios repentinos en el dominio entre compradores y vendedores. Cuando el +DI suavizado por Laguerre cruza por debajo del -DI suavizado, el sistema entra en una posición larga, esperando una reversión alcista. Por el contrario, cuando el +DI suavizado cruza por encima del -DI suavizado, el sistema abre una posición corta.
Las posiciones se cierran cuando los valores suavizados actuales indican que el lado opuesto ha tomado el control. El método está diseñado como un enfoque contratendencia, desvaneciendo los extremos de corto plazo en el índice direccional.
Detalles
- Criterios de entrada:
- Largo: Laguerre +DI cruza por debajo de Laguerre –DI.
- Corto: Laguerre +DI cruza por encima de Laguerre –DI.
- Largo/Corto: Ambos lados.
- Criterios de salida:
- Largo: Laguerre –DI se mueve por encima de Laguerre +DI.
- Corto: Laguerre +DI se mueve por encima de Laguerre –DI.
- Stops: Sin stops fijos, solo protección de posición predeterminada.
- Valores predeterminados:
ADX Period= 14.Gamma= 0.764 (factor de suavizado Laguerre).Candle Type= marco temporal de 4 horas.
- Filtros:
- Categoría: Contratendencia
- Dirección: Ambos
- Indicadores: ADX
- Stops: No
- Complejidad: Medio
- Marco temporal: Medio plazo
- Estacionalidad: No
- Redes neuronales: No
- Divergencia: Sí
- Nivel de riesgo: Medio
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy that uses Laguerre-filtered DI+ and DI- to trade directional shifts.
/// </summary>
public class LaguerreAdxStrategy : Strategy
{
private readonly StrategyParam<int> _adxPeriod;
private readonly StrategyParam<decimal> _gamma;
private readonly StrategyParam<decimal> _stopLossPct;
private readonly StrategyParam<decimal> _takeProfitPct;
private readonly StrategyParam<DataType> _candleType;
private AverageDirectionalIndex _adx;
private decimal _prevUp;
private decimal _prevDown;
private decimal _l0Up, _l1Up, _l2Up, _l3Up;
private decimal _l0Down, _l1Down, _l2Down, _l3Down;
private bool _isInitialized;
public int AdxPeriod { get => _adxPeriod.Value; set => _adxPeriod.Value = value; }
public decimal Gamma { get => _gamma.Value; set => _gamma.Value = value; }
public decimal StopLossPct { get => _stopLossPct.Value; set => _stopLossPct.Value = value; }
public decimal TakeProfitPct { get => _takeProfitPct.Value; set => _takeProfitPct.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public LaguerreAdxStrategy()
{
_adxPeriod = Param(nameof(AdxPeriod), 14)
.SetDisplay("ADX Period", "Period for ADX calculation", "Indicators");
_gamma = Param(nameof(Gamma), 0.764m)
.SetDisplay("Gamma", "Laguerre smoothing factor", "Indicators");
_stopLossPct = Param(nameof(StopLossPct), 2m)
.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk");
_takeProfitPct = Param(nameof(TakeProfitPct), 3m)
.SetDisplay("Take Profit %", "Take profit percentage", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for candles", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_adx = default;
_prevUp = _prevDown = 0;
_l0Up = _l1Up = _l2Up = _l3Up = 0;
_l0Down = _l1Down = _l2Down = _l3Down = 0;
_isInitialized = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_adx = new AverageDirectionalIndex { Length = AdxPeriod };
Indicators.Add(_adx);
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
StartProtection(
takeProfit: new Unit(TakeProfitPct, UnitTypes.Percent),
stopLoss: new Unit(StopLossPct, UnitTypes.Percent),
useMarketOrders: true);
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var adxResult = _adx.Process(candle);
if (!adxResult.IsFormed)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var adxVal = (AverageDirectionalIndexValue)adxResult;
var plus = adxVal.Dx.Plus ?? 0m;
var minus = adxVal.Dx.Minus ?? 0m;
var up = LaguerreRsi(plus, ref _l0Up, ref _l1Up, ref _l2Up, ref _l3Up);
var down = LaguerreRsi(minus, ref _l0Down, ref _l1Down, ref _l2Down, ref _l3Down);
if (!_isInitialized)
{
_prevUp = up;
_prevDown = down;
_isInitialized = true;
return;
}
// Crossover signals
if (_prevUp <= _prevDown && up > down && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
}
else if (_prevUp >= _prevDown && up < down && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
}
_prevUp = up;
_prevDown = down;
}
private decimal LaguerreRsi(decimal value, ref decimal l0, ref decimal l1, ref decimal l2, ref decimal l3)
{
var l0Prev = l0;
var l1Prev = l1;
var l2Prev = l2;
l0 = (1m - Gamma) * value + Gamma * l0;
l1 = -Gamma * l0 + l0Prev + Gamma * l1;
l2 = -Gamma * l1 + l1Prev + Gamma * l2;
l3 = -Gamma * l2 + l2Prev + Gamma * l3;
decimal cu = 0, cd = 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;
return cu + cd == 0 ? 0 : cu / (cu + cd);
}
}
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, Unit, UnitTypes
from StockSharp.Algo.Indicators import AverageDirectionalIndex, CandleIndicatorValue
from StockSharp.Algo.Strategies import Strategy
class laguerre_adx_strategy(Strategy):
def __init__(self):
super(laguerre_adx_strategy, self).__init__()
self._adx_period = self.Param("AdxPeriod", 14) \
.SetDisplay("ADX Period", "Period for ADX calculation", "Indicators")
self._gamma = self.Param("Gamma", 0.764) \
.SetDisplay("Gamma", "Laguerre smoothing factor", "Indicators")
self._stop_loss_pct = self.Param("StopLossPct", 2.0) \
.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk")
self._take_profit_pct = self.Param("TakeProfitPct", 3.0) \
.SetDisplay("Take Profit %", "Take profit percentage", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe for candles", "General")
self._adx = None
self._prev_up = 0.0
self._prev_down = 0.0
self._l0_up = 0.0
self._l1_up = 0.0
self._l2_up = 0.0
self._l3_up = 0.0
self._l0_down = 0.0
self._l1_down = 0.0
self._l2_down = 0.0
self._l3_down = 0.0
self._is_initialized = False
@property
def adx_period(self):
return self._adx_period.Value
@property
def gamma(self):
return self._gamma.Value
@property
def stop_loss_pct(self):
return self._stop_loss_pct.Value
@property
def take_profit_pct(self):
return self._take_profit_pct.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(laguerre_adx_strategy, self).OnReseted()
self._adx = None
self._prev_up = 0.0
self._prev_down = 0.0
self._l0_up = 0.0
self._l1_up = 0.0
self._l2_up = 0.0
self._l3_up = 0.0
self._l0_down = 0.0
self._l1_down = 0.0
self._l2_down = 0.0
self._l3_down = 0.0
self._is_initialized = False
def OnStarted2(self, time):
super(laguerre_adx_strategy, self).OnStarted2(time)
self._adx = AverageDirectionalIndex()
self._adx.Length = self.adx_period
self.Indicators.Add(self._adx)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self.process_candle).Start()
self.StartProtection(
takeProfit=Unit(self.take_profit_pct, UnitTypes.Percent),
stopLoss=Unit(self.stop_loss_pct, UnitTypes.Percent),
useMarketOrders=True)
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _laguerre_rsi(self, value, l_state):
g = float(self.gamma)
l0_prev = l_state[0]
l1_prev = l_state[1]
l2_prev = l_state[2]
l_state[0] = (1.0 - g) * value + g * l_state[0]
l_state[1] = -g * l_state[0] + l0_prev + g * l_state[1]
l_state[2] = -g * l_state[1] + l1_prev + g * l_state[2]
l_state[3] = -g * l_state[2] + l2_prev + g * l_state[3]
cu = 0.0
cd = 0.0
if l_state[0] >= l_state[1]:
cu = l_state[0] - l_state[1]
else:
cd = l_state[1] - l_state[0]
if l_state[1] >= l_state[2]:
cu += l_state[1] - l_state[2]
else:
cd += l_state[2] - l_state[1]
if l_state[2] >= l_state[3]:
cu += l_state[2] - l_state[3]
else:
cd += l_state[3] - l_state[2]
return 0.0 if cu + cd == 0 else cu / (cu + cd)
def process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
cv = CandleIndicatorValue(self._adx, candle)
adx_result = self._adx.Process(cv)
if not adx_result.IsFormed:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
plus = adx_result.Dx.Plus
minus = adx_result.Dx.Minus
plus = float(plus) if plus is not None else 0.0
minus = float(minus) if minus is not None else 0.0
up_state = [self._l0_up, self._l1_up, self._l2_up, self._l3_up]
up = self._laguerre_rsi(plus, up_state)
self._l0_up, self._l1_up, self._l2_up, self._l3_up = up_state
down_state = [self._l0_down, self._l1_down, self._l2_down, self._l3_down]
down = self._laguerre_rsi(minus, down_state)
self._l0_down, self._l1_down, self._l2_down, self._l3_down = down_state
if not self._is_initialized:
self._prev_up = up
self._prev_down = down
self._is_initialized = True
return
if self._prev_up <= self._prev_down and up > down and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif self._prev_up >= self._prev_down and up < down and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_up = up
self._prev_down = down
def CreateClone(self):
return laguerre_adx_strategy()