Hull MA RSI Strategie
Implementierung der Strategie Hull Moving Average + RSI. Kaufen, wenn der HMA nach oben dreht und der RSI unter RsiOversold liegt. Verkaufen, wenn der HMA nach unten dreht und der RSI über RsiOverbought liegt.
Tests zeigen eine durchschnittliche jährliche Rendite von etwa 64%. Am besten geeignet für den Forex-Markt.
Der Hull MA liefert eine geglättete Trendlinie und der RSI hebt Impulsdivergenz hervor. Trades erfolgen, wenn der RSI an Extrempunkten dreht, während der Preis der Hull-Richtung folgt.
Geeignet für kurzfristige Swing-Trader, die frühe Signale suchen. ATR-basierte Stops schützen den Trade.
Details
- Einstiegskriterien:
- Long:
HullMA turning up && RSI < RsiOversold - Short:
HullMA turning down && RSI > RsiOverbought
- Long:
- Long/Short: Beide
- Ausstiegskriterien:
- Richtungswechsel des Hull MA
- Stops: ATR-basiert mit
StopLossAtr - Standardwerte:
HmaPeriod= 9RsiPeriod= 14RsiOversold= 30mRsiOverbought= 70mStopLossAtr= 2AtrPeriod= 14CandleType= TimeSpan.FromMinutes(5).TimeFrame() Der HMA dreht nach oben, wenn er nach einem Rückgang steigt, und nach unten, wenn er nach einem Anstieg fällt. Der Stop liegt StopLossAtr ATR (AtrPeriod) vom Einstiegsschluss entfernt und wird auf Kerzenschlüssen geprüft. Ein Einstiegssignal gegen eine offene Position dreht sie.
- Filter:
- Kategorie: Mean Reversion
- Richtung: Beide
- Indikatoren: Hull MA, Moving Average, RSI
- 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 RSI strategy.
/// The Hull average turns up when it rises after falling and turns down when it falls after rising. A turn up with RSI below RsiOversold
/// goes long and a turn down with RSI above RsiOverbought goes short, reversing an opposite position. A long closes when the Hull average
/// falls and a short when it rises. The stop lies StopLossAtr ATR from the entry close and is checked on candle closes.
/// </summary>
public class HullMaRsiStrategy : Strategy
{
private readonly StrategyParam<int> _hmaPeriod;
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<decimal> _rsiOversold;
private readonly StrategyParam<decimal> _rsiOverbought;
private readonly StrategyParam<decimal> _stopLossAtr;
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 RSI.
/// </summary>
public int RsiPeriod
{
get => _rsiPeriod.Value;
set => _rsiPeriod.Value = value;
}
/// <summary>
/// RSI level for longs.
/// </summary>
public decimal RsiOversold
{
get => _rsiOversold.Value;
set => _rsiOversold.Value = value;
}
/// <summary>
/// RSI level for shorts.
/// </summary>
public decimal RsiOverbought
{
get => _rsiOverbought.Value;
set => _rsiOverbought.Value = value;
}
/// <summary>
/// Stop distance from the entry in ATRs.
/// </summary>
public decimal StopLossAtr
{
get => _stopLossAtr.Value;
set => _stopLossAtr.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 HullMaRsiStrategy()
{
_hmaPeriod = Param(nameof(HmaPeriod), 9)
.SetGreaterThanZero()
.SetDisplay("HMA Period", "Period of the Hull moving average", "Indicators");
_rsiPeriod = Param(nameof(RsiPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Period", "Period of RSI", "Indicators");
_rsiOversold = Param(nameof(RsiOversold), 30m)
.SetDisplay("RSI Oversold", "RSI level for longs", "Indicators");
_rsiOverbought = Param(nameof(RsiOverbought), 70m)
.SetDisplay("RSI Overbought", "RSI level for shorts", "Indicators");
_stopLossAtr = Param(nameof(StopLossAtr), 2m)
.SetNotNegative()
.SetDisplay("Stop Loss ATR", "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(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();
_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 rsi = new RelativeStrengthIndex { Length = RsiPeriod };
var atr = new AverageTrueRange { Length = AtrPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(hull, rsi, 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, rsi);
}
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue hullValue, IIndicatorValue rsiValue, IIndicatorValue atrValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!hullValue.IsFormed || !rsiValue.IsFormed || !atrValue.IsFormed)
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 rsi = rsiValue.GetValue<decimal>();
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;
if (turnsUp && rsi < RsiOversold && Position <= 0)
{
BuyMarket(Volume + Math.Abs(Position));
_stopPrice = close - StopLossAtr * atr;
}
else if (turnsDown && rsi > RsiOverbought && Position >= 0)
{
SellMarket(Volume + Math.Abs(Position));
_stopPrice = close + StopLossAtr * atr;
}
else if (Position > 0 && (falling || (StopLossAtr > 0 && close <= _stopPrice)))
{
SellMarket(Position);
}
else if (Position < 0 && (rising || (StopLossAtr > 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, RelativeStrengthIndex, AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class hull_ma_rsi_strategy(Strategy):
"""
Hull MA RSI strategy.
The Hull average turns up when it rises after falling and turns down when it falls after rising. A turn up with RSI below RsiOversold
goes long and a turn down with RSI above RsiOverbought goes short, reversing an opposite position. A long closes when the Hull average
falls and a short when it rises. The stop lies StopLossAtr ATR from the entry close and is checked on candle closes.
"""
def __init__(self):
super(hull_ma_rsi_strategy, self).__init__()
self._hma_period = self.Param("HmaPeriod", 9).SetGreaterThanZero().SetDisplay("HMA Period", "Period of the Hull moving average", "Indicators")
self._rsi_period = self.Param("RsiPeriod", 14).SetGreaterThanZero().SetDisplay("RSI Period", "Period of RSI", "Indicators")
self._rsi_oversold = self.Param("RsiOversold", 30.0).SetDisplay("RSI Oversold", "RSI level for longs", "Indicators")
self._rsi_overbought = self.Param("RsiOverbought", 70.0).SetDisplay("RSI Overbought", "RSI level for shorts", "Indicators")
self._stop_loss_atr = self.Param("StopLossAtr", 2.0).SetNotNegative().SetDisplay("Stop Loss ATR", "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(5))).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_rsi_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(hull_ma_rsi_strategy, self).OnStarted2(time)
self._reset_state()
hull = HullMovingAverage()
hull.Length = self._hma_period.Value
rsi = RelativeStrengthIndex()
rsi.Length = self._rsi_period.Value
atr = AverageTrueRange()
atr.Length = self._atr_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(hull, rsi, 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, rsi)
def _process_candle(self, candle, hull_value, rsi_value, atr_value):
if candle.State != CandleStates.Finished:
return
if not hull_value.IsFormed or not rsi_value.IsFormed or not atr_value.IsFormed:
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
rsi = rsi_value.GetValue[Decimal](None)
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
stop_atr = Decimal(self._stop_loss_atr.Value)
if turns_up and rsi < Decimal(self._rsi_oversold.Value) and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
self._stop_price = close - stop_atr * atr
elif turns_down and rsi > Decimal(self._rsi_overbought.Value) 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 (stop_atr > 0 and close <= self._stop_price)):
self.SellMarket(self.Position)
elif self.Position < 0 and (rising or (stop_atr > 0 and close >= self._stop_price)):
self.BuyMarket(-self.Position)
def CreateClone(self):
return hull_ma_rsi_strategy()