Auf GitHub ansehen

Zwei ausstehende Orders 2

Überblick

Diese Strategie ist eine StockSharp-Portierung des MetaTrader-Expertenberaters "Zwei ausstehende Aufträge 2". Es hält zwei symmetrische ausstehende Aufträge um den Marktpreis herum und ermöglicht der zuerst ausgelösten Seite, den Handel mit konfigurierbaren Stop-Loss-, Take-Profit- und Trailing-Regeln zu verwalten. Die Konvertierung verwendet die hohe Ebene StockSharp API und behält die Kernideen des ursprünglichen Algorithmus bei, während jeder Einstellknopf durch Strategieparameter freigelegt wird.

Handelslogik

  1. Die Strategie abonniert die ausgewählte Kerzenserie (standardmäßig tägliche Kerzen). Wenn eine Kerze fertig ist, wird sie zum Entscheidungspunkt für den nächsten Handelszyklus.
  2. Aktive ausstehende Orders werden storniert, sobald sie ablaufen oder bevor neue Bestellungen aufgegeben werden. Dies garantiert, dass es nur die frischesten Sorten auf dem Markt gibt.
  3. Wenn der aktuelle Spread innerhalb des zulässigen Schwellenwerts liegt und die Anzahl der aktiven Positionen/Orders unter dem konfigurierten Limit liegt, platziert die Strategie zwei symmetrische ausstehende Orders:
    • Stoppmodus (Standardeinstellung) platziert einen Kaufstopp über dem Markt und einen Verkaufsstopp darunter.
    • Im Limit-Modus wird ein Kauflimit unterhalb des Marktes und ein Verkaufslimit darüber gesetzt.
    • Das Flag Reverse Levels tauscht die Ordertypen aus, um den ursprünglichen EA-Reverse-Wechsel zu reproduzieren.
  4. Die Einstiegspreise werden durch den Parameter Pending Indent vom aktuellen Geld-/Briefkurs abgesetzt. Aufträge werden übersprungen, wenn sie näher als der Min Step-Abstand zu bestehenden Positionen liegen.
  5. Ausstehende Bestellungen können nach einer bestimmten Anzahl von Minuten ablaufen. Bei Ablauf werden alle verbleibenden Bestellungen storniert.

Positionsmanagement

  • Sobald eine Order ausgeführt wurde, verfolgt die Strategie den durchschnittlichen Einstiegspreis und das durchschnittliche Volumen für die entsprechende Seite. Gegenüberliegende Füllungen verkleinern oder schließen die bestehende Position, bevor sie eine neue eröffnen.
  • Die Strategie beendet Long-Positionen, wenn der Preis eine dieser Bedingungen erreicht:
    • Der Preis berührt die Stop-Loss-Distanz unterhalb des durchschnittlichen Einstiegspreises.
    • Der Preis erreicht die Take-Profit-Distanz über dem durchschnittlichen Einstiegspreis.
    • Ein Trailing-Stop wird aktiviert, nachdem der Gewinn die Aktivierungsschwelle überschreitet und der Preis anschließend auf das Trailing-Niveau zurückfällt (in Schritten).
  • Short-Trades nutzen die gespiegelten Regeln mit invertierten Preisvergleichen.
  • Wenn Nur eine Position aktiviert ist, wartet die Engine darauf, dass das aktuelle Engagement geschlossen wird, bevor neue ausstehende Aufträge aufgegeben werden.

Parameter

Name Beschreibung
StopLossPoints Abstand zum schützenden Stop-Loss in Punkten (0 deaktiviert ihn).
TakeProfitPoints Entfernung zum Take-Profit-Ziel in Punkten (0 deaktiviert es).
MaxPositions Maximale Anzahl gleichzeitig aktiver Positionen und ausstehender Aufträge.
MinStepPoints Mindestabstand zwischen dem Einstiegspreis bestehender Geschäfte und neuen ausstehenden Aufträgen.
TrailingActivatePoints Gewinnschwelle, die den Trailing Stop aktiviert (0 deaktiviert das Trailing).
TrailingStopPoints Abstand zwischen Marktpreis und Trailing Stop nach Aktivierung.
TrailingStepPoints Minimale Preisverbesserung, die erforderlich ist, um den Trailing Stop erneut zu verschieben.
TradeMode Zulässige Richtung für neue ausstehende Orders: Buy, Sell oder BuySell.
PendingType Art der ausstehenden Aufträge, die aufgegeben werden sollen: Stop oder Limit.
PendingExpirationMinutes Lebensdauer ausstehender Bestellungen in Minuten (0 behält sie bei, bis sie manuell ausgeführt oder storniert werden).
PendingIndentPoints Offset vom aktuellen Geld-/Briefkurs, der zur Berechnung der Preise für ausstehende Aufträge verwendet wird.
PendingMaxSpreadPoints Maximal zulässige Spanne zwischen Bid und Ask, um ausstehende Aufträge zu erteilen (0 deaktiviert den Filter).
OnlyOnePosition Bei true wird die Eröffnung neuer Trades verhindert, bis die aktuelle Position geschlossen ist.
ReverseLevels Vertauscht die Platzierung von Kauf- und Verkaufsaufträgen, um den ursprünglichen EA-Umkehrmodus widerzuspiegeln.
CandleType Zeitrahmen, der zum Auslösen der Signalauswertung verwendet wird (standardmäßig täglich).

Notizen

  • Preisabstände werden in Punkten ausgedrückt und automatisch in die Tick-Größe des Instruments umgerechnet.
  • Die Strategie basiert auf StockSharp-Hilfsmethoden (BuyStop, SellStop, BuyLimit, SellLimit) für die Auftragsregistrierung und verwendet CancelActiveOrders, um das Buch jedes Mal zurückzusetzen, wenn eine neue Entscheidung getroffen wird.
  • Die Trailing-Stop-Logik wird bei fertigen Kerzen ausgewertet. Für das Intrabar-Trailing-Verhalten verwenden Sie ein kürzeres CandleType.
using System;
using System.Collections.Generic;

using Ecng.Common;

using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Simplified from "Two pending orders 2" MetaTrader expert.
/// Places symmetric breakout levels around recent range and enters on breakout.
/// Uses high/low of N bars as breakout boundaries.
/// </summary>
public class TwoPendingOrders2Strategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _lookback;

	private readonly Queue<decimal> _highs = new();
	private readonly Queue<decimal> _lows = new();

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public int Lookback
	{
		get => _lookback.Value;
		set => _lookback.Value = value;
	}

	public TwoPendingOrders2Strategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe for breakout detection", "General");

		_lookback = Param(nameof(Lookback), 10)
			.SetGreaterThanZero()
			.SetDisplay("Lookback", "Number of bars for high/low range", "Indicators");
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_highs.Clear();
		_lows.Clear();

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(ProcessCandle)
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawOwnTrades(area);
		}
	}

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

		if (_highs.Count < Lookback)
		{
			EnqueueCandle(candle);
			return;
		}

		decimal highest = decimal.MinValue;
		decimal lowest = decimal.MaxValue;
		var highs = _highs.ToArray();
		var lows = _lows.ToArray();
		foreach (var h in highs)
			if (h > highest) highest = h;
		foreach (var l in lows)
			if (l < lowest) lowest = l;

		var close = candle.ClosePrice;
		var volume = Volume;
		if (volume <= 0)
			volume = 1;
		var range = highest - lowest;
		var breakoutPadding = range * 0.05m;

		// Breakout above range
		if (close > highest + breakoutPadding)
		{
			if (Position <= 0)
				BuyMarket(Position < 0 ? Math.Abs(Position) + volume : volume);
		}
		// Breakout below range
		else if (close < lowest - breakoutPadding)
		{
			if (Position >= 0)
				SellMarket(Position > 0 ? Math.Abs(Position) + volume : volume);
		}

		EnqueueCandle(candle);
	}

	private void EnqueueCandle(ICandleMessage candle)
	{
		_highs.Enqueue(candle.HighPrice);
		_lows.Enqueue(candle.LowPrice);

		if (_highs.Count > Lookback)
		{
			_highs.Dequeue();
			_lows.Dequeue();
		}
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		_highs.Clear();
		_lows.Clear();

		base.OnReseted();
	}
}