Der Hull Moving Average reagiert schnell auf Preisänderungen und bleibt dabei glatt. Eine Änderung seiner Richtung kann eine kurzfristige Umkehr ankündigen. Diese Strategie überwacht aufeinanderfolgende Hull MA-Werte und handelt, wenn die Steigung wechselt.
Tests zeigen eine durchschnittliche Jahresrendite von etwa 154%. Am besten funktioniert die Strategie am Aktienmarkt.
Wenn der gleitende Durchschnitt von fallend auf steigend wechselt, wird eine Long-Position eröffnet. Ein Wechsel von steigend auf fallend initiiert einen Short. Das Risiko wird mit einem ATR-basierten Stop kontrolliert, der jenseits der letzten Kerze platziert wird.
Ausstiege verlassen sich auf diesen Schutz-Stop und erfassen einen Teil der Bewegung, die auf den von Hull MA hervorgehobenen Schwungwechsel folgt.
Details
Einstiegskriterien: Hull MA-Steigung ändert die Richtung.
Long/Short: Beide.
Ausstiegskriterien: Stop-Loss.
Stops: Ja, ATR-basiert.
Standardwerte:
HmaPeriod = 9
AtrMultiplier = 2 ATR
Der Stop liegt so viele ATR (Periode 14) jenseits des Tiefs der Einstiegskerze bei Long bzw. ihres Hochs bei Short und wird bei Kerzenschluss geprüft; eine Wende in die Gegenrichtung dreht die Position.
CandleType = 15 minute
Filter:
Kategorie: Trendfolge
Richtung: Beide
Indikatoren: Hull MA, ATR
Stops: Ja
Komplexität: Grundlegend
Zeitrahmen: Intraday
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 Reversal strategy.
/// When the Hull MA turns from falling to rising the position turns long, and when it turns from rising to falling it turns short.
/// The stop lies AtrMultiplier ATRs beyond the entry candle's low (for a long) or high (for a short).
/// </summary>
public class HullMaReversalStrategy : Strategy
{
/// <summary>
/// Period of the ATR that sizes the stop.
/// </summary>
public const int AtrPeriod = 14;
private readonly StrategyParam<int> _hmaPeriod;
private readonly StrategyParam<decimal> _atrMultiplier;
private readonly StrategyParam<DataType> _candleType;
private decimal? _prevHma;
// Direction of the latest move of the Hull MA: 1 rising, -1 falling, 0 none yet.
private int _slope;
private decimal _stopPrice;
/// <summary>
/// Hull MA period.
/// </summary>
public int HmaPeriod
{
get => _hmaPeriod.Value;
set => _hmaPeriod.Value = value;
}
/// <summary>
/// Distance of the stop beyond the entry candle in ATR multiples.
/// </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>
/// Constructor.
/// </summary>
public HullMaReversalStrategy()
{
_hmaPeriod = Param(nameof(HmaPeriod), 9)
.SetGreaterThanZero()
.SetDisplay("HMA Period", "Period for Hull Moving Average", "Indicators");
_atrMultiplier = Param(nameof(AtrMultiplier), 2m)
.SetGreaterThanZero()
.SetDisplay("ATR Multiplier", "Distance of the stop beyond the entry candle in ATR multiples", "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();
_prevHma = null;
_slope = 0;
_stopPrice = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevHma = null;
_slope = 0;
_stopPrice = default;
var hma = new HullMovingAverage { Length = HmaPeriod };
var atr = new AverageTrueRange { Length = AtrPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(hma, atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, hma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue hmaValue, IIndicatorValue atrValue)
{
if (candle.State != CandleStates.Finished || !hmaValue.IsFormed || !atrValue.IsFormed)
return;
var hma = hmaValue.GetValue<decimal>();
var prevHma = _prevHma;
_prevHma = hma;
if (prevHma is not decimal lastHma)
return;
var previousSlope = _slope;
var slope = hma > lastHma ? 1 : hma < lastHma ? -1 : 0;
if (slope != 0)
_slope = slope;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var close = candle.ClosePrice;
var distance = AtrMultiplier * atrValue.GetValue<decimal>();
if (previousSlope == -1 && slope == 1 && Position <= 0)
{
BuyMarket(Volume + Math.Abs(Position));
_stopPrice = candle.LowPrice - distance;
}
else if (previousSlope == 1 && slope == -1 && Position >= 0)
{
SellMarket(Volume + Math.Abs(Position));
_stopPrice = candle.HighPrice + distance;
}
else if (Position > 0 && close <= _stopPrice)
{
SellMarket(Position);
}
else if (Position < 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, Math, Decimal
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import HullMovingAverage, AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
# Period of the ATR that sizes the stop.
ATR_PERIOD = 14
class hull_ma_reversal_strategy(Strategy):
"""
Hull MA Reversal strategy.
When the Hull MA turns from falling to rising the position turns long, and when it turns from rising to falling it turns short.
The stop lies AtrMultiplier ATRs beyond the entry candle's low (for a long) or high (for a short).
"""
def __init__(self):
super(hull_ma_reversal_strategy, self).__init__()
self._hma_period = self.Param("HmaPeriod", 9).SetGreaterThanZero().SetDisplay("HMA Period", "Period for Hull Moving Average", "Indicators")
self._atr_multiplier = self.Param("AtrMultiplier", 2.0).SetGreaterThanZero().SetDisplay("ATR Multiplier", "Distance of the stop beyond the entry candle in ATR multiples", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._prev_hma = None
# Direction of the latest move of the Hull MA: 1 rising, -1 falling, 0 none yet.
self._slope = 0
self._stop_price = Decimal(0)
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(hull_ma_reversal_strategy, self).OnReseted()
self._prev_hma = None
self._slope = 0
self._stop_price = Decimal(0)
def OnStarted2(self, time):
super(hull_ma_reversal_strategy, self).OnStarted2(time)
self._prev_hma = None
self._slope = 0
self._stop_price = Decimal(0)
hma = HullMovingAverage()
hma.Length = self._hma_period.Value
atr = AverageTrueRange()
atr.Length = ATR_PERIOD
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(hma, atr, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, hma)
self.DrawOwnTrades(area)
def _process_candle(self, candle, hma_value, atr_value):
if candle.State != CandleStates.Finished or not hma_value.IsFormed or not atr_value.IsFormed:
return
hma = hma_value.GetValue[Decimal](None)
prev_hma = self._prev_hma
self._prev_hma = hma
if prev_hma is None:
return
previous_slope = self._slope
slope = 1 if hma > prev_hma else (-1 if hma < prev_hma else 0)
if slope != 0:
self._slope = slope
if not self.IsFormedAndOnlineAndAllowTrading():
return
close = candle.ClosePrice
distance = Decimal(self._atr_multiplier.Value) * atr_value.GetValue[Decimal](None)
if previous_slope == -1 and slope == 1 and self.Position <= 0:
self.BuyMarket(self.Volume + Math.Abs(self.Position))
self._stop_price = candle.LowPrice - distance
elif previous_slope == 1 and slope == -1 and self.Position >= 0:
self.SellMarket(self.Volume + Math.Abs(self.Position))
self._stop_price = candle.HighPrice + distance
elif self.Position > 0 and close <= self._stop_price:
self.SellMarket(self.Position)
elif self.Position < 0 and close >= self._stop_price:
self.BuyMarket(-self.Position)
def CreateClone(self):
return hull_ma_reversal_strategy()