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
- 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.
- 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.
- 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.
- Zeitbasierter Ausstieg – wenn
UseTimeClose aktiviert ist, schließt die Strategie alle verfolgten Positionen, wenn die Uhr das CloseTime-Fenster erreicht (das TradeDuration dauert).
- 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;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes
from StockSharp.Algo.Strategies import Strategy
SIDE_BUY = 0
SIDE_SELL = 1
class multi_hedging_scheduler_strategy(Strategy):
def __init__(self):
super(multi_hedging_scheduler_strategy, self).__init__()
self._trade_direction = self.Param("TradeDirection", SIDE_BUY)
self._trade_start_hour = self.Param("TradeStartHour", 10)
self._trade_start_minute = self.Param("TradeStartMinute", 0)
self._trade_duration_minutes = self.Param("TradeDurationMinutes", 5)
self._enable_time_close = self.Param("UseTimeClose", True)
self._close_hour = self.Param("CloseHour", 17)
self._close_minute = self.Param("CloseMinute", 0)
self._enable_equity_close = self.Param("CloseByEquityPercent", True)
self._profit_percent = self.Param("PercentProfit", 1.0)
self._loss_percent = self.Param("PercentLoss", 55.0)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(1)))
self._initial_balance = 0.0
self._position_opened = False
@property
def TradeDirection(self):
return self._trade_direction.Value
@TradeDirection.setter
def TradeDirection(self, value):
self._trade_direction.Value = value
@property
def TradeStartHour(self):
return self._trade_start_hour.Value
@TradeStartHour.setter
def TradeStartHour(self, value):
self._trade_start_hour.Value = value
@property
def TradeStartMinute(self):
return self._trade_start_minute.Value
@TradeStartMinute.setter
def TradeStartMinute(self, value):
self._trade_start_minute.Value = value
@property
def TradeDurationMinutes(self):
return self._trade_duration_minutes.Value
@TradeDurationMinutes.setter
def TradeDurationMinutes(self, value):
self._trade_duration_minutes.Value = value
@property
def UseTimeClose(self):
return self._enable_time_close.Value
@UseTimeClose.setter
def UseTimeClose(self, value):
self._enable_time_close.Value = value
@property
def CloseHour(self):
return self._close_hour.Value
@CloseHour.setter
def CloseHour(self, value):
self._close_hour.Value = value
@property
def CloseMinute(self):
return self._close_minute.Value
@CloseMinute.setter
def CloseMinute(self, value):
self._close_minute.Value = value
@property
def CloseByEquityPercent(self):
return self._enable_equity_close.Value
@CloseByEquityPercent.setter
def CloseByEquityPercent(self, value):
self._enable_equity_close.Value = value
@property
def PercentProfit(self):
return self._profit_percent.Value
@PercentProfit.setter
def PercentProfit(self, value):
self._profit_percent.Value = value
@property
def PercentLoss(self):
return self._loss_percent.Value
@PercentLoss.setter
def PercentLoss(self, value):
self._loss_percent.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def _is_within_window(self, current_minutes, start_hour, start_minute, duration_minutes):
start_total = start_hour * 60 + start_minute
end_total = start_total + duration_minutes
if end_total < 1440:
return current_minutes >= start_total and current_minutes < end_total
else:
overflow = end_total - 1440
return current_minutes >= start_total or current_minutes < overflow
def OnStarted2(self, time):
super(multi_hedging_scheduler_strategy, self).OnStarted2(time)
self._initial_balance = 0.0
self._position_opened = False
self.SubscribeCandles(self.CandleType).Bind(self.ProcessCandle).Start()
self.StartProtection(
Unit(2000.0, UnitTypes.Absolute),
Unit(1000.0, UnitTypes.Absolute))
def ProcessCandle(self, candle):
if candle.State != CandleStates.Finished:
return
if self._initial_balance == 0.0 and self.Portfolio is not None and self.Portfolio.CurrentValue is not None:
self._initial_balance = float(self.Portfolio.CurrentValue)
open_time = candle.OpenTime
hour = open_time.Hour
minute = open_time.Minute
current_minutes = hour * 60 + minute
duration = int(self.TradeDurationMinutes)
if self.CloseByEquityPercent and self._try_handle_equity_targets():
return
if self.UseTimeClose and self._is_within_window(current_minutes, int(self.CloseHour), int(self.CloseMinute), duration):
if self.Position > 0:
self.SellMarket()
elif self.Position < 0:
self.BuyMarket()
self._position_opened = False
return
if not self._is_within_window(current_minutes, int(self.TradeStartHour), int(self.TradeStartMinute), duration):
return
if self._position_opened:
return
direction = int(self.TradeDirection)
if direction == SIDE_BUY and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._position_opened = True
elif direction == SIDE_SELL and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._position_opened = True
def _try_handle_equity_targets(self):
if self._initial_balance <= 0.0:
return False
if self.Portfolio is None or self.Portfolio.CurrentValue is None:
return False
equity = float(self.Portfolio.CurrentValue)
profit_level = self._initial_balance * (1.0 + float(self.PercentProfit) / 100.0)
loss_level = self._initial_balance * (1.0 - float(self.PercentLoss) / 100.0)
if equity >= profit_level or equity <= loss_level:
if self.Position > 0:
self.SellMarket()
elif self.Position < 0:
self.BuyMarket()
self._position_opened = False
return True
return False
def OnReseted(self):
super(multi_hedging_scheduler_strategy, self).OnReseted()
self._initial_balance = 0.0
self._position_opened = False
def CreateClone(self):
return multi_hedging_scheduler_strategy()