Strategie RSI Hull MA
Diese Strategie verwendet RSI Hull MA-Indikatoren zur Signalerzeugung. Ein Long-Einstieg erfolgt, wenn RSI < RsiOversold && HMA(t) > HMA(t-1) (überverkauft mit steigendem HMA). Ein Short-Einstieg erfolgt, wenn RSI > RsiOverbought && HMA(t) < HMA(t-1) (überkauft mit fallendem HMA). Sie eignet sich für Trader, die in gemischten Märkten nach Gelegenheiten suchen.
Tests zeigen eine durchschnittliche Jahresrendite von etwa 58%. Die Strategie funktioniert am besten am Aktienmarkt.
Details
- Einstiegskriterien:
- Long: RSI < RsiOversold && HMA(t) > HMA(t-1) (überverkauft mit steigendem HMA)
- Short: RSI > RsiOverbought && HMA(t) < HMA(t-1) (überkauft mit fallendem HMA)
- Long/Short: Beide Seiten.
- Ausstiegskriterien:
- Long: Long-Position schließen, wenn RSI in die neutrale Zone zurückkehrt
- Short: Short-Position schließen, wenn RSI in die neutrale Zone zurückkehrt
- Stops: Ja.
- Standardwerte:
RsiPeriod= 14RsiOversold= 30RsiOverbought= 70HullPeriod= 9AtrPeriod= 14AtrMultiplier= 2 30 und 70 sind die Standardwerte der RSI-Schwellen, die die Regeln nennen. Die neutrale Zone beginnt bei RSI 50: Ein Long schließt, sobald der RSI 50 erreicht, ein Short, sobald er auf 50 fällt. Der Stop liegt AtrMultiplier mal die ATR über AtrPeriod vom Einstiegsschluss entfernt und wird bei Kerzenschluss geprüft; 0 schaltet ihn ab. Ein Einstiegssignal gegen eine offene Position dreht sie.CandleType= TimeSpan.FromMinutes(5)
- Filter:
- Kategorie: Gemischt
- Richtung: Beide
- Indikatoren: RSI Hull MA
- Stops: Ja
- Komplexität: Mittel
- 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>
/// RSI Hull MA strategy.
/// RSI below RsiOversold with a rising HullPeriod Hull moving average goes long and RSI above RsiOverbought with a falling one goes short,
/// reversing an opposite position. A long closes once RSI returns to the neutral 50 level and a short likewise. The stop lies
/// AtrMultiplier ATR from the entry close and is checked on candle closes.
/// </summary>
public class RsiHullMaStrategy : Strategy
{
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<decimal> _rsiOversold;
private readonly StrategyParam<decimal> _rsiOverbought;
private readonly StrategyParam<int> _hullPeriod;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<decimal> _atrMultiplier;
private readonly StrategyParam<DataType> _candleType;
private decimal? _prevHull;
private decimal _stopPrice;
/// <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>
/// Period of the Hull moving average.
/// </summary>
public int HullPeriod
{
get => _hullPeriod.Value;
set => _hullPeriod.Value = value;
}
/// <summary>
/// Period of the stop ATR.
/// </summary>
public int AtrPeriod
{
get => _atrPeriod.Value;
set => _atrPeriod.Value = value;
}
/// <summary>
/// Stop distance from the entry in ATRs.
/// </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 RsiHullMaStrategy()
{
_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");
_hullPeriod = Param(nameof(HullPeriod), 9)
.SetGreaterThanZero()
.SetDisplay("Hull Period", "Period of the Hull moving average", "Indicators");
_atrPeriod = Param(nameof(AtrPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("ATR Period", "Period of the stop ATR", "Risk");
_atrMultiplier = Param(nameof(AtrMultiplier), 2m)
.SetNotNegative()
.SetDisplay("ATR Multiplier", "Stop distance from the entry in ATRs", "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;
_stopPrice = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevHull = null;
_stopPrice = default;
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
var hull = new HullMovingAverage { Length = HullPeriod };
var atr = new AverageTrueRange { Length = AtrPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(rsi, hull, 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 rsiValue, IIndicatorValue hullValue, IIndicatorValue atrValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!hullValue.IsFormed)
return;
var hull = hullValue.GetValue<decimal>();
var prevHull = _prevHull;
_prevHull = hull;
if (prevHull is not decimal previous || !rsiValue.IsFormed || !atrValue.IsFormed)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var rsi = rsiValue.GetValue<decimal>();
var atr = atrValue.GetValue<decimal>();
var close = candle.ClosePrice;
// RSI 50 is the middle of the neutral zone.
if (rsi < RsiOversold && hull > previous && Position <= 0)
{
BuyMarket(Volume + Math.Abs(Position));
_stopPrice = close - AtrMultiplier * atr;
}
else if (rsi > RsiOverbought && hull < previous && Position >= 0)
{
SellMarket(Volume + Math.Abs(Position));
_stopPrice = close + AtrMultiplier * atr;
}
else if (Position > 0 && (rsi >= 50m || (AtrMultiplier > 0 && close <= _stopPrice)))
{
SellMarket(Position);
}
else if (Position < 0 && (rsi <= 50m || (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 RelativeStrengthIndex, HullMovingAverage, AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class rsi_hull_ma_strategy(Strategy):
"""
RSI Hull MA strategy.
RSI below RsiOversold with a rising HullPeriod Hull moving average goes long and RSI above RsiOverbought with a falling one goes short,
reversing an opposite position. A long closes once RSI returns to the neutral 50 level and a short likewise. The stop lies
AtrMultiplier ATR from the entry close and is checked on candle closes.
"""
def __init__(self):
super(rsi_hull_ma_strategy, self).__init__()
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._hull_period = self.Param("HullPeriod", 9).SetGreaterThanZero().SetDisplay("Hull Period", "Period of the Hull moving average", "Indicators")
self._atr_period = self.Param("AtrPeriod", 14).SetGreaterThanZero().SetDisplay("ATR Period", "Period of the stop ATR", "Risk")
self._atr_multiplier = self.Param("AtrMultiplier", 2.0).SetNotNegative().SetDisplay("ATR Multiplier", "Stop distance from the entry in ATRs", "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._stop_price = Decimal(0)
def OnReseted(self):
super(rsi_hull_ma_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(rsi_hull_ma_strategy, self).OnStarted2(time)
self._reset_state()
rsi = RelativeStrengthIndex()
rsi.Length = self._rsi_period.Value
hull = HullMovingAverage()
hull.Length = self._hull_period.Value
atr = AverageTrueRange()
atr.Length = self._atr_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(rsi, hull, 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, rsi_value, hull_value, atr_value):
if candle.State != CandleStates.Finished:
return
if not hull_value.IsFormed:
return
hull = hull_value.GetValue[Decimal](None)
previous = self._prev_hull
self._prev_hull = hull
if previous is None or not rsi_value.IsFormed or not atr_value.IsFormed:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
rsi = rsi_value.GetValue[Decimal](None)
atr = atr_value.GetValue[Decimal](None)
close = candle.ClosePrice
# RSI 50 is the middle of the neutral zone.
stop_atr = Decimal(self._atr_multiplier.Value)
neutral = Decimal(50)
if rsi < Decimal(self._rsi_oversold.Value) and hull > previous and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
self._stop_price = close - stop_atr * atr
elif rsi > Decimal(self._rsi_overbought.Value) and hull < previous and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
self._stop_price = close + stop_atr * atr
elif self.Position > 0 and (rsi >= neutral or (stop_atr > 0 and close <= self._stop_price)):
self.SellMarket(self.Position)
elif self.Position < 0 and (rsi <= neutral or (stop_atr > 0 and close >= self._stop_price)):
self.BuyMarket(-self.Position)
def CreateClone(self):
return rsi_hull_ma_strategy()