Laguerre ADX Strategie
Diese Strategie wendet einen Laguerre-Filter auf die +DI- und -DI-Komponenten des Average Directional Index (ADX) Indikators an. Die Glättung reduziert Rauschen in der Richtungsbewegung und hebt plötzliche Verschiebungen in der Dominanz zwischen Käufern und Verkäufern hervor. Wenn der Laguerre-geglättete +DI unter den geglätteten -DI kreuzt, tritt das System in eine Long-Position ein und erwartet eine bullische Umkehr. Umgekehrt öffnet das System eine Short-Position, wenn der geglättete +DI über den geglätteten -DI kreuzt.
Positionen werden geschlossen, wenn die aktuellen geglätteten Werte anzeigen, dass die entgegengesetzte Seite die Kontrolle übernommen hat. Die Methode ist als konträrer Ansatz konzipiert und nutzt kurzfristige Extreme im Richtungsindex.
Details
- Einstiegskriterien:
- Long: Laguerre +DI kreuzt unter Laguerre –DI.
- Short: Laguerre +DI kreuzt über Laguerre –DI.
- Long/Short: Beide Seiten.
- Ausstiegskriterien:
- Long: Laguerre –DI bewegt sich über Laguerre +DI.
- Short: Laguerre +DI bewegt sich über Laguerre –DI.
- Stops: Keine festen Stops, nur Standard-Positionsschutz.
- Standardwerte:
ADX Period= 14.Gamma= 0.764 (Laguerre-Glättungsfaktor).Candle Type= 4-Stunden-Zeitrahmen.
- Filter:
- Kategorie: Gegentrend
- Richtung: Beide
- Indikatoren: ADX
- Stops: Nein
- Komplexität: Mittel
- Zeitrahmen: Mittelfristig
- Saisonalität: Nein
- Neuronale Netze: Nein
- Divergenz: Ja
- Risikolevel: Mittel
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()