Tendencia Hull MA
Estrategia basada en la tendencia de la Hull Moving Average.
Las pruebas indican un retorno anual promedio de aproximadamente 61%. Funciona mejor en el mercado de criptomonedas.
La estrategia de Tendencia Hull MA monitorea la pendiente de la Hull Moving Average. Las pendientes ascendentes provocan largos y las pendientes descendentes provocan cortos, con un stop trailing de ATR protegiendo cada operación.
Su cálculo receptivo reduce el rezago en comparación con las medias móviles tradicionales, permitiendo al sistema reaccionar rápidamente al nuevo momentum. El stop de ATR ayuda a evitar grandes drawdowns si la pendiente cambia abruptamente.
Detalles
- Criterios de entrada: Señales basadas en MA, ATR.
- Largo/Corto: Ambos directions.
- Criterios de salida: Señal opuesta o stop.
- Stops: Sí.
- Valores predeterminados:
HmaPeriod= 9AtrPeriod= 14AtrMultiplier= 2mCandleType= TimeSpan.FromMinutes(5)
- Filtros:
- Categoría: Tendencia
- Dirección: Ambos
- Indicadores: MA, ATR
- Stops: Sí
- Complejidad: Básico
- Marco temporal: Intradía (5m)
- Estacionalidad: No
- Redes neuronales: No
- Divergencia: No
- Nivel de riesgo: Medio
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 Hull Moving Average trend.
/// It turns long when the Hull MA starts rising and short when it starts falling,
/// and protects the position with a stop that trails the close by AtrMultiplier ATRs.
/// </summary>
public class HullMaTrendStrategy : Strategy
{
private readonly StrategyParam<int> _hmaPeriod;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<decimal> _atrMultiplier;
private readonly StrategyParam<DataType> _candleType;
private decimal? _prevHmaValue;
// Direction of the last slope change: 1 rising, -1 falling, 0 none yet.
private int _slope;
private decimal _stopPrice;
/// <summary>
/// Period for Hull Moving Average.
/// </summary>
public int HmaPeriod
{
get => _hmaPeriod.Value;
set => _hmaPeriod.Value = value;
}
/// <summary>
/// Period for ATR calculation (stop-loss).
/// </summary>
public int AtrPeriod
{
get => _atrPeriod.Value;
set => _atrPeriod.Value = value;
}
/// <summary>
/// Multiplier for ATR to determine stop-loss distance.
/// </summary>
public decimal AtrMultiplier
{
get => _atrMultiplier.Value;
set => _atrMultiplier.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initialize the Hull MA Trend strategy.
/// </summary>
public HullMaTrendStrategy()
{
_hmaPeriod = Param(nameof(HmaPeriod), 9)
.SetGreaterThanZero()
.SetDisplay("HMA Period", "Period for Hull Moving Average", "Indicators")
.SetOptimize(5, 15, 2);
_atrPeriod = Param(nameof(AtrPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("ATR Period", "Period for Average True Range (stop-loss)", "Risk parameters")
.SetOptimize(10, 20, 2);
_atrMultiplier = Param(nameof(AtrMultiplier), 2m)
.SetGreaterThanZero()
.SetDisplay("ATR Multiplier", "Distance of the trailing stop in ATR multiples", "Risk parameters")
.SetOptimize(1, 3, 0.5m);
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevHmaValue = null;
_slope = 0;
_stopPrice = 0m;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
// Create indicators
var hma = new HullMovingAverage { Length = HmaPeriod };
var atr = new AverageTrueRange { Length = AtrPeriod };
// Create subscription and bind indicators
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(hma, atr, ProcessCandle)
.Start();
// Setup chart visualization if available
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, hma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue hmaIndicatorValue, IIndicatorValue atrIndicatorValue)
{
if (candle.State != CandleStates.Finished || !hmaIndicatorValue.IsFormed || !atrIndicatorValue.IsFormed)
return;
var hmaValue = hmaIndicatorValue.GetValue<decimal>();
var atrValue = atrIndicatorValue.GetValue<decimal>();
var previous = _prevHmaValue;
_prevHmaValue = hmaValue;
if (previous is not decimal prevHma || !IsFormedAndOnlineAndAllowTrading())
return;
var close = candle.ClosePrice;
var distance = AtrMultiplier * atrValue;
var slope = hmaValue > prevHma ? 1 : hmaValue < prevHma ? -1 : 0;
// Only a change of the slope direction is a new signal, so a stopped trade is not reopened at once.
if (slope != 0 && slope != _slope)
{
_slope = slope;
if (slope > 0 && Position <= 0)
{
BuyMarket(Volume + Math.Abs(Position));
_stopPrice = close - distance;
return;
}
if (slope < 0 && Position >= 0)
{
SellMarket(Volume + Math.Abs(Position));
_stopPrice = close + distance;
return;
}
}
if (Position > 0)
{
if (close <= _stopPrice)
SellMarket(Position);
else
_stopPrice = Math.Max(_stopPrice, close - distance);
}
else if (Position < 0)
{
if (close >= _stopPrice)
BuyMarket(-Position);
else
_stopPrice = Math.Min(_stopPrice, close + distance);
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math, Decimal
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import HullMovingAverage, AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class hull_ma_trend_strategy(Strategy):
"""
Strategy based on Hull Moving Average trend.
It turns long when the Hull MA starts rising and short when it starts falling,
and protects the position with a stop that trails the close by AtrMultiplier ATRs.
"""
def __init__(self):
super(hull_ma_trend_strategy, self).__init__()
self._hma_period = self.Param("HmaPeriod", 9) \
.SetGreaterThanZero() \
.SetDisplay("HMA Period", "Period for Hull Moving Average", "Indicators")
self._atr_period = self.Param("AtrPeriod", 14) \
.SetGreaterThanZero() \
.SetDisplay("ATR Period", "Period for Average True Range (stop-loss)", "Risk parameters")
self._atr_multiplier = self.Param("AtrMultiplier", 2.0) \
.SetGreaterThanZero() \
.SetDisplay("ATR Multiplier", "Distance of the trailing stop in ATR multiples", "Risk parameters")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._prev_hma_value = None
# Direction of the last slope change: 1 rising, -1 falling, 0 none yet.
self._slope = 0
self._stop_price = Decimal(0)
@property
def HmaPeriod(self):
return self._hma_period.Value
@HmaPeriod.setter
def HmaPeriod(self, value):
self._hma_period.Value = value
@property
def AtrPeriod(self):
return self._atr_period.Value
@AtrPeriod.setter
def AtrPeriod(self, value):
self._atr_period.Value = value
@property
def AtrMultiplier(self):
return self._atr_multiplier.Value
@AtrMultiplier.setter
def AtrMultiplier(self, value):
self._atr_multiplier.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnStarted2(self, time):
super(hull_ma_trend_strategy, self).OnStarted2(time)
hma = HullMovingAverage()
hma.Length = self.HmaPeriod
atr = AverageTrueRange()
atr.Length = self.AtrPeriod
self.SubscribeCandles(self.CandleType) \
.BindEx(hma, atr, self.ProcessCandle) \
.Start()
def ProcessCandle(self, candle, hma_indicator_value, atr_indicator_value):
if candle.State != CandleStates.Finished or not hma_indicator_value.IsFormed or not atr_indicator_value.IsFormed:
return
hma_value = hma_indicator_value.GetValue[Decimal](None)
atr_value = atr_indicator_value.GetValue[Decimal](None)
previous = self._prev_hma_value
self._prev_hma_value = hma_value
if previous is None or not self.IsFormedAndOnlineAndAllowTrading():
return
close = candle.ClosePrice
distance = Decimal(self.AtrMultiplier) * atr_value
slope = 1 if hma_value > previous else (-1 if hma_value < previous else 0)
# Only a change of the slope direction is a new signal, so a stopped trade is not reopened at once.
if slope != 0 and slope != self._slope:
self._slope = slope
if slope > 0 and self.Position <= 0:
self.BuyMarket(self.Volume + Math.Abs(self.Position))
self._stop_price = close - distance
return
if slope < 0 and self.Position >= 0:
self.SellMarket(self.Volume + Math.Abs(self.Position))
self._stop_price = close + distance
return
if self.Position > 0:
if close <= self._stop_price:
self.SellMarket(self.Position)
else:
self._stop_price = Math.Max(self._stop_price, close - distance)
elif self.Position < 0:
if close >= self._stop_price:
self.BuyMarket(-self.Position)
else:
self._stop_price = Math.Min(self._stop_price, close + distance)
def OnReseted(self):
super(hull_ma_trend_strategy, self).OnReseted()
self._prev_hma_value = None
self._slope = 0
self._stop_price = Decimal(0)
def CreateClone(self):
return hull_ma_trend_strategy()