Auf GitHub ansehen

Multi-Hedging-Planer-Strategie

Übersicht

Die Multi-Hedging-Planer-Strategie ist eine direkte StockSharp-Konvertierung des ursprünglichen MetaTrader-5-Expertenberaters MultiHedg_1.mq5. Die Strategie ist für Konten ausgelegt, die Hedging ermöglichen, und kann bis zu zehn verschiedene Instrumente gleichzeitig verwalten. Sie öffnet Positionen in derselben Richtung während eines konfigurierbaren Handelsfensters und bietet Portfolio-Ausstiegslogik basierend auf Zeit- oder Eigenkapitalprozent-Schwellenwerten.

Anstatt sich auf Indikatoren zu stützen, verwendet die Strategie einen Ein-Minuten-Kerzenstrom (konfigurierbar) rein als Zeitquelle. Jede fertiggestellte Kerze löst Prüfungen aus, um Trades zu öffnen, alles zu schließen wenn das Handelsfenster abläuft, und eigenkapitalbasierte Risikoregeln durchzusetzen. Die Strategie eignet sich daher für Portfolios, bei denen die Ausführung eher durch Zeitplan als durch Preismuster gesteuert wird.

Handelslogik

  1. Instrumentenauswahl – Bis zu zehn Symbole können aktiviert werden. Für jeden aktivierten Eintrag löst die Strategie den Ticker über den SecurityProvider auf, abonniert Kerzen des konfigurierten Typs und verwendet dieselbe Logik für alle Instrumente.
  2. Handelsfenster – wenn der Kerzenzeitstempel in das TradeStartTime-Fenster eintritt (das TradeDuration dauert), öffnet die Strategie eine Marktposition in der konfigurierten Richtung (TradeDirection) für jedes aktivierte Symbol, das noch keine offene Position in dieser Richtung hat. Wenn eine entgegengesetzte Position existiert, wird das Volumen erhöht, um zur gewünschten Seite zu wechseln.
  3. Eigenkapitalschutz – wenn CloseByEquityPercent aktiviert ist und das Portfolio-Eigenkapital vom Startguthaben um PercentProfit oder PercentLoss abweicht, wird jede von der Strategie verwaltete offene Position geschlossen.
  4. Zeitbasierter Ausstieg – wenn UseTimeClose aktiviert ist, schließt die Strategie alle verfolgten Positionen, wenn die Uhr das CloseTime-Fenster erreicht (das TradeDuration dauert).
  5. Protokollierung – Aktionen wie Einstiege, eigenkapitalbasierte Ausstiege und zeitbasierte Ausstiege werden durch LogInfo-Aufrufe für Rückverfolgbarkeit protokolliert.

Parameter

Parameter Beschreibung Standard
TradeDirection Richtung aller Orders (Buy oder Sell). Buy
TradeStartTime Lokalzeit, wenn das Eintrittsfenster öffnet. 19:51
TradeDuration Länge beider Eintritts- und Schließfenster. 00:05:00
UseTimeClose Aktiviert das zeitbasierte Schließfenster. true
CloseTime Lokalzeit, wenn das Schließfenster öffnet. 20:50
CloseByEquityPercent Aktiviert das Schließen aller Positionen bei Eigenkapitalschwellenwerten. true
PercentProfit Prozentuale Eigenkapitalgewinn, der einen globalen Schluss auslöst. 1.0
PercentLoss Prozentualer Eigenkapitalrückgang, der einen globalen Schluss auslöst. 55.0
CandleType Kerzentyp als Planungsantrieb. 1-Minuten-Zeitrahmen
UseSymbol0..9 Schaltet den Handel für das entsprechende Symbol. true für Symbole 0–5, false für 6–9
Symbol0..9 Ticker für jeden Slot, über SecurityProvider.LookupById aufgelöst. Siehe Standardwerte unten
Volume0..9 Ordervolumen für jeden Slot (Lots im Original-EA). 0.1–1.0

Standard-Symbol-Konfiguration

Slot Aktiviert Symbol Volumen
0 EURUSD 0.1
1 GBPUSD 0.2
2 GBPJPY 0.3
3 EURCAD 0.4
4 USDCHF 0.5
5 USDJPY 0.6
6 USDCHF 0.7
7 GBPUSD 0.8
8 EURUSD 0.9
9 USDJPY 1.0

Verwendungshinweise

  • Sicherstellen, dass das Konto Hedging unterstützt, wenn das ursprüngliche MetaTrader-Verhalten repliziert werden soll. Auf Netting-Konten kompensiert die Strategie automatisch entgegengesetzte Positionen beim Wechseln der Richtungen.
  • Instrument-Identifier in den SymbolX-Parametern genau so angeben, wie sie dem StockSharp SecurityProvider bekannt sind (zum Beispiel EURUSD@FXCM).
  • Der Kerzenstrom wird nur verwendet, um die Planungslogik anzutreiben. CandleType anpassen, wenn die Datenquelle ein anderes Aggregationsintervall bereitstellt.
  • Eigenkapitalschutz vergleicht das Live-Eigenkapital mit dem bei OnStarted erfassten Guthaben. Neustart der Strategie setzt das Referenzguthaben zurück.
  • Die Strategie enthält keine Schutz-Stop- oder Take-Profit-Orders. Globale Ausstiege werden ausschließlich durch die Eigenkapitalprozentsätze und das Schließfenster gesteuert.

Konvertierungshinweise

  • Der ursprüngliche MT5-Experte verwendete OnTick. In der StockSharp-Version ersetzen fertiggestellte Kerzen Tick-Ereignisse, um Zeitfenster auf High-Level, ereignisgesteuerte Weise auszuwerten.
  • Magic-Number-Filterung ist unnötig, da die Strategie innerhalb des Strategie-Containers von StockSharp operiert; daher iteriert CloseAllManagedPositions nur durch die konfigurierten Symbole.
  • Sound-Benachrichtigungen und Chart-Kommentare wurden weggelassen, aber die Strategie protokolliert alle kritischen Aktionen über LogInfo für einfachere Prüfung.
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>
/// Hedging scheduler strategy that opens positions during a configurable time window
/// and closes when equity targets are reached or a separate exit window arrives.
/// Simplified to single-security from the original multi-symbol version.
/// </summary>
public class MultiHedgingSchedulerStrategy : Strategy
{
	private readonly StrategyParam<Sides> _tradeDirection;
	private readonly StrategyParam<TimeSpan> _tradeStartTime;
	private readonly StrategyParam<TimeSpan> _tradeDuration;
	private readonly StrategyParam<bool> _enableTimeClose;
	private readonly StrategyParam<TimeSpan> _closeTime;
	private readonly StrategyParam<bool> _enableEquityClose;
	private readonly StrategyParam<decimal> _profitPercent;
	private readonly StrategyParam<decimal> _lossPercent;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _initialBalance;
	private bool _positionOpened;

	/// <summary>
	/// Trading direction used when opening positions.
	/// </summary>
	public Sides TradeDirection
	{
		get => _tradeDirection.Value;
		set => _tradeDirection.Value = value;
	}

	/// <summary>
	/// Time of day when the trading window starts.
	/// </summary>
	public TimeSpan TradeStartTime
	{
		get => _tradeStartTime.Value;
		set => _tradeStartTime.Value = value;
	}

	/// <summary>
	/// Duration of the trading and optional closing windows.
	/// </summary>
	public TimeSpan TradeDuration
	{
		get => _tradeDuration.Value;
		set => _tradeDuration.Value = value;
	}

	/// <summary>
	/// Enables the separate time based close window.
	/// </summary>
	public bool UseTimeClose
	{
		get => _enableTimeClose.Value;
		set => _enableTimeClose.Value = value;
	}

	/// <summary>
	/// Time of day when the closing window starts.
	/// </summary>
	public TimeSpan CloseTime
	{
		get => _closeTime.Value;
		set => _closeTime.Value = value;
	}

	/// <summary>
	/// Enables closing when equity reaches profit or loss thresholds.
	/// </summary>
	public bool CloseByEquityPercent
	{
		get => _enableEquityClose.Value;
		set => _enableEquityClose.Value = value;
	}

	/// <summary>
	/// Percentage profit target based on starting balance.
	/// </summary>
	public decimal PercentProfit
	{
		get => _profitPercent.Value;
		set => _profitPercent.Value = value;
	}

	/// <summary>
	/// Percentage loss threshold based on starting balance.
	/// </summary>
	public decimal PercentLoss
	{
		get => _lossPercent.Value;
		set => _lossPercent.Value = value;
	}

	/// <summary>
	/// Candle series driving the scheduling logic.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Initializes the strategy.
	/// </summary>
	public MultiHedgingSchedulerStrategy()
	{
		_tradeDirection = Param(nameof(TradeDirection), Sides.Buy)
			.SetDisplay("Trade Direction", "Direction used for opening positions", "General");

		_tradeStartTime = Param(nameof(TradeStartTime), new TimeSpan(10, 0, 0))
			.SetDisplay("Trade Start", "Time of day to begin opening positions", "Scheduling");

		_tradeDuration = Param(nameof(TradeDuration), TimeSpan.FromMinutes(5))
			.SetDisplay("Window Length", "Duration of trading and closing windows", "Scheduling");

		_enableTimeClose = Param(nameof(UseTimeClose), true)
			.SetDisplay("Use Close Window", "Enable time based portfolio closing", "Scheduling");

		_closeTime = Param(nameof(CloseTime), new TimeSpan(17, 0, 0))
			.SetDisplay("Close Start", "Time of day to start the close window", "Scheduling");

		_enableEquityClose = Param(nameof(CloseByEquityPercent), true)
			.SetDisplay("Use Equity Targets", "Enable equity based exit", "Risk Management");

		_profitPercent = Param(nameof(PercentProfit), 1m)
			.SetDisplay("Profit %", "Equity percentage gain to close all positions", "Risk Management");

		_lossPercent = Param(nameof(PercentLoss), 55m)
			.SetDisplay("Loss %", "Equity percentage loss to close all positions", "Risk Management");

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(1).TimeFrame())
			.SetDisplay("Candle Type", "Candle series driving the scheduler", "General");
	}

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_initialBalance = 0m;
		_positionOpened = false;
	}

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

		_initialBalance = Portfolio?.CurrentValue ?? 0m;

		SubscribeCandles(CandleType)
			.Bind(ProcessCandle)
			.Start();
	}

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

		if (!IsFormed)
			return;

		var timeOfDay = candle.OpenTime.TimeOfDay;

		if (CloseByEquityPercent && TryHandleEquityTargets())
			return;

		if (UseTimeClose && IsWithinWindow(timeOfDay, CloseTime, TradeDuration))
		{
			if (Position > 0)
				SellMarket(Math.Abs(Position));
			else if (Position < 0)
				BuyMarket(Math.Abs(Position));

			_positionOpened = false;
			return;
		}

		var direction = TradeDirection;

		if (!IsWithinWindow(timeOfDay, TradeStartTime, TradeDuration))
			return;

		if (_positionOpened)
			return;

		var volume = Volume;
		if (volume <= 0m)
			volume = 1m;

		if (direction == Sides.Buy && Position <= 0)
		{
			if (Position < 0)
				BuyMarket(Math.Abs(Position));
			BuyMarket(volume);
			_positionOpened = true;
		}
		else if (direction == Sides.Sell && Position >= 0)
		{
			if (Position > 0)
				SellMarket(Position);
			SellMarket(volume);
			_positionOpened = true;
		}
	}

	private bool TryHandleEquityTargets()
	{
		if (_initialBalance <= 0m)
			return false;

		var equity = Portfolio?.CurrentValue;
		if (equity == null)
			return false;

		var profitLevel = _initialBalance * (1m + PercentProfit / 100m);
		var lossLevel = _initialBalance * (1m - PercentLoss / 100m);

		if (equity.Value >= profitLevel || equity.Value <= lossLevel)
		{
			if (Position > 0)
				SellMarket(Math.Abs(Position));
			else if (Position < 0)
				BuyMarket(Math.Abs(Position));

			_positionOpened = false;
			return true;
		}

		return false;
	}

	private static bool IsWithinWindow(TimeSpan current, TimeSpan start, TimeSpan length)
	{
		if (length <= TimeSpan.Zero)
			return current == start;

		var end = start + length;

		if (end < TimeSpan.FromDays(1))
			return current >= start && current < end;

		var overflow = end - TimeSpan.FromDays(1);
		return current >= start || current < overflow;
	}
}