Auf GitHub ansehen

Master-Exit-Plan-Strategie

Überblick

MasterExitPlanStrategy reproduziert die Risikomanagementlogik des Expertenberaters „Master Exit Plan“ von MetaTrader unter Verwendung des übergeordneten API von StockSharp. Die Strategie eröffnet keine neuen Trades. Stattdessen überwacht es das bestehende Risiko, wendet eine Kombination aus versteckten und sichtbaren Stop-Regeln an, verfolgt ausstehende Aufträge und schließt alles, sobald das Eigenkapital ein konfiguriertes Gewinnziel erreicht.

Die Implementierung abonniert einminütige Kerzen, um die iOpen(symbol, PERIOD_M1, 1)-Aufrufe aus dem Originalskript zu emulieren. Alle Timer werden vom Strategieplaner gesteuert und jede Sekunde ausgewertet, passend zum Verhalten der MetaTrader EventSetTimer(1)-Schleife.

Funktionen

  • Equity Target Exit – schließt alle Positionen, wenn die Portfolio-Aktiengewinne den konfigurierten Prozentsatz erreichen.
  • Statische und dynamische Stop-Level – überwacht sowohl Stop-Abstände vom Einstiegspreis als auch minutenbasierte dynamische Anker.
  • Hidden Stop Handling – führt Schutzexits intern aus, anstatt sich auf Börsenaufträge zu verlassen.
  • Trailing-Stop-Modul – wird nach einem minimalen Geldgewinn aktiviert und folgt dem Stop mit Spread-Kompensation.
  • Pending Order Trailing – Buy-Stop- und Sell-Stop-Orders werden automatisch neu registriert, damit sie dem Markt folgen.

Parameter

Name Beschreibung Standard
EnableTargetEquity Ermöglichen Sie die Liquidation von Eigenkapitalzielen. false
TargetEquityPercent Prozentsatz des aktuellen Saldos, der als Ziel verwendet wird. 1
EnableStopLoss Aktivieren Sie einen statischen Stop-Loss im Broker-Stil. false
StopLossPoints Statischer Stoppabstand (MetaTrader Punkte). 2000
EnableDynamicStopLoss Binden Sie den harten Anschlag an die Öffnung der vorherigen Minute. false
DynamicStopLossPoints Dynamischer Stoppabstand (Punkte). 2000
EnableHiddenStopLoss Aktivieren Sie versteckten statischen Stop-Loss. false
HiddenStopLossPoints Verborgener statischer Stoppabstand (Punkte). 800
EnableHiddenDynamicStopLoss Aktivieren Sie den versteckten dynamischen Stopp basierend auf der offenen Minute. false
HiddenDynamicStopLossPoints Verborgener dynamischer Stoppabstand (Punkte). 800
EnableTrailingStop Aktivieren Sie das Trailing-Stop-Modul. false
TrailingStopPoints Der Rückstand hinter dem Preis (Punkte) bleibt erhalten. 5
TrailingTargetPercent Mindestgewinn in % des Guthabens, bevor das Trailing aktiviert wird. 0.2
SureProfitPoints Zusätzliche Punkte, die gesichert werden müssen, bevor der Trailing Stop aktiviert wird. 30
EnableTrailPendingOrders Aktivieren Sie das Nachverfolgen aktiver Stop-Orders (Einträge). false
TrailPendingOrderPoints Offset in Punkten für nachlaufende ausstehende Stop-Orders. 10

Nutzungshinweise

  1. Hängen Sie die Strategie an ein Wertpapier an, das bereits von einem anderen Eingabemodul oder manuellen Orders verwaltet wird. Legen Sie Volume entsprechend den Verträgen fest, die Sie bei der Reduzierung schließen müssen.
  2. Stellen Sie ein Portfolio bereit, das Portfolio.CurrentValue meldet. Die Strategie verwendet diesen Wert, um AccountBalance und AccountEquity aus MetaTrader zu approximieren. Fehlt der Wert, bleibt die Eigenkapitalziellogik untätig.
  3. Die Strategie bewertet die besten Geld-/Briefkurse bei der Überprüfung der Stoppbedingungen. Stellen Sie sicher, dass Level-1-Daten verfügbar sind, damit Spread-Aware-Berechnungen aussagekräftig sind.
  4. Hidden Stops und Trailing Exits werden als softwaregesteuerte Market Orders umgesetzt. Brokerseitige Stop-Orders werden nicht erstellt; Das Verhalten spiegelt die „verborgene“ Natur des ursprünglichen EA wider.

Unterschiede zur MQL-Version

  • Stop-Levels werden durch die Erteilung von Marktaufträgen durchgesetzt, wenn Schwellenwerte überschritten werden. Der ursprüngliche EA hat das Feld OrderStopLoss geändert; StockSharp verwendet stattdessen aktive Überwachung.
  • Dynamische Stoppberechnungen basieren auf der letzten abgeschlossenen einminütigen Kerze, die über SubscribeCandles geliefert wurde. Wenn dieses Abonnement fehlt, bleiben dynamische Regeln deaktiviert.
  • Pending Order Trailing ignoriert schützende Stop-Orders, die von anderen Strategien erstellt wurden, da MasterExitPlanStrategy sie selbst nicht registriert.
  • Eigenkapitalprüfungen verwenden Portfolio.CurrentValue (Fallback auf Portfolio.BeginValue) anstelle von AccountBalance/AccountEquity.

Testen

Die Strategie enthält keine automatisierten Tests. Verwenden Sie den Tester von StockSharp mit historischen Daten, um das Verhalten Ihrer Instrumente vor der Bereitstellung in der Produktion zu überprüfen.

namespace StockSharp.Samples.Strategies;

using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;

/// <summary>
/// Master Exit Plan strategy: EMA trend following with ATR-based trailing stop exit.
/// Enters on EMA crossover, exits when price retraces by ATR multiple.
/// </summary>
public class MasterExitPlanStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _atrPeriod;
	private readonly StrategyParam<decimal> _atrMultiplier;

	private decimal _entryPrice;
	private decimal _trailStop;
	private bool _wasBullish;
	private bool _hasTrendState;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int AtrPeriod { get => _atrPeriod.Value; set => _atrPeriod.Value = value; }
	public decimal AtrMultiplier { get => _atrMultiplier.Value; set => _atrMultiplier.Value = value; }

	public MasterExitPlanStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
		_fastPeriod = Param(nameof(FastPeriod), 20)
			.SetGreaterThanZero()
			.SetDisplay("Fast EMA", "Fast EMA period", "Indicators");
		_slowPeriod = Param(nameof(SlowPeriod), 60)
			.SetGreaterThanZero()
			.SetDisplay("Slow EMA", "Slow EMA period", "Indicators");
		_atrPeriod = Param(nameof(AtrPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("ATR Period", "ATR period for trailing stop", "Risk");
		_atrMultiplier = Param(nameof(AtrMultiplier), 3m)
			.SetDisplay("ATR Multiplier", "ATR multiplier for trailing distance", "Risk");
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_entryPrice = 0;
		_trailStop = 0;
		_wasBullish = false;
		_hasTrendState = false;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_entryPrice = 0;
		_trailStop = 0;
		_wasBullish = false;
		_hasTrendState = false;
		var fastEma = new ExponentialMovingAverage { Length = FastPeriod };
		var slowEma = new ExponentialMovingAverage { Length = SlowPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(fastEma, slowEma, ProcessCandle).Start();
	}

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

		var close = candle.ClosePrice;
		var range = candle.HighPrice - candle.LowPrice;
		if (range <= 0) return;

		var trailDist = range * AtrMultiplier;
		var isBullish = fastValue > slowValue;
		var crossedUp = _hasTrendState && !_wasBullish && isBullish;
		var crossedDown = _hasTrendState && _wasBullish && !isBullish;

		if (Position > 0)
		{
			var newStop = close - trailDist;
			if (newStop > _trailStop)
				_trailStop = newStop;
			if (close < _trailStop)
			{
				SellMarket();
				_trailStop = 0;
				_entryPrice = 0;
				_wasBullish = isBullish;
				_hasTrendState = true;
				return;
			}
			else if (crossedDown)
			{
				SellMarket();
				_trailStop = 0;
				_entryPrice = 0;
				_wasBullish = isBullish;
				_hasTrendState = true;
				return;
			}
		}
		else if (Position < 0)
		{
			var newStop = close + trailDist;
			if (_trailStop == 0 || newStop < _trailStop)
				_trailStop = newStop;
			if (close > _trailStop)
			{
				BuyMarket();
				_trailStop = 0;
				_entryPrice = 0;
				_wasBullish = isBullish;
				_hasTrendState = true;
				return;
			}
			else if (crossedUp)
			{
				BuyMarket();
				_trailStop = 0;
				_entryPrice = 0;
				_wasBullish = isBullish;
				_hasTrendState = true;
				return;
			}
		}

		if (Position == 0)
		{
			if (crossedUp)
			{
				BuyMarket();
				_entryPrice = close;
				_trailStop = close - trailDist;
			}
			else if (crossedDown)
			{
				SellMarket();
				_entryPrice = close;
				_trailStop = close + trailDist;
			}
		}

		_wasBullish = isBullish;
		_hasTrendState = true;
	}
}