Auf GitHub ansehen

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 = 14
    • RsiOversold = 30
    • RsiOverbought = 70
    • HullPeriod = 9
    • AtrPeriod = 14
    • AtrMultiplier = 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);
		}
	}
}