Auf GitHub ansehen

Improve MA & RSI Hedge Strategie

Diese Strategie portiert den ursprünglichen MetaTrader-Expert "Improve" unter Verwendung der High-Level-API nach StockSharp. Sie handelt gleichzeitig zwei Instrumente: das für die Strategie ausgewählte Hauptsymbol und ein Hedge-Symbol. Die Handelsrichtung wird durch die Beziehung zwischen zwei geglätteten gleitenden Durchschnitten am Hauptinstrument und dem Relative Strength Index (RSI) definiert. Das Hedge-Bein spiegelt die Richtung des Hauptbeins wider und schafft eine gepaarte Exposition, die darauf abzielt, von synchronisierten Momentum-Bewegungen zu profitieren und gleichzeitig das Einzelinstrument-Risiko zu begrenzen.

Strategielogik

  • Zwei Geglättete Gleitende Durchschnitte (SMMA) auf dem primären Symbol mit konfigurierbaren schnellen und langsamen Perioden berechnen.
  • RSI auf denselben Kerzen berechnen und überverkaufte/überkaufte Schwellenwerte überwachen.
  • Long auf beiden Instrumenten eingehen, wenn die langsame SMMA über der schnellen SMMA liegt und RSI beim oder unter dem Überverkauft-Schwellenwert ist.
  • Short auf beiden Instrumenten eingehen, wenn die langsame SMMA unter der schnellen SMMA liegt und RSI beim oder über dem Überkauft-Schwellenwert ist.
  • Positionen bleiben offen, bis der kombinierte offene Gewinn beider Beine das konfigurierte Geldziel übersteigt, woraufhin die Strategie beide Seiten liquidiert.

Der Algorithmus verfolgt die aktuellsten Schlusskurse jedes Instruments. Der kombinierte Gewinn wird aus der Differenz zwischen dem aktuellen Schluss und dem gespeicherten Einstiegspreis jedes Beins geschätzt. Da kein Stop-Loss angewendet wird, können Positionen für längere Zeiträume offen bleiben, wenn der Preis das Gewinnziel nicht erreicht.

Parameter

Parameter Beschreibung
Volume Ordermenge für beide Instrumente, das primäre und das Hedge-Instrument.
Profit Target Gemeinsames Geldziel für beide Beine; wenn erreicht, schließt die Strategie jede offene Position.
Hedge Security Sekundäres Instrument, das zusammen mit dem primären Wertpapier gehandelt wird.
Fast MA Periode des schnellen Geglätteten Gleitenden Durchschnitts (Standard 8).
Slow MA Periode des langsamen Geglätteten Gleitenden Durchschnitts (Standard 21). Muss größer als die schnelle MA-Periode sein.
RSI Period Für die RSI-Berechnung verwendete Länge (Standard 21).
Oversold RSI-Niveau, das Long-Einstiege zusammen mit der MA-Bedingung auslöst (Standard 30).
Overbought RSI-Niveau, das Short-Einstiege zusammen mit der MA-Bedingung auslöst (Standard 70).
Candle Type Zeitrahmen für Berechnungen; standardmäßig 1-Stunden-Kerzen, kann aber angepasst werden.

Indikatoren

  • Geglätteter Gleitender Durchschnitt (SMMA) – wird zweimal verwendet, um die schnellen und langsamen Trendkomponenten zu definieren.
  • Relative Strength Index (RSI) – bestimmt Überverkauft-/Überkauft-Bedingungen zur Bestätigung.

Einstiegs- und Ausstiegsregeln

  1. Long-Einstieg
    • Langsame SMMA > Schnelle SMMA am primären Symbol.
    • RSI ≤ Überverkauft.
    • Beide Beine werden mit Marktorders in dieselbe Richtung eröffnet (Kauf/Kauf).
  2. Short-Einstieg
    • Langsame SMMA < Schnelle SMMA am primären Symbol.
    • RSI ≥ Überkauft.
    • Beide Beine werden mit Marktorders in dieselbe Richtung eröffnet (Verkauf/Verkauf).
  3. Ausstieg
    • Wenn (primärer Gewinn + Hedge-Gewinn) ≥ Profit Target, schließt die Strategie beide Positionen mit Marktorders.
    • Keine zusätzliche Stop-Loss- oder Trailing-Logik wird angewendet; Risikomanagement sollte extern hinzugefügt werden, wenn erforderlich.

Verwendungshinweise

  • Sicherstellen, dass sowohl das primäre Wertpapier als auch das Hedge-Wertpapier vor dem Starten der Strategie zugewiesen sind; andernfalls wird eine Ausnahme ausgelöst.
  • Die kombinierte Gewinnschätzung basiert auf Kerzenschlusskursen. Slippage und Ausführungsunterschiede zwischen den beiden Beinen können den tatsächlich realisierten Gewinn beeinflussen.
  • Da die Strategie beide Beine gleichzeitig öffnet, eignet sie sich für korrelierte Instrumente (zum Beispiel Währungspaare oder verwandte Futures), bei denen ein gleichzeitiges Bewegen erwartet wird.
  • Erwägen Sie, Portfolio-Level-Risikokontrollen hinzuzufügen, wenn Sie live handeln, da der ursprüngliche Algorithmus nur das virtuelle Gewinnziel für Ausstiege verwendet.
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;

using StockSharp.Algo;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Dual smoothed moving average and RSI hedge strategy converted from Improve.mq5.
/// </summary>
public class ImproveMaRsiHedgeStrategy : Strategy
{
	private readonly StrategyParam<decimal> _profitTarget;
	private readonly StrategyParam<Security> _hedgeSecurity;
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<decimal> _oversoldLevel;
	private readonly StrategyParam<decimal> _overboughtLevel;
	private readonly StrategyParam<DataType> _candleType;

	private SmoothedMovingAverage _fastMa = null!;
	private SmoothedMovingAverage _slowMa = null!;
	private RelativeStrengthIndex _rsi = null!;

	private decimal _baseLastClose;
	private decimal _hedgeLastClose;
	private decimal _baseEntryPrice;
	private decimal _hedgeEntryPrice;
	private bool _hasBaseClose;
	private bool _hasHedgeClose;
	private int _pairDirection;


	/// <summary>
	/// Profit target across both legs expressed in money.
	/// </summary>
	public decimal ProfitTarget
	{
		get => _profitTarget.Value;
		set => _profitTarget.Value = value;
	}

	/// <summary>
	/// Second instrument traded alongside the primary security.
	/// </summary>
	public Security HedgeSecurity
	{
		get => _hedgeSecurity.Value;
		set => _hedgeSecurity.Value = value;
	}

	/// <summary>
	/// Smoothed moving average period for the fast line.
	/// </summary>
	public int FastMaPeriod
	{
		get => _fastPeriod.Value;
		set => _fastPeriod.Value = value;
	}

	/// <summary>
	/// Smoothed moving average period for the slow line.
	/// </summary>
	public int SlowMaPeriod
	{
		get => _slowPeriod.Value;
		set => _slowPeriod.Value = value;
	}

	/// <summary>
	/// RSI calculation length.
	/// </summary>
	public int RsiPeriod
	{
		get => _rsiPeriod.Value;
		set => _rsiPeriod.Value = value;
	}

	/// <summary>
	/// RSI oversold threshold.
	/// </summary>
	public decimal OversoldLevel
	{
		get => _oversoldLevel.Value;
		set => _oversoldLevel.Value = value;
	}

	/// <summary>
	/// RSI overbought threshold.
	/// </summary>
	public decimal OverboughtLevel
	{
		get => _overboughtLevel.Value;
		set => _overboughtLevel.Value = value;
	}

	/// <summary>
	/// Type of candles used for calculations.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Initializes a new instance of the <see cref="ImproveMaRsiHedgeStrategy"/> class.
	/// </summary>
	public ImproveMaRsiHedgeStrategy()
	{

		_profitTarget = Param(nameof(ProfitTarget), 50m)
			.SetGreaterThanZero()
			.SetDisplay("Profit Target", "Combined profit target across both legs", "Risk")
			;

		_hedgeSecurity = Param<Security>(nameof(HedgeSecurity))
			.SetDisplay("Hedge Security", "Secondary instrument to trade", "General");

		_fastPeriod = Param(nameof(FastMaPeriod), 8)
			.SetGreaterThanZero()
			.SetDisplay("Fast MA", "Fast smoothed MA period", "Indicators")
			;

		_slowPeriod = Param(nameof(SlowMaPeriod), 21)
			.SetGreaterThanZero()
			.SetDisplay("Slow MA", "Slow smoothed MA period", "Indicators")
			;

		_rsiPeriod = Param(nameof(RsiPeriod), 21)
			.SetGreaterThanZero()
			.SetDisplay("RSI Period", "Length of the RSI", "Indicators")
			;

		_oversoldLevel = Param(nameof(OversoldLevel), 30m)
			.SetDisplay("Oversold", "RSI oversold threshold", "Indicators")
			;

		_overboughtLevel = Param(nameof(OverboughtLevel), 70m)
			.SetDisplay("Overbought", "RSI overbought threshold", "Indicators")
			;

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Candle Type", "Time frame for calculations", "Data");
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		if (Security != null)
			yield return (Security, CandleType);

		if (HedgeSecurity != null)
			yield return (HedgeSecurity, CandleType);
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_fastMa = null!;
		_slowMa = null!;
		_rsi = null!;
		_baseLastClose = 0m;
		_hedgeLastClose = 0m;
		_baseEntryPrice = 0m;
		_hedgeEntryPrice = 0m;
		_hasBaseClose = false;
		_hasHedgeClose = false;
		_pairDirection = 0;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		if (Security == null)
			throw new InvalidOperationException("Primary security must be specified.");

		if (HedgeSecurity == null)
			throw new InvalidOperationException("Hedge security must be specified.");

		if (FastMaPeriod >= SlowMaPeriod)
			throw new InvalidOperationException("Fast MA period must be less than slow MA period.");

		_fastMa = new SmoothedMovingAverage { Length = FastMaPeriod };
		_slowMa = new SmoothedMovingAverage { Length = SlowMaPeriod };
		_rsi = new RelativeStrengthIndex { Length = RsiPeriod };

		var baseSubscription = SubscribeCandles(CandleType);
		baseSubscription
			.Bind(_fastMa, _slowMa, _rsi, ProcessBaseCandle)
			.Start();

		var hedgeSubscription = SubscribeCandles(CandleType, false, HedgeSecurity);
		hedgeSubscription
			.Bind(ProcessHedgeCandle)
			.Start();
	}

	private void ProcessBaseCandle(ICandleMessage candle, decimal fastValue, decimal slowValue, decimal rsiValue)
	{
		if (candle.State != CandleStates.Finished)
			return;

		_baseLastClose = candle.ClosePrice;
		_hasBaseClose = true;

		CheckProfitTarget();

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		if (!_fastMa.IsFormed || !_slowMa.IsFormed || !_rsi.IsFormed)
			return;

		if (_pairDirection != 0)
			return;

		if (!_hasHedgeClose)
			return;

		if (slowValue > fastValue && rsiValue <= OversoldLevel)
		{
			OpenPair(1);
		}
		else if (slowValue < fastValue && rsiValue >= OverboughtLevel)
		{
			OpenPair(-1);
		}
	}

	private void ProcessHedgeCandle(ICandleMessage candle)
	{
		if (candle.State != CandleStates.Finished)
			return;

		_hedgeLastClose = candle.ClosePrice;
		_hasHedgeClose = true;

		CheckProfitTarget();
	}

	private void OpenPair(int direction)
	{
		if (direction == 0)
			return;

		var basePos = GetPositionValue(Security, Portfolio) ?? 0m;
		var hedgePos = GetPositionValue(HedgeSecurity, Portfolio) ?? 0m;

		if (basePos != 0m || hedgePos != 0m)
			return;

		var volume = Volume;

		if (direction > 0)
		{
			BuyMarket(volume, Security);
			BuyMarket(volume, HedgeSecurity);
		}
		else
		{
			SellMarket(volume, Security);
			SellMarket(volume, HedgeSecurity);
		}

		_pairDirection = direction;
		_baseEntryPrice = _baseLastClose;
		_hedgeEntryPrice = _hedgeLastClose;
	}

	private void CheckProfitTarget()
	{
		if (_pairDirection == 0 || !_hasBaseClose || !_hasHedgeClose)
			return;

		var baseProfit = _pairDirection > 0
			? (_baseLastClose - _baseEntryPrice) * Volume
			: (_baseEntryPrice - _baseLastClose) * Volume;

		var hedgeProfit = _pairDirection > 0
			? (_hedgeLastClose - _hedgeEntryPrice) * Volume
			: (_hedgeEntryPrice - _hedgeLastClose) * Volume;

		var totalProfit = baseProfit + hedgeProfit;

		if (totalProfit >= ProfitTarget)
		{
			ClosePair();
		}
	}

	private void ClosePair()
	{
		var basePos = GetPositionValue(Security, Portfolio) ?? 0m;
		if (basePos > 0)
		{
			SellMarket(basePos, Security);
		}
		else if (basePos < 0)
		{
			BuyMarket(-basePos, Security);
		}

		var hedgePos = GetPositionValue(HedgeSecurity, Portfolio) ?? 0m;
		if (hedgePos > 0)
		{
			SellMarket(hedgePos, HedgeSecurity);
		}
		else if (hedgePos < 0)
		{
			BuyMarket(-hedgePos, HedgeSecurity);
		}

		_pairDirection = 0;
		_baseEntryPrice = 0m;
		_hedgeEntryPrice = 0m;
	}
}