Strategie Hull Ma Adx
Strategie basierend auf Hull Moving Average und ADX. Einstieg Long, wenn HMA steigt und ADX > 25 (starker Trend). Einstieg Short, wenn HMA fällt und ADX > 25 (starker Trend). Ausstieg, wenn ADX < 20 (abschwächender Trend).
Tests zeigen eine durchschnittliche Jahresrendite von etwa 178%. Die Strategie funktioniert am besten am Aktienmarkt.
Hull MA zeigt den Trend, während ADX dessen Intensität bestätigt. Einstiege folgen dem Hull-Gefälle, wenn ADX Stärke anzeigt.
Effektiv für Trader, die sich auf gleichmäßige Trends mit Bestätigung konzentrieren. ATR-Stops halten Verluste unter Kontrolle.
Details
- Einstiegskriterien:
- Long:
HullMA turning up && ADX > 25 - Short:
HullMA turning down && ADX > 25
- Long:
- Long/Short: Beide
- Ausstiegskriterien: Hull MA Umkehr
- Stops: ATR-basiert mit
AtrMultiplier - Standardwerte:
HmaPeriod= 9AdxPeriod= 14AdxThreshold= 25AdxExitThreshold= 20AtrMultiplier= 2AtrPeriod= 14CandleType= TimeSpan.FromMinutes(15).TimeFrame() Die Werte 25 und 20 in den Regeln sind die Standardwerte von AdxThreshold und AdxExitThreshold. Der HMA dreht nach oben, wenn er nach einem Rückgang steigt, und nach unten, wenn er nach einem Anstieg fällt; ein Long schließt, wenn er fällt, ein Short, wenn er steigt, und jede Position schließt, sobald ADX unter AdxExitThreshold fällt. Der Stop liegt AtrMultiplier ATR (AtrPeriod) vom Einstiegsschluss entfernt und wird auf Kerzenschlüssen geprüft. Ein Einstiegssignal gegen eine offene Position dreht sie.
- Filter:
- Kategorie: Trend
- Richtung: Beide
- Indikatoren: Hull MA, Moving Average, ADX
- Stops: Ja
- Komplexität: Mittel
- Zeitrahmen: Mittelfristig
- Saisonalität: Nein
- Neuronale Netze: Nein
- Divergenz: Nein
- 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>
/// Hull MA ADX strategy.
/// The Hull average turns up when it rises after falling and turns down when it falls after rising. While ADX is above AdxThreshold, a turn up
/// goes long and a turn down goes short, reversing an opposite position. A long closes when the Hull average falls, a short when it rises,
/// and either closes once ADX drops below AdxExitThreshold. The stop lies AtrMultiplier ATR from the entry close and is checked on candle closes.
/// </summary>
public class HullMaAdxStrategy : Strategy
{
private readonly StrategyParam<int> _hmaPeriod;
private readonly StrategyParam<int> _adxPeriod;
private readonly StrategyParam<decimal> _adxThreshold;
private readonly StrategyParam<decimal> _adxExitThreshold;
private readonly StrategyParam<decimal> _atrMultiplier;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal? _prevHull;
private decimal? _prevPrevHull;
private decimal _stopPrice;
/// <summary>
/// Period of the Hull moving average.
/// </summary>
public int HmaPeriod
{
get => _hmaPeriod.Value;
set => _hmaPeriod.Value = value;
}
/// <summary>
/// Period of ADX.
/// </summary>
public int AdxPeriod
{
get => _adxPeriod.Value;
set => _adxPeriod.Value = value;
}
/// <summary>
/// ADX level required to enter.
/// </summary>
public decimal AdxThreshold
{
get => _adxThreshold.Value;
set => _adxThreshold.Value = value;
}
/// <summary>
/// ADX level below which the position closes.
/// </summary>
public decimal AdxExitThreshold
{
get => _adxExitThreshold.Value;
set => _adxExitThreshold.Value = value;
}
/// <summary>
/// Stop distance from the entry in ATRs.
/// </summary>
public decimal AtrMultiplier
{
get => _atrMultiplier.Value;
set => _atrMultiplier.Value = value;
}
/// <summary>
/// Period of the stop ATR.
/// </summary>
public int AtrPeriod
{
get => _atrPeriod.Value;
set => _atrPeriod.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public HullMaAdxStrategy()
{
_hmaPeriod = Param(nameof(HmaPeriod), 9)
.SetGreaterThanZero()
.SetDisplay("HMA Period", "Period of the Hull moving average", "Indicators");
_adxPeriod = Param(nameof(AdxPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("ADX Period", "Period of ADX", "Indicators");
_adxThreshold = Param(nameof(AdxThreshold), 25m)
.SetDisplay("ADX Threshold", "ADX level required to enter", "Indicators");
_adxExitThreshold = Param(nameof(AdxExitThreshold), 20m)
.SetDisplay("ADX Exit Threshold", "ADX level below which the position closes", "Indicators");
_atrMultiplier = Param(nameof(AtrMultiplier), 2m)
.SetNotNegative()
.SetDisplay("ATR Multiplier", "Stop distance from the entry in ATRs", "Risk");
_atrPeriod = Param(nameof(AtrPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("ATR Period", "Period of the stop ATR", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).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();
_prevHull = null;
_prevPrevHull = null;
_stopPrice = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevHull = null;
_prevPrevHull = null;
_stopPrice = default;
var hull = new HullMovingAverage { Length = HmaPeriod };
var adx = new AverageDirectionalIndex { Length = AdxPeriod };
var atr = new AverageTrueRange { Length = AtrPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(hull, adx, atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, hull);
DrawOwnTrades(area);
var oscillators = CreateChartArea();
if (oscillators != null)
{
DrawIndicator(oscillators, adx);
}
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue hullValue, IIndicatorValue adxValue, IIndicatorValue atrValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!hullValue.IsFormed || !adxValue.IsFormed || !atrValue.IsFormed)
return;
if (adxValue is not AverageDirectionalIndexValue { MovingAverage: decimal strength })
return;
var hull = hullValue.GetValue<decimal>();
var prevHull = _prevHull;
var prevPrevHull = _prevPrevHull;
_prevPrevHull = prevHull;
_prevHull = hull;
if (prevHull is not decimal last || prevPrevHull is not decimal beforeLast)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var atr = atrValue.GetValue<decimal>();
var close = candle.ClosePrice;
var rising = hull > last;
var falling = hull < last;
var turnsUp = rising && last < beforeLast;
var turnsDown = falling && last > beforeLast;
var strong = strength > AdxThreshold;
var weak = strength < AdxExitThreshold;
if (turnsUp && strong && Position <= 0)
{
BuyMarket(Volume + Math.Abs(Position));
_stopPrice = close - AtrMultiplier * atr;
}
else if (turnsDown && strong && Position >= 0)
{
SellMarket(Volume + Math.Abs(Position));
_stopPrice = close + AtrMultiplier * atr;
}
else if (Position > 0 && (falling || weak || (AtrMultiplier > 0 && close <= _stopPrice)))
{
SellMarket(Position);
}
else if (Position < 0 && (rising || weak || (AtrMultiplier > 0 && close >= _stopPrice)))
{
BuyMarket(-Position);
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import HullMovingAverage, AverageDirectionalIndex, AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class hull_ma_adx_strategy(Strategy):
"""
Hull MA ADX strategy.
The Hull average turns up when it rises after falling and turns down when it falls after rising. While ADX is above AdxThreshold, a turn up
goes long and a turn down goes short, reversing an opposite position. A long closes when the Hull average falls, a short when it rises,
and either closes once ADX drops below AdxExitThreshold. The stop lies AtrMultiplier ATR from the entry close and is checked on candle closes.
"""
def __init__(self):
super(hull_ma_adx_strategy, self).__init__()
self._hma_period = self.Param("HmaPeriod", 9).SetGreaterThanZero().SetDisplay("HMA Period", "Period of the Hull moving average", "Indicators")
self._adx_period = self.Param("AdxPeriod", 14).SetGreaterThanZero().SetDisplay("ADX Period", "Period of ADX", "Indicators")
self._adx_threshold = self.Param("AdxThreshold", 25.0).SetDisplay("ADX Threshold", "ADX level required to enter", "Indicators")
self._adx_exit_threshold = self.Param("AdxExitThreshold", 20.0).SetDisplay("ADX Exit Threshold", "ADX level below which the position closes", "Indicators")
self._atr_multiplier = self.Param("AtrMultiplier", 2.0).SetNotNegative().SetDisplay("ATR Multiplier", "Stop distance from the entry in ATRs", "Risk")
self._atr_period = self.Param("AtrPeriod", 14).SetGreaterThanZero().SetDisplay("ATR Period", "Period of the stop ATR", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._reset_state()
@property
def candle_type(self):
return self._candle_type.Value
def _reset_state(self):
self._prev_hull = None
self._prev_prev_hull = None
self._stop_price = Decimal(0)
def OnReseted(self):
super(hull_ma_adx_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(hull_ma_adx_strategy, self).OnStarted2(time)
self._reset_state()
hull = HullMovingAverage()
hull.Length = self._hma_period.Value
adx = AverageDirectionalIndex()
adx.Length = self._adx_period.Value
atr = AverageTrueRange()
atr.Length = self._atr_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(hull, adx, atr, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, hull)
self.DrawOwnTrades(area)
oscillators = self.CreateChartArea()
if oscillators is not None:
self.DrawIndicator(oscillators, adx)
def _process_candle(self, candle, hull_value, adx_value, atr_value):
if candle.State != CandleStates.Finished:
return
if not hull_value.IsFormed or not adx_value.IsFormed or not atr_value.IsFormed or adx_value.MovingAverage is None:
return
hull = hull_value.GetValue[Decimal](None)
prev_hull = self._prev_hull
prev_prev_hull = self._prev_prev_hull
self._prev_prev_hull = prev_hull
self._prev_hull = hull
if prev_hull is None or prev_prev_hull is None:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
strength = adx_value.MovingAverage
atr = atr_value.GetValue[Decimal](None)
close = candle.ClosePrice
rising = hull > prev_hull
falling = hull < prev_hull
turns_up = rising and prev_hull < prev_prev_hull
turns_down = falling and prev_hull > prev_prev_hull
strong = strength > Decimal(self._adx_threshold.Value)
weak = strength < Decimal(self._adx_exit_threshold.Value)
stop_atr = Decimal(self._atr_multiplier.Value)
if turns_up and strong and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
self._stop_price = close - stop_atr * atr
elif turns_down and strong and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
self._stop_price = close + stop_atr * atr
elif self.Position > 0 and (falling or weak or (stop_atr > 0 and close <= self._stop_price)):
self.SellMarket(self.Position)
elif self.Position < 0 and (rising or weak or (stop_atr > 0 and close >= self._stop_price)):
self.BuyMarket(-self.Position)
def CreateClone(self):
return hull_ma_adx_strategy()