Strategie Hull Ma Stochastic
Hull Moving Average + Stochastic Oscillator Strategie. Die Strategie tritt ein, wenn sich die HMA-Trendrichtung ändert und der Stochastic überverkaufte/überkaufte Bedingungen bestätigt.
Tests zeigen eine durchschnittliche Jahresrendite von etwa 94%. Die Strategie funktioniert am besten auf dem Aktienmarkt.
Der Hull MA zeigt die Trendrichtung schnell auf. Der Stochastic wartet auf einen Rückgang oder eine Erholung innerhalb dieses Trends, um den Trade auszulösen.
Ein flexibler Ansatz für Trader, die glatte Signale bevorzugen. ATR-basierte Stops begrenzen den potenziellen Verlust.
Details
- Einstiegskriterien:
- Long:
HullMA turning up && StochK < 20 - Short:
HullMA turning down && StochK > 80
- Long:
- Long/Short: Beide
- Ausstiegskriterien:
- Hull MA Richtungswechsel
- Stops: ATR-basiert mit
StopLossAtr - Standardwerte:
HmaPeriod= 9StochPeriod= 14StochK= 3StochD= 3CandleType= TimeSpan.FromMinutes(5).TimeFrame()StopLossAtr= 2m
- Filter:
- Kategorie: Mean Reversion
- Richtung: Beide
- Indikatoren: Hull MA, Moving Average, Stochastic Oscillator
- Stops: Ja
- Komplexität: Mittel
- Zeitrahmen: Mittelfristig
- Saisonalität: Nein
- Neuronale Netze: Nein
- Divergenz: Nein
- Risikolevel: Mittel
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>
/// Hull Moving Average + Stochastic Oscillator strategy.
/// Strategy enters when HMA trend direction changes with Stochastic confirming oversold/overbought conditions.
/// </summary>
public class HullMaStochasticStrategy : Strategy
{
private readonly StrategyParam<int> _hmaPeriod;
private readonly StrategyParam<int> _stochPeriod;
private readonly StrategyParam<int> _stochK;
private readonly StrategyParam<int> _stochD;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _cooldownBars;
private readonly StrategyParam<decimal> _stopLossPercent;
// Indicators
private HullMovingAverage _hma;
private StochasticOscillator _stochastic;
private AverageTrueRange _atr;
private int _cooldown;
// Previous HMA value for trend detection
private decimal _prevHmaValue;
/// <summary>
/// Hull Moving Average period.
/// </summary>
public int HmaPeriod
{
get => _hmaPeriod.Value;
set => _hmaPeriod.Value = value;
}
/// <summary>
/// Stochastic period.
/// </summary>
public int StochPeriod
{
get => _stochPeriod.Value;
set => _stochPeriod.Value = value;
}
/// <summary>
/// Stochastic %K period.
/// </summary>
public int StochK
{
get => _stochK.Value;
set => _stochK.Value = value;
}
/// <summary>
/// Stochastic %D period.
/// </summary>
public int StochD
{
get => _stochD.Value;
set => _stochD.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Bars to wait between trades.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Stop-loss percentage.
/// </summary>
public decimal StopLossPercent
{
get => _stopLossPercent.Value;
set => _stopLossPercent.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public HullMaStochasticStrategy()
{
_hmaPeriod = Param(nameof(HmaPeriod), 9)
.SetGreaterThanZero()
.SetDisplay("HMA Period", "Hull Moving Average period", "Indicators")
.SetOptimize(4, 30, 2);
_stochPeriod = Param(nameof(StochPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("Stochastic Period", "Stochastic oscillator period", "Indicators")
.SetOptimize(5, 30, 5);
_stochK = Param(nameof(StochK), 3)
.SetGreaterThanZero()
.SetDisplay("Stochastic %K", "Stochastic %K period", "Indicators")
.SetOptimize(1, 10, 1);
_stochD = Param(nameof(StochD), 3)
.SetGreaterThanZero()
.SetDisplay("Stochastic %D", "Stochastic %D period", "Indicators")
.SetOptimize(1, 10, 1);
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_cooldownBars = Param(nameof(CooldownBars), 90)
.SetRange(5, 500)
.SetDisplay("Cooldown Bars", "Bars between trades", "General");
_stopLossPercent = Param(nameof(StopLossPercent), 1.0m)
.SetNotNegative()
.SetDisplay("Stop Loss %", "Stop loss percentage from entry price", "Risk Management")
.SetOptimize(0.5m, 2.0m, 0.5m);
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_hma = null;
_stochastic = null;
_atr = null;
_prevHmaValue = 0;
_cooldown = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
// Create indicators
_hma = new HullMovingAverage { Length = HmaPeriod };
_stochastic = new StochasticOscillator
{
K = { Length = StochK },
D = { Length = StochD },
};
_atr = new AverageTrueRange { Length = 14 };
// Subscribe to candles and bind indicators
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(_hma, _stochastic, _atr, ProcessCandle)
.Start();
// Setup chart
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _hma);
var secondArea = CreateChartArea();
if (secondArea != null)
{
DrawIndicator(secondArea, _stochastic);
}
DrawOwnTrades(area);
}
}
private void ProcessCandle(
ICandleMessage candle,
IIndicatorValue hmaValue,
IIndicatorValue stochValue,
IIndicatorValue atrValue)
{
// Skip unfinished candles
if (candle.State != CandleStates.Finished)
return;
// Check if strategy is ready to trade
if (!IsFormedAndOnlineAndAllowTrading())
return;
// Get indicator values
decimal hma = hmaValue.ToDecimal();
var stochTyped = (StochasticOscillatorValue)stochValue;
if (stochTyped.K is not decimal stochK)
return;
decimal atr = atrValue.ToDecimal();
// Skip first candle after initialization
if (_prevHmaValue == 0)
{
_prevHmaValue = hma;
return;
}
// Detect HMA trend direction
bool hmaIncreasing = hma > _prevHmaValue;
bool hmaDecreasing = hma < _prevHmaValue;
if (_cooldown > 0)
{
_cooldown--;
_prevHmaValue = hma;
return;
}
// Trading logic:
// Buy/short by HMA slope with a light stochastic filter.
if (hmaIncreasing && stochK > 50 && Position == 0)
{
BuyMarket();
_cooldown = CooldownBars;
LogInfo($"Long entry: Price={candle.ClosePrice}, HMA={hma}, Prev HMA={_prevHmaValue}, Stochastic %K={stochK}");
}
// Sell when HMA is decreasing and stochastic confirms bearish momentum.
else if (hmaDecreasing && stochK < 50 && Position == 0)
{
SellMarket();
_cooldown = CooldownBars;
LogInfo($"Short entry: Price={candle.ClosePrice}, HMA={hma}, Prev HMA={_prevHmaValue}, Stochastic %K={stochK}");
}
// Exit when HMA trend changes direction
else if (Position > 0 && hmaDecreasing)
{
SellMarket();
_cooldown = CooldownBars;
LogInfo($"Long exit: Price={candle.ClosePrice}, HMA={hma}, Prev HMA={_prevHmaValue}");
}
else if (Position < 0 && hmaIncreasing)
{
BuyMarket();
_cooldown = CooldownBars;
LogInfo($"Short exit: Price={candle.ClosePrice}, HMA={hma}, Prev HMA={_prevHmaValue}");
}
// Save current HMA value for next candle
_prevHmaValue = hma;
}
}
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, Unit, UnitTypes
from StockSharp.Algo.Indicators import HullMovingAverage, StochasticOscillator, AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class hull_ma_stochastic_strategy(Strategy):
def __init__(self):
super(hull_ma_stochastic_strategy, self).__init__()
self._hma_period = self.Param("HmaPeriod", 9) \
.SetDisplay("HMA Period", "Hull Moving Average period", "Indicators")
self._stoch_period = self.Param("StochPeriod", 14) \
.SetDisplay("Stochastic Period", "Stochastic oscillator period", "Indicators")
self._stoch_k = self.Param("StochK", 3) \
.SetDisplay("Stochastic %K", "Stochastic %K period", "Indicators")
self._stoch_d = self.Param("StochD", 3) \
.SetDisplay("Stochastic %D", "Stochastic %D period", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._cooldown_bars = self.Param("CooldownBars", 90) \
.SetDisplay("Cooldown Bars", "Bars between trades", "General")
self._stop_loss_percent = self.Param("StopLossPercent", 1.0) \
.SetDisplay("Stop Loss %", "Stop loss percentage from entry price", "Risk Management")
self._hma = None
self._stochastic = None
self._atr = None
self._prev_hma_value = 0.0
self._cooldown = 0
@property
def HmaPeriod(self):
return self._hma_period.Value
@HmaPeriod.setter
def HmaPeriod(self, value):
self._hma_period.Value = value
@property
def StochPeriod(self):
return self._stoch_period.Value
@StochPeriod.setter
def StochPeriod(self, value):
self._stoch_period.Value = value
@property
def StochK(self):
return self._stoch_k.Value
@StochK.setter
def StochK(self, value):
self._stoch_k.Value = value
@property
def StochD(self):
return self._stoch_d.Value
@StochD.setter
def StochD(self, value):
self._stoch_d.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def CooldownBars(self):
return self._cooldown_bars.Value
@CooldownBars.setter
def CooldownBars(self, value):
self._cooldown_bars.Value = value
@property
def StopLossPercent(self):
return self._stop_loss_percent.Value
@StopLossPercent.setter
def StopLossPercent(self, value):
self._stop_loss_percent.Value = value
def OnStarted2(self, time):
super(hull_ma_stochastic_strategy, self).OnStarted2(time)
self._prev_hma_value = 0.0
self._cooldown = 0
self._hma = HullMovingAverage()
self._hma.Length = self.HmaPeriod
self._stochastic = StochasticOscillator()
self._stochastic.K.Length = self.StochK
self._stochastic.D.Length = self.StochD
self._atr = AverageTrueRange()
self._atr.Length = 14
self.SubscribeCandles(self.CandleType) \
.BindEx(self._hma, self._stochastic, self._atr, self.ProcessCandle) \
.Start()
def ProcessCandle(self, candle, hma_value, stoch_value, atr_value):
if candle.State != CandleStates.Finished:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
hma_f = float(hma_value)
stoch_k = stoch_value.K
if stoch_k is None:
return
stoch_k_f = float(stoch_k)
if self._prev_hma_value == 0:
self._prev_hma_value = hma_f
return
hma_increasing = hma_f > self._prev_hma_value
hma_decreasing = hma_f < self._prev_hma_value
cooldown_bars = int(self.CooldownBars)
if self._cooldown > 0:
self._cooldown -= 1
self._prev_hma_value = hma_f
return
if hma_increasing and stoch_k_f > 50 and self.Position == 0:
self.BuyMarket()
self._cooldown = cooldown_bars
elif hma_decreasing and stoch_k_f < 50 and self.Position == 0:
self.SellMarket()
self._cooldown = cooldown_bars
elif self.Position > 0 and hma_decreasing:
self.SellMarket()
self._cooldown = cooldown_bars
elif self.Position < 0 and hma_increasing:
self.BuyMarket()
self._cooldown = cooldown_bars
self._prev_hma_value = hma_f
def OnReseted(self):
super(hull_ma_stochastic_strategy, self).OnReseted()
self._hma = None
self._stochastic = None
self._atr = None
self._prev_hma_value = 0.0
self._cooldown = 0
def CreateClone(self):
return hull_ma_stochastic_strategy()