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
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.
/// Enters long when Hull MA changes direction from down to up.
/// Enters short when Hull MA changes direction from up to down.
/// Uses cooldown to control trade frequency.
/// </summary>
public class HullMaReversalStrategy : Strategy
{
private readonly StrategyParam<int> _hmaPeriod;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _cooldownBars;
private decimal _prevHma;
private decimal _prevPrevHma;
private int _cooldown;
/// <summary>
/// HMA period.
/// </summary>
public int HmaPeriod
{
get => _hmaPeriod.Value;
set => _hmaPeriod.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Cooldown bars.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public HullMaReversalStrategy()
{
_hmaPeriod = Param(nameof(HmaPeriod), 9)
.SetRange(5, 20)
.SetDisplay("HMA Period", "Period for Hull Moving Average", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_cooldownBars = Param(nameof(CooldownBars), 500)
.SetRange(1, 1000)
.SetDisplay("Cooldown Bars", "Bars to wait between trades", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevHma = default;
_prevPrevHma = default;
_cooldown = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevHma = 0;
_prevPrevHma = 0;
_cooldown = 0;
var hma = new HullMovingAverage { Length = HmaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(hma, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, hma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal hmaValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (_prevHma == 0)
{
_prevHma = hmaValue;
return;
}
if (_prevPrevHma == 0)
{
_prevPrevHma = _prevHma;
_prevHma = hmaValue;
return;
}
if (_cooldown > 0)
{
_cooldown--;
_prevPrevHma = _prevHma;
_prevHma = hmaValue;
return;
}
// Direction change detection
var dirChangedUp = _prevHma < _prevPrevHma && hmaValue > _prevHma;
var dirChangedDown = _prevHma > _prevPrevHma && hmaValue < _prevHma;
if (Position == 0 && dirChangedUp)
{
BuyMarket();
_cooldown = CooldownBars;
}
else if (Position == 0 && dirChangedDown)
{
SellMarket();
_cooldown = CooldownBars;
}
else if (Position > 0 && dirChangedDown)
{
SellMarket();
_cooldown = CooldownBars;
}
else if (Position < 0 && dirChangedUp)
{
BuyMarket();
_cooldown = CooldownBars;
}
_prevPrevHma = _prevHma;
_prevHma = hmaValue;
}
}
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 HullMovingAverage
from StockSharp.Algo.Strategies import Strategy
class hull_ma_reversal_strategy(Strategy):
"""
Hull MA Reversal strategy.
Enters long when Hull MA changes direction from down to up.
Enters short when Hull MA changes direction from up to down.
Uses cooldown to control trade frequency.
"""
def __init__(self):
super(hull_ma_reversal_strategy, self).__init__()
self._hma_period = self.Param("HmaPeriod", 9).SetDisplay("HMA Period", "Period for Hull Moving Average", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(1))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._cooldown_bars = self.Param("CooldownBars", 500).SetDisplay("Cooldown Bars", "Bars to wait between trades", "General")
self._prev_hma = 0.0
self._prev_prev_hma = 0.0
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(hull_ma_reversal_strategy, self).OnReseted()
self._prev_hma = 0.0
self._prev_prev_hma = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(hull_ma_reversal_strategy, self).OnStarted2(time)
self._prev_hma = 0.0
self._prev_prev_hma = 0.0
self._cooldown = 0
hma = HullMovingAverage()
hma.Length = self._hma_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(hma, 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_val):
if candle.State != CandleStates.Finished:
return
hv = float(hma_val)
if self._prev_hma == 0:
self._prev_hma = hv
return
if self._prev_prev_hma == 0:
self._prev_prev_hma = self._prev_hma
self._prev_hma = hv
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_prev_hma = self._prev_hma
self._prev_hma = hv
return
cd = self._cooldown_bars.Value
# Direction change detection
dir_changed_up = self._prev_hma < self._prev_prev_hma and hv > self._prev_hma
dir_changed_down = self._prev_hma > self._prev_prev_hma and hv < self._prev_hma
if self.Position == 0 and dir_changed_up:
self.BuyMarket()
self._cooldown = cd
elif self.Position == 0 and dir_changed_down:
self.SellMarket()
self._cooldown = cd
elif self.Position > 0 and dir_changed_down:
self.SellMarket()
self._cooldown = cd
elif self.Position < 0 and dir_changed_up:
self.BuyMarket()
self._cooldown = cd
self._prev_prev_hma = self._prev_hma
self._prev_hma = hv
def CreateClone(self):
return hull_ma_reversal_strategy()