Auf GitHub ansehen

Fly-System-Scalp-Strategie

Überblick

Die Fly-System-Scalp-Strategie ist ein hochfrequentes Ausbruchssystem, das das Kernverhalten des ursprünglichen MQL4-Expert-Advisors FlySystemEA reproduziert. Die Strategie überwacht ständig die besten Bid/Ask-Quotes und platziert zwei symmetrische Stop-Orders um den Marktpreis. Ziel ist es, schnelle Mikrotrends nach kurzfristigen Konsolidierungen zu erfassen und Spread, Kommissionen sowie Handelssitzungsgrenzen streng zu kontrollieren.

Die Konvertierung konzentriert sich auf folgende Mechaniken:

  • Automatische Platzierung von Buy-Stop- und Sell-Stop-Orders in konfigurierbarer Entfernung vom Markt.
  • Automatische Stornierung von Pending Orders, wenn der Spread (inklusive Kommission) die zulässige Schwelle überschreitet oder der Handel außerhalb der erlaubten Sitzung liegt.
  • Optionales Take-Profit- und obligatorisches Stop-Loss-Management direkt an neuen Pending Orders.
  • Unterstützung für manuelles Festvolumen und automatische risikobasierte Positionsgröße anhand von Broker-Kontraktspezifikationen (Preisschritt, Schrittwert, Lotschritt, Min-/Max-Volumen).
  • Selbstzurücksetzender Handelszyklus, der auf Positionsschließungen wartet, bevor ein neues Paar Stop-Orders scharfgestellt wird.

Die StockSharp-Implementierung nutzt die High-Level-API (Level-1-Abonnement mit Bind) und folgt den erforderlichen Projektkonventionen: Strategieparameter werden über StrategyParam bereitgestellt, Kommentare sind auf Englisch, und der Namespace verwendet die file-scoped-Deklaration.

Handelslogik

  1. Level-1-Feed: Die Strategie abonniert Level-1-Daten für die zugewiesene Security. Jede Aktualisierung speichert das jüngste Bid/Ask-Paar.
  2. Validierungsschicht: Vor jeder Handelsaktion prüft die Engine:
    • Die Strategie ist online und darf handeln.
    • Die aktuelle Zeit liegt innerhalb des optionalen Handelsfensters.
    • Der Spread plus Kommission überschreitet MaxSpread Pips nicht.
  3. Pending-Order-Platzierung: Wenn die Bedingungen erfüllt sind, keine Position offen ist und die Strategie für einen neuen Zyklus bereit ist, werden zwei Orders vorbereitet:
    • Buy Stop bei Ask + PendingDistance * pip mit schützendem Stop Loss und optionalem Take Profit.
    • Sell Stop bei Bid - PendingDistance * pip mit gespiegelten Schutzwerten. Orders werden neu registriert, wenn die Differenz zwischen gewünschtem und tatsächlichem Preis ModifyThreshold Pips erreicht.
  4. Orderverwaltung: Öffnet sich eine Position, wird die entgegengesetzte Pending Order sofort storniert. Wird ein Handelszyklus durch Spread-/Zeitverletzungen unterbrochen, werden alle Pending Orders entfernt und die Strategie wartet auf gültige Bedingungen.
  5. Positionsgröße: Wenn AutoLotSize aktiviert ist, wird das Volumen aus RiskFactor Prozent der Equity geteilt durch den Verlust pro Kontrakt bei der konfigurierten Stop-Distanz abgeleitet. Das Volumen wird auf den Broker-Lotschritt gerundet und an Min-/Max-Grenzen angepasst.
  6. Schutz: StartProtection() wird aufgerufen, damit StockSharp die Position für Notfallliquidation überwacht, falls die Infrastruktur dies erfordert.

Parameter

Name Beschreibung Standard
PendingDistance Distanz in Pips zwischen Marktpreis und beiden Stop-Orders. 4
StopLossDistance Stop-Loss-Distanz in Pips für neue Positionen. 0.4
TakeProfitDistance Take-Profit-Distanz in Pips, wenn aktiviert. 10
UseTakeProfit Aktiviert Take-Profit-Platzierung. false
MaxSpread Maximal erlaubter Spread (Pips); 0 deaktiviert den Filter. 1
CommissionInPips Kommission (in Pips), die dem Spreadfilter hinzugefügt wird. 0
AutoLotSize Aktiviert risikobasierte Positionsgröße. false
RiskFactor Equity-Prozentsatz für Positionsgröße bei aktivem Auto-Sizing. 10
ManualVolume Festes Volumen, wenn Auto-Sizing deaktiviert ist. 0.1
UseTimeFilter Aktiviert den Handelssitzungsfilter. false
TradeStartTime Sitzungsstartzeit (inklusive). 00:00:00
TradeStopTime Sitzungsende (exklusiv). 00:00:00
ModifyThreshold Preisdelta (Pips), das vor erneuter Registrierung einer Pending Order erforderlich ist. 1

Nutzungshinweise

  • Stellen Sie sicher, dass das Zielinstrument Step, PriceStep, StepPrice, LotStep, MinVolume und MaxVolume bereitstellt, da automatisches Sizing auf diesen Werten beruht. Fehlen Daten, fällt die Strategie sauber auf ManualVolume zurück.
  • Der Pip-Wert wird aus Dezimalpräzision und Preisschritt der Security geschätzt, passend zur ursprünglichen MQL-Logik (inklusive Sonderbehandlung für 3-/5-stellige Forex-Quotes).
  • Wenn TradeStartTime gleich TradeStopTime ist und UseTimeFilter aktiviert ist, gilt die Sitzung als immer offen. Ist die Startzeit größer als die Stoppzeit, läuft die Sitzung über Mitternacht.
  • Spread-Validierung addiert CommissionInPips zum aktuellen Spread und repliziert damit das Verhalten, bei dem die MQL-Version Spread und Kommission in einem Filter kombinierte.
  • Die Strategie erstellt oder verwaltet keine Chartobjekte. Visualisierung kann extern ergänzt werden, indem Level-1-Daten an Charts gebunden werden.

Unterschiede zum ursprünglichen EA

  • Der Low-Level-Tick-Timer und GUI-Elemente der MQL-Version werden bewusst ausgelassen. Die StockSharp-Variante nutzt Level-1-Ereignisse und integriertes Logging.
  • Die Orderänderungslogik ist vereinfacht: Wenn der Zielpreis um mehr als ModifyThreshold Pips abweicht, wird die Order neu registriert statt der mehrzweigigen Anpassungslogik des EA.
  • Automatische Kommissionserkennung aus der Tradehistorie wird durch den statischen Parameter CommissionInPips ersetzt; der Risikofilter addiert diesen Wert dennoch vor dem Handel zum Spread.
  • Die StockSharp-Version nutzt StartProtection() anstelle eigener Stop-Level-Überwachungsschleifen.

Historische Tests

Die Strategie benötigt Level-1-Quote-Daten, um die Stop-Order-Auslöse-Logik zu reproduzieren. Für historische Simulationen liefern Sie Bid/Ask-Serien oder bauen synthetische Level-1-Daten aus Tickhistorie. Reine Candle-Feeds reichen nicht aus, weil Pending Stop Orders auf Spread-Änderungen reagieren müssen.

using System;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Fly System Scalp strategy: EMA crossover + RSI confirmation.
/// Buys when close crosses above EMA and RSI confirms.
/// Sells when close crosses below EMA and RSI confirms.
/// </summary>
public class FlySystemScalpStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _emaPeriod;
	private readonly StrategyParam<int> _rsiPeriod;

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

	public int EmaPeriod
	{
		get => _emaPeriod.Value;
		set => _emaPeriod.Value = value;
	}

	public int RsiPeriod
	{
		get => _rsiPeriod.Value;
		set => _rsiPeriod.Value = value;
	}

	public FlySystemScalpStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");

		_emaPeriod = Param(nameof(EmaPeriod), 20)
			.SetGreaterThanZero()
			.SetDisplay("EMA Period", "EMA period", "Indicators");

		_rsiPeriod = Param(nameof(RsiPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("RSI Period", "RSI period", "Indicators");
	}

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

		var ema = new ExponentialMovingAverage { Length = EmaPeriod };
		var rsi = new RelativeStrengthIndex { Length = RsiPeriod };

		decimal? prevClose = null;
		decimal? prevEma = null;

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(ema, rsi, (candle, emaVal, rsiVal) =>
			{
				if (candle.State != CandleStates.Finished)
					return;

				if (!IsFormedAndOnlineAndAllowTrading())
					return;

				var close = candle.ClosePrice;

				if (prevClose.HasValue && prevEma.HasValue)
				{
					var crossUp = prevClose.Value <= prevEma.Value && close > emaVal;
					var crossDown = prevClose.Value >= prevEma.Value && close < emaVal;

					if (crossUp && rsiVal < 55m && Position <= 0)
						BuyMarket();
					else if (crossDown && rsiVal > 45m && Position >= 0)
						SellMarket();
				}

				prevClose = close;
				prevEma = emaVal;
			})
			.Start();

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