Auf GitHub ansehen

Close-Order-Strategie senden

Send Close Order ist eine Portierung des 2009 MetaTrader 4 Expertenberaters „SendCloseOrder“ von Vladimir Hlystov. Das ursprüngliche Skript zeichnet vier manuelle Trendlinien basierend auf Bill Williams-Fraktalen und öffnet oder schließt Marktaufträge, wann immer der Preis eines dieser prognostizierten Niveaus berührt. Die StockSharp-Version repliziert die Entscheidungslogik mit vollautomatischem Linienmanagement und funktioniert auf allen von der Plattform bereitgestellten Kerzenserien.

Handelslogik

  1. Fraktale Erkennung – jede fertige Kerze aktualisiert ein Schiebefenster mit fünf Balken. Sobald das Fenster voll ist, wird die Kerze in der Mitte mit den fraktalen Bedingungen von Bill Williams verglichen. Bestätigte Höchst- und Tiefstwerte werden chronologisch gespeichert.
  2. Trendlinienrekonstruktion
    • Verkaufslinie verbindet die letzten beiden Aufwärtsfraktale, die durch ein Abwärtsfraktal getrennt sind, und bildet eine Widerstandssteigung.
    • Close #1 ist die Verkaufslinie, die um 15 Preisschritte (15 × Security.PriceStep) nach oben verschoben wird und als lange Ausstiegsschiene fungiert.
    • Kauflinie verbindet die letzten beiden Abwärtsfraktale, die durch ein Aufwärtsfraktal getrennt sind, und bildet eine Unterstützungsneigung.
    • Close #2 ist die um 15 Preisschritte nach unten verschobene Kauflinie und fungiert als Short-Exit-Schiene.
  3. Signalauswertung – die vier Zeilen werden auf den Zeitstempel der fertigen Kerze hochgerechnet. Liegt der prognostizierte Preis innerhalb des Hoch-/Tief-Bereichs der Kerze (mit einer kleinen Toleranz von zwei Preisschritten), wird die entsprechende Aktion ausgelöst.
  4. Auftragsverwaltung
    • Durch Berühren von „Schließen Nr. 1“ oder „Schließen Nr. 2“ wird die gesamte Position sofort über ClosePosition() geschlossen.
    • Durch Berühren der Verkaufs- oder Kauflinie wird eine Marktorder mit einem Volumen von TradeVolume geöffnet, sofern die resultierende absolute Position MaxOrders × TradeVolume nicht überschreitet. Wenn eine Gegenposition vorhanden ist, gleicht die Order diese zunächst aus und stapelt dann einen neuen Eintrag, was dem Verhalten von Sicherungskonten entspricht.

Parameter

Name Standard Beschreibung
EnableSellLine true Erlauben Sie Trades, wenn die prognostizierte Widerstandslinie erreicht wird.
EnableBuyLine true Erlauben Sie Trades, wenn die prognostizierte Unterstützungslinie erreicht wird.
EnableCloseLongLine true Ermöglichen Sie das Schließen von Long-Positionen auf der verschobenen Widerstandslinie (Schluss Nr. 1).
EnableCloseShortLine true Erlauben Sie das Schließen von Short-Positionen auf der verschobenen Unterstützungslinie (Schluss #2).
MaxOrders 1 Maximale Anzahl gestapelter Einträge in der aktuellen Richtung.
TradeVolume 0.1 Volumen jeder einzelnen Market Order.
CandleType 1h Zeitrahmen Für fraktale Berechnungen verwendete Kerzenserie.

Unterschiede zur MetaTrader-Version

  • Der Port StockSharp berechnet die vier Linien jedes Mal neu, wenn ein neues Fraktal erscheint. In MetaTrader musste der Benutzer Trendlinien manuell löschen und neu zeichnen.
  • Die Ausführung basiert auf aggregierten Nettopositionen; Gleichzeitige Long- und Short-Baskets werden vom Standardportfoliomodell von StockSharp nicht unterstützt.
  • Die Berührungserkennung verwendet das Hoch/Tief der fertigen Kerze mit einer Preisschritttoleranz anstelle der momentanen Bid/Ask-Kurse von Ticks.
  • Diagrammobjekte (Trendlinien und Beschriftungen) werden nicht erstellt; Der Fokus liegt auf Handelssignalen.

Nutzungshinweise

  • Die Strategie kann auf jedem Instrument ausgeführt werden, das Kerzen und einen gültigen PriceStep bereitstellt. Wenn Security.PriceStep Null ist, fällt der Code auf 0.0001 zurück.
  • Erhöhen Sie MaxOrders, um das Stapelverhalten des ursprünglichen EA zu emulieren. Halten Sie TradeVolume an der Losgröße des Instruments ausgerichtet, um Rundungen zu vermeiden.
  • Der Linienversatz ist auf den historischen Wert von 15 Punkten festgelegt. Passen Sie den Quellcode an, wenn die Eingabe MetaTrader geändert wird.

Es wird nur die C#-Implementierung bereitgestellt. Eine Python-Übersetzung wird bei Bedarf separat hinzugefügt.

using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// SendCloseOrder: Fractal high/low breakout with EMA filter and ATR stops.
/// </summary>
public class SendCloseOrderStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _emaLength;
	private readonly StrategyParam<int> _atrLength;

	private decimal _entryPrice;
	private decimal _fractalHigh;
	private decimal _fractalLow;
	private decimal _prev2High;
	private decimal _prev1High;
	private decimal _prev2Low;
	private decimal _prev1Low;
	private int _barCount;

	public SendCloseOrderStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe.", "General");

		_emaLength = Param(nameof(EmaLength), 50)
			.SetDisplay("EMA Length", "Trend filter.", "Indicators");

		_atrLength = Param(nameof(AtrLength), 14)
			.SetDisplay("ATR Length", "ATR period.", "Indicators");
	}

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

	public int EmaLength
	{
		get => _emaLength.Value;
		set => _emaLength.Value = value;
	}

	public int AtrLength
	{
		get => _atrLength.Value;
		set => _atrLength.Value = value;
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_entryPrice = 0;
		_fractalHigh = 0;
		_fractalLow = 0;
		_prev2High = 0;
		_prev1High = 0;
		_prev2Low = 0;
		_prev1Low = 0;
		_barCount = 0;
	}

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

		_entryPrice = 0;
		_fractalHigh = 0;
		_fractalLow = 0;
		_prev2High = 0;
		_prev1High = 0;
		_prev2Low = 0;
		_prev1Low = 0;
		_barCount = 0;

		var ema = new ExponentialMovingAverage { Length = EmaLength };
		var atr = new AverageTrueRange { Length = AtrLength };

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

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

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

		_barCount++;

		var high = candle.HighPrice;
		var low = candle.LowPrice;
		var close = candle.ClosePrice;

		// Detect fractal high: prev1High > prev2High and prev1High > current high
		if (_barCount > 3 && _prev1High > _prev2High && _prev1High > high)
			_fractalHigh = _prev1High;

		// Detect fractal low: prev1Low < prev2Low and prev1Low < current low
		if (_barCount > 3 && _prev1Low < _prev2Low && _prev1Low < low)
			_fractalLow = _prev1Low;

		_prev2High = _prev1High;
		_prev1High = high;
		_prev2Low = _prev1Low;
		_prev1Low = low;

		if (_fractalHigh == 0 || _fractalLow == 0 || atrVal <= 0)
			return;

		if (Position > 0)
		{
			if (close >= _entryPrice + atrVal * 3m || close <= _entryPrice - atrVal * 1.5m)
			{
				SellMarket();
				_entryPrice = 0;
			}
		}
		else if (Position < 0)
		{
			if (close <= _entryPrice - atrVal * 3m || close >= _entryPrice + atrVal * 1.5m)
			{
				BuyMarket();
				_entryPrice = 0;
			}
		}

		if (Position == 0)
		{
			if (close > _fractalHigh && close > emaVal)
			{
				_entryPrice = close;
				BuyMarket();
			}
			else if (close < _fractalLow && close < emaVal)
			{
				_entryPrice = close;
				SellMarket();
			}
		}
	}
}