Estrategia Exp ADX Cross Hull Style
Esta estrategia combina señales de cruce del Average Directional Index (ADX) con un filtro de Hull Moving Average (HMA). Las entradas ocurren cuando la línea +DI cruza por encima de la línea -DI para posiciones largas o por debajo para posiciones cortas. Las salidas son gestionadas por un par de medias móviles Hull: la media rápida cruzando la media lenta cierra las posiciones. La estrategia opera en el marco temporal de 4 horas por defecto.
Detalles
- Criterios de entrada
- Largo: +DI cruza por encima de -DI.
- Corto: -DI cruza por encima de +DI.
- Criterios de salida
- Largo: HMA rápida cae por debajo de HMA lenta.
- Corto: HMA rápida sube por encima de HMA lenta.
- Indicadores
- AverageDirectionalIndex (período 14).
- HullMovingAverage longitud rápida 20.
- HullMovingAverage longitud lenta 50.
- Marco temporal: velas de 4 horas (configurable).
- Stops: ninguno por defecto.
- Dirección: largo y corto.
La estrategia no depende de colecciones históricas; reacciona a datos de velas en streaming. Los parámetros pueden optimizarse para diferentes mercados. La salida del gráfico muestra velas de precio con ambas medias móviles Hull y marcas de operaciones.
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 ADX directional cross and Hull moving averages.
/// Buys when +DI crosses above -DI and sells when -DI crosses above +DI.
/// </summary>
public class ExpAdxCrossHullStyleStrategy : Strategy
{
private readonly StrategyParam<int> _adxPeriod;
private readonly StrategyParam<int> _fastHullLength;
private readonly StrategyParam<int> _slowHullLength;
private readonly StrategyParam<DataType> _candleType;
private decimal? _prevPlusDi;
private decimal? _prevMinusDi;
public int AdxPeriod { get => _adxPeriod.Value; set => _adxPeriod.Value = value; }
public int FastHullLength { get => _fastHullLength.Value; set => _fastHullLength.Value = value; }
public int SlowHullLength { get => _slowHullLength.Value; set => _slowHullLength.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public ExpAdxCrossHullStyleStrategy()
{
_adxPeriod = Param(nameof(AdxPeriod), 14)
.SetDisplay("ADX Period", "Period for ADX calculation", "Indicators");
_fastHullLength = Param(nameof(FastHullLength), 20)
.SetDisplay("Fast Hull Length", "Period of the fast Hull MA", "Indicators");
_slowHullLength = Param(nameof(SlowHullLength), 50)
.SetDisplay("Slow Hull Length", "Period of the slow Hull MA", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles used by the strategy", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
protected override void OnReseted()
{
base.OnReseted();
_prevPlusDi = _prevMinusDi = null;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var adx = new AverageDirectionalIndex { Length = AdxPeriod };
var fastHull = new HullMovingAverage { Length = FastHullLength };
var slowHull = new HullMovingAverage { Length = SlowHullLength };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(adx, fastHull, slowHull, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fastHull);
DrawIndicator(area, slowHull);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue adxValue, IIndicatorValue fastHullValue, IIndicatorValue slowHullValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var adxVal = adxValue as IAverageDirectionalIndexValue;
if (adxVal?.Dx is not IDirectionalIndexValue dx)
return;
if (dx.Plus is not decimal plusDi || dx.Minus is not decimal minusDi)
return;
if (!fastHullValue.IsFormed || !slowHullValue.IsFormed)
return;
var fastHull = fastHullValue.GetValue<decimal>();
var slowHull = slowHullValue.GetValue<decimal>();
if (_prevPlusDi is decimal prevPlus && _prevMinusDi is decimal prevMinus)
{
// Entry: +DI crosses above -DI
if (prevPlus <= prevMinus && plusDi > minusDi && Position <= 0)
BuyMarket();
// Entry: -DI crosses above +DI
else if (prevPlus >= prevMinus && plusDi < minusDi && Position >= 0)
SellMarket();
// Exit on Hull MA cross
if (Position > 0 && fastHull < slowHull)
SellMarket();
else if (Position < 0 && fastHull > slowHull)
BuyMarket();
}
_prevPlusDi = plusDi;
_prevMinusDi = minusDi;
}
}
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 AverageDirectionalIndex, HullMovingAverage
from StockSharp.Algo.Strategies import Strategy
class exp_adx_cross_hull_style_strategy(Strategy):
def __init__(self):
super(exp_adx_cross_hull_style_strategy, self).__init__()
self._adx_period = self.Param("AdxPeriod", 14) \
.SetDisplay("ADX Period", "Period for ADX calculation", "Indicators")
self._fast_hull_length = self.Param("FastHullLength", 20) \
.SetDisplay("Fast Hull Length", "Period of the fast Hull MA", "Indicators")
self._slow_hull_length = self.Param("SlowHullLength", 50) \
.SetDisplay("Slow Hull Length", "Period of the slow Hull MA", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles used by the strategy", "General")
self._prev_plus_di = None
self._prev_minus_di = None
@property
def adx_period(self):
return self._adx_period.Value
@property
def fast_hull_length(self):
return self._fast_hull_length.Value
@property
def slow_hull_length(self):
return self._slow_hull_length.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(exp_adx_cross_hull_style_strategy, self).OnReseted()
self._prev_plus_di = None
self._prev_minus_di = None
def OnStarted2(self, time):
super(exp_adx_cross_hull_style_strategy, self).OnStarted2(time)
adx = AverageDirectionalIndex()
adx.Length = self.adx_period
fast_hull = HullMovingAverage()
fast_hull.Length = self.fast_hull_length
slow_hull = HullMovingAverage()
slow_hull.Length = self.slow_hull_length
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(adx, fast_hull, slow_hull, self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast_hull)
self.DrawIndicator(area, slow_hull)
self.DrawOwnTrades(area)
def process_candle(self, candle, adx_value, fast_hull_value, slow_hull_value):
if candle.State != CandleStates.Finished:
return
plus_di = adx_value.Dx.Plus
minus_di = adx_value.Dx.Minus
if plus_di is None or minus_di is None:
return
plus_di = float(plus_di)
minus_di = float(minus_di)
if not fast_hull_value.IsFormed or not slow_hull_value.IsFormed:
return
fast_hull = float(fast_hull_value)
slow_hull = float(slow_hull_value)
if self._prev_plus_di is not None and self._prev_minus_di is not None:
# Entry: +DI crosses above -DI
if self._prev_plus_di <= self._prev_minus_di and plus_di > minus_di and self.Position <= 0:
self.BuyMarket()
# Entry: -DI crosses above +DI
elif self._prev_plus_di >= self._prev_minus_di and plus_di < minus_di and self.Position >= 0:
self.SellMarket()
# Exit on Hull MA cross
if self.Position > 0 and fast_hull < slow_hull:
self.SellMarket()
elif self.Position < 0 and fast_hull > slow_hull:
self.BuyMarket()
self._prev_plus_di = plus_di
self._prev_minus_di = minus_di
def CreateClone(self):
return exp_adx_cross_hull_style_strategy()