Auf GitHub ansehen

Check-Execution-Strategie

Überblick

Die Check-Execution-Strategie reproduziert das Verhalten des ursprünglichen MQL-Experts, der eine Brokerorder wiederholt modifiziert, um Ausführungsqualität zu messen. Der Algorithmus kann entweder einen Pending Buy Stop oder einen schützenden Sell Stop testen, der eine mit Marktorder eröffnete Long-Position absichert. Jede Modifikation zeichnet sowohl den beobachteten Spread als auch die Zeit auf, die der Handelsplatz benötigt, um die Änderung zu akzeptieren. Dadurch lassen sich latenzsensitive Bedingungen eines Brokers einfach bewerten.

Kernlogik

  1. Beste Bid-/Ask-Aktualisierungen über die High-Level-API SubscribeLevel1 abonnieren.
  2. Die initiale Testorder abhängig vom gewählten Modus platzieren:
    • Pending - einen Buy Stop oberhalb des aktuellen Ask-Preises senden.
    • Market - zum Markt kaufen und anschließend einen schützenden Sell Stop unterhalb des letzten Ask senden.
  3. Bei jeder Kursaktualisierung:
    • Den rollierenden Durchschnitt des Bid/Ask-Spreads mit SimpleMovingAverage aktualisieren.
    • Die verfolgte Order mit neuem Offset vom Ask-Preis erneut registrieren, wenn eine Änderung nötig ist und keine vorherige Anfrage auf Bestätigung wartet.
    • Die Ausführungslatenz messen, sobald die Order in den Zustand Active zurückkehrt, und sie in einen zweiten SimpleMovingAverage einspeisen, um die laufende durchschnittliche Verzögerung in Millisekunden zu erhalten.
  4. Den Modifikationszyklus wiederholen, bis die konfigurierte Iterationszahl erreicht ist. Danach storniert die Strategie verbleibende Pending-/Stop-Orders, schließt bei Bedarf die eröffnete Long-Position und gibt aggregierte Spread- und Latenzstatistiken aus.

Parameter

Name Beschreibung Standard
Volume Handelsvolumen für jede Order. 0.01
Iterations Anzahl der Modifikationsversuche für die Mittelwertbildung. Auf 1-500 begrenzt. 30
Order Mode Wählt den Ablauf: Pending oder Market. Pending
Pending Offset Distanz in Preisschritten über dem Ask für den Test-Buy-Stop. 100
Stop Offset Distanz in Preisschritten unter dem Ask für den schützenden Sell Stop. 100

Verhaltenshinweise

  • Volumenwerte werden auf die VolumeStep-, MinVolume- und MaxVolume-Beschränkungen des Wertpapiers normalisiert, um abgelehnte Orders zu vermeiden.
  • Preis-Offsets werden mit dem Instrumenten-PriceStep in tatsächliche Preise übersetzt. Ein Standardschritt von 0.0001 wird verwendet, wenn das Wertpapier keinen bereitstellt.
  • Die Strategie zählt eine Modifikation nur, wenn der Handelsplatz die Anfrage bestätigt, indem er die Order in den Zustand Active oder Done bewegt. Jede Bestätigung aktualisiert sowohl den Ausführungstimer als auch den Modifikationszähler.
  • Sobald die Zielanzahl von Iterationen erreicht ist, beendet die Strategie automatisch Orderänderungen, storniert Pending-Schutz, schließt jede Testposition und protokolliert eine Zusammenfassung mit den gemessenen Mittelwerten.

Unterschiede zur MQL-Version

  • Spread- und Ausführungsdurchschnitte werden mit StockSharp-SimpleMovingAverage-Indikatoren statt manuellen Arrays berechnet.
  • Ordermanagement verwendet High-Level-Helfer wie BuyMarket, BuyStop, SellStop und ReRegisterOrder, um mit dem StockSharp-Strategieframework konsistent zu bleiben.
  • Benutzeroberflächenfeedback wird über das Strategielog statt über Chart-Kommentare und grafische Objekte bereitgestellt.
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;

public class CheckExecutionStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _takeProfitPoints;

	private ExponentialMovingAverage _fast;
	private ExponentialMovingAverage _slow;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;
	private int _cooldown;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
	public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }

	public CheckExecutionStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
		_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
		_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
		_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
	}

	protected override void OnReseted()
	{
		base.OnReseted();
		_fast = null; _slow = null;
		_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_fast = new ExponentialMovingAverage { Length = FastPeriod };
		_slow = new ExponentialMovingAverage { Length = SlowPeriod };
		var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
		subscription.Bind(_fast, _slow, ProcessCandle);
		subscription.Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
		if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }

		var close = candle.ClosePrice;
		var step = Security?.PriceStep ?? 1m;

		if (Position > 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}
		else if (Position < 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}

		if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
		{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
		else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
		{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }

		_prevFast = fastValue; _prevSlow = slowValue;
	}
}