Auf GitHub ansehen

Exp Skyscraper Fix Duplex-Strategie

Überblick

Exp Skyscraper Fix Duplex ist ein Port des MQL5-Expert Advisors Exp_Skyscraper_Fix_Duplex. Die Strategie führt den Skyscraper Fix-Kanal auf der Long- und Short-Seite unabhängig aus, sodass jede Seite ihren eigenen Zeitrahmen, ATR-Fenster und Sensitivität verwenden kann. Long- und Short-Trades können daher auf unterschiedliche Marktregimes reagieren, während sie dieselbe Ausführungslogik innerhalb von StockSharp teilen.

Indikatorlogik

Der benutzerdefinierte Skyscraper Fix-Indikator reproduziert das ursprüngliche Skript:

  • Ein ATR mit einer festen internen Periode von 15 wird für jede abgeschlossene Kerze berechnet.
  • Die höchsten und niedrigsten ATR-Werte über das konfigurierbare Length-Fenster bestimmen den adaptiven Preisschritt.
  • Abhängig vom ausgewählten Mode werden entweder der Hoch/Tief der Kerze oder der Schlusskurs verwendet, um obere und untere Kanalniveaus im doppelten Schrittabstand zu projizieren.
  • Der jüngste Ausbruch über das obere Niveau oder unter das untere Niveau kippt den internen Trend und fixiert das Trailing-Niveau so, dass es sich nie gegen den aktuellen Bias bewegt.
  • Das Kreuzen der entgegengesetzten Trail-Linie erzeugt diskrete Kauf- oder Verkaufsauslöser (spiegelt die Pfeil-Buffer des Indikators in MQL wider).

Der Indikator exponiert das obere Trailing-Niveau, das untere Trailing-Niveau, Einstiegsauslöser und eine Mittellinie, die bei Bedarf geplottet werden kann.

Handelsregeln

Long- und Short-Operationen werden separat für jede abgeschlossene Kerze des jeweiligen Abonnements ausgewertet:

  • Long-Einstieg – ausgelöst, wenn der Long-Indikator ein frisches Kaufniveau meldet. Bestehende Short-Exponierung wird zuerst gedeckt, dann wird eine neue Long-Marktorder mit dem konfigurierten Volumen eingereicht.
  • Long-Ausstieg – ausgelöst, wenn der Long-Indikator die entgegengesetzte Trailing-Linie meldet. Jede bestehende Long-Position wird mit einem Marktverkauf geschlossen.
  • Short-Einstieg – ausgelöst, wenn der Short-Indikator ein frisches Verkaufsniveau meldet. Bestehende Long-Exponierung wird zuerst geschlossen, dann wird eine neue Short-Marktorder gesendet.
  • Short-Ausstieg – ausgelöst, wenn der Short-Indikator die entgegengesetzte Trailing-Linie meldet. Jede aktive Short-Position wird mit einem Marktkauf gedeckt.

Signale können mit den SignalBar-Parametern verzögert werden, damit die Strategie auf der zuletzt geschlossenen Kerze (0) oder auf weiter zurückliegenden Kerzen (1 ahmt die Standard-MQL-Einrichtung nach) reagiert.

Parameter

  • TradeVolume – Ordergröße für Markteinstiege.
  • EnableLongEntries / EnableLongExits – Umschalter für Long-seitigen Handel.
  • LongCandleType – Kerzenserie für den Long-Indikator.
  • LongLength, LongKv, LongPercentage, LongMode, LongSignalBar – Skyscraper Fix-Einstellungen für die Long-Seite.
  • EnableShortEntries / EnableShortExits – Umschalter für Short-seitigen Handel.
  • ShortCandleType – Kerzenserie für den Short-Indikator.
  • ShortLength, ShortKv, ShortPercentage, ShortMode, ShortSignalBar – Skyscraper Fix-Einstellungen für die Short-Seite.

Nutzungshinweise

  • Die Strategie setzt die globale Volume-Eigenschaft aus TradeVolume, sodass Standard-BuyMarket()- und SellMarket()-Aufrufe diese Größe automatisch verwenden.
  • Beide Indikatorinstanzen lesen den PriceStep des Instruments. Wenn dieser null ist, wartet der Indikator still, bis ein gültiger Preisschritt verfügbar ist.
  • StartProtection() wird beim Start aufgerufen, damit Schutzmaßnahmen auf Plattformebene aktiv sind, bevor die erste Order eingereicht wird.
  • Es gibt keine separate Python-Implementierung; das PY-Verzeichnis wird wie angefordert absichtlich weggelassen.
using System;
using System.Collections.Generic;

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

namespace StockSharp.Samples.Strategies;

public class ExpSkyscraperFixDuplexStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private decimal? _prevFast, _prevSlow;

	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 ExpSkyscraperFixDuplexStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame()).SetDisplay("Candle Type", "Timeframe", "General");
		_fastPeriod = Param(nameof(FastPeriod), 9).SetGreaterThanZero().SetDisplay("Fast DEMA", "Fast DEMA period", "Indicators");
		_slowPeriod = Param(nameof(SlowPeriod), 25).SetGreaterThanZero().SetDisplay("Slow DEMA", "Slow DEMA period", "Indicators");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevFast = null;
		_prevSlow = null;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevFast = null; _prevSlow = null;
		var fast = new DoubleExponentialMovingAverage { Length = FastPeriod };
		var slow = new DoubleExponentialMovingAverage { Length = SlowPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(fast, slow, ProcessCandle).Start();
		var area = CreateChartArea();
		if (area != null) { DrawCandles(area, subscription); DrawIndicator(area, fast); DrawIndicator(area, slow); DrawOwnTrades(area); }
	}

	private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!IsFormedAndOnlineAndAllowTrading()) { _prevFast = fast; _prevSlow = slow; return; }
		if (_prevFast == null || _prevSlow == null) { _prevFast = fast; _prevSlow = slow; return; }
		var prevAbove = _prevFast.Value > _prevSlow.Value;
		var currAbove = fast > slow;
		_prevFast = fast; _prevSlow = slow;
		if (!prevAbove && currAbove && Position <= 0) { if (Position < 0) BuyMarket(); BuyMarket(); }
		else if (prevAbove && !currAbove && Position >= 0) { if (Position > 0) SellMarket(); SellMarket(); }
	}
}