Auf GitHub ansehen

Waddah Attar Win Grid-Strategie

Die Waddah Attar Win Grid Strategy repliziert den MetaTrader 4 Expert Advisor aus dem MQL/8210-Skript. Es wird kontinuierlich eine symmetrische Leiter von Kauf- und Verkaufslimitaufträgen rund um das aktuelle Geld/Brief aufrecht erhalten. Wenn der Preis in Richtung der aktuellsten Rasterebene driftet, stapelt die Strategie automatisch eine neue ausstehende Order einen Schritt weiter entfernt und erhöht optional das Volumen jeder zusätzlichen Order. Der variable Gewinn wird bei jeder Auftragsbuchaktualisierung überwacht und sobald der konfigurierte Eigenkapitalgewinn erreicht ist, werden alle Positionen und Arbeitsaufträge gleichzeitig geschlossen.

Wie es funktioniert

  1. Initialisierung
    • Abonniert Orderbuchaktualisierungen, um sofort auf Geld-/Briefänderungen reagieren zu können.
    • Zeichnet den aktuellen Portfoliowert auf, der als Basis-Aktienreferenz verwendet werden soll.
    • Startet das integrierte Risikoschutz-Subsystem von StockSharp.
  2. Basismanagement
    • Wenn keine aktiven Aufträge vorliegen und die Nettoposition unverändert ist, wird der aktuelle Portfoliowert zum neuen Referenzsaldo. Dies spiegelt den ursprünglichen Expert Advisor wider, der bei jedem Tick den aktuellen Kontostand speicherte.
  3. Erste Rasterplatzierung
    • Sobald der Handel erlaubt ist und keine Orders aktiv sind, platziert die Strategie zwei ausstehende Orders:
      • Ein Kauflimit Step Points unter dem aktuellen Briefkurs.
      • Ein Verkaufslimit Step Points über dem aktuellen Gebotspreis.
    • Beide Bestellungen verwenden den Wert First Volume.
  4. Neue Bestellungen stapeln
    • Wenn sich der Briefkurs innerhalb von fünf Preisschritten um das letzte Kauflimit bewegt, setzt die Strategie ein neues Kauflimit, das einen ganzen Schritt unter dem vorherigen Niveau liegt.
    • Wenn sich der Geldkurs innerhalb von fünf Preisschritten um das letzte Verkaufslimit bewegt, setzt die Strategie ein neues Verkaufslimit, das einen ganzen Schritt über dem vorherigen Niveau liegt.
    • Jede neue ausstehende Bestellung erhöht das Volumen um Increment Volume und ermöglicht auf Wunsch eine Pyramidenbildung im Martingal-Stil.
  5. Gewinnerfassung
    • Der variable Gewinn wird als Differenz zwischen dem aktuellen Portfolio-Eigenkapital und dem hinterlegten Referenzsaldo berechnet.
    • Sobald dieser Gewinn Min Profit übersteigt, wird jede aktive Order storniert und alle offenen Positionen werden mit einem einzigen Aufruf von CloseAll reduziert.
    • Das Grundeigenkapital wird aktualisiert, sodass das Raster mit einer sauberen Weste neu gestartet werden kann.

Strategiemerkmale

  • Marktdaten: Funktioniert ausschließlich auf Orderbuch-Snapshots der Ebene 1 (bester Geld-/Briefkurs).
  • Order-Typen: verwendet nur Limit-Orders; Es werden keine Stopps oder Markteintritte automatisch generiert.
  • Engagement: Kann gleichzeitig Long- und Short-Positionen in absicherungsfähigen Portfolios halten.
  • Risikokontrolle: es fehlen harte Stop-Losses; setzt auf das variable Gewinnziel und externe Risikoregeln.
  • Neueingabe: Nach der Reduzierung oder manuellen Stornierung von Aufträgen wird das ursprüngliche Raster automatisch neu erstellt, wenn die Marktdatenschleife das nächste Mal ausgeführt wird.

Parameter

Parameter Standard Beschreibung
Step Points 120 Abstand zwischen aufeinanderfolgenden Rasterebenen, ausgedrückt in Preispunkten (Preisschritt-Vielfaches).
First Volume 0.1 Volumen, das für das allererste Paar ausstehender Aufträge verwendet wird.
Increment Volume 0.0 Jeder neu gestapelten Bestellung wird zusätzliches Volumen hinzugefügt. auf Null setzen, um alle Bestellungen gleich groß zu halten.
Min Profit 450 Variabler Gewinn (in Kontowährung), der zum Schließen aller offenen Positionen und ausstehenden Aufträge erforderlich ist.

Hinweise und Einschränkungen

  • Stellen Sie sicher, dass PriceStep des Instruments richtig eingestellt ist; Die Strategie multipliziert Step Points mit PriceStep, um tatsächliche Preise abzuleiten.
  • Da der Algorithmus Aufträge häufig storniert und ersetzt, sollten Broker- oder Börsenlimits für die Anzahl ausstehender Aufträge berücksichtigt werden.
  • Es gibt keinen integrierten Drawdown-Schutz – erwägen Sie eine Kombination der Strategie mit externem Risikomanagement oder Stopps auf Portfolioebene.
  • Das Netz kann bei starker Preisentwicklung unbegrenzt erweitert werden, ohne dass das Gewinnziel erreicht wird; Wählen Sie Increment Volume sorgfältig aus, um die Margin-Nutzung zu kontrollieren.

Dateien

  • CS/WaddahAttarWinGridStrategy.cs – C#-Implementierung der Handelslogik.
  • README.md – diese Dokumentation (Englisch).
  • README_ru.md – Russische Übersetzung mit identischem Inhalt.
  • README_zh.md – Chinesische Übersetzung mit identischem Inhalt.
using System;
using System.Collections.Generic;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Grid strategy converted from the "Waddah Attar Win" MetaTrader 4 expert advisor.
/// Places paired orders around the market, pyramids positions with an optional volume increment,
/// and closes the entire exposure once the floating profit target is achieved.
/// </summary>
public class WaddahAttarWinGridStrategy : Strategy
{
	private readonly StrategyParam<int> _stepPoints;
	private readonly StrategyParam<decimal> _firstVolume;
	private readonly StrategyParam<decimal> _incrementVolume;
	private readonly StrategyParam<decimal> _minProfit;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _lastBuyGridPrice;
	private decimal _lastSellGridPrice;
	private decimal _currentBuyVolume;
	private decimal _currentSellVolume;
	private decimal _referenceBalance;
	private bool _gridActive;

	/// <summary>
	/// Distance in price points between consecutive grid levels.
	/// </summary>
	public int StepPoints
	{
		get => _stepPoints.Value;
		set => _stepPoints.Value = value;
	}

	/// <summary>
	/// Volume for the very first pair of orders.
	/// </summary>
	public decimal FirstVolume
	{
		get => _firstVolume.Value;
		set => _firstVolume.Value = value;
	}

	/// <summary>
	/// Volume increment applied to each newly stacked order.
	/// </summary>
	public decimal IncrementVolume
	{
		get => _incrementVolume.Value;
		set => _incrementVolume.Value = value;
	}

	/// <summary>
	/// Floating profit target in account currency that closes all positions.
	/// </summary>
	public decimal MinProfit
	{
		get => _minProfit.Value;
		set => _minProfit.Value = value;
	}

	/// <summary>
	/// Candle type for price data.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Initializes strategy parameters.
	/// </summary>
	public WaddahAttarWinGridStrategy()
	{
		_stepPoints = Param(nameof(StepPoints), 1500)
			.SetGreaterThanZero()
			.SetDisplay("Step (Points)", "Distance between grid levels in points", "Grid")
			.SetOptimize(20, 400, 10);

		_firstVolume = Param(nameof(FirstVolume), 0.1m)
			.SetGreaterThanZero()
			.SetDisplay("First Volume", "Volume for the initial orders", "Trading");

		_incrementVolume = Param(nameof(IncrementVolume), 0m)
			.SetDisplay("Increment Volume", "Additional volume added when stacking new orders", "Trading");

		_minProfit = Param(nameof(MinProfit), 450m)
			.SetNotNegative()
			.SetDisplay("Min Profit", "Floating profit target in account currency", "Risk");

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Candle type for price data", "General");
	}

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

		_lastBuyGridPrice = 0m;
		_lastSellGridPrice = 0m;
		_currentBuyVolume = 0m;
		_currentSellVolume = 0m;
		_referenceBalance = 0m;
		_gridActive = false;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_referenceBalance = Portfolio?.CurrentValue ?? 0m;

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

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

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		var priceStep = Security?.PriceStep ?? 0.01m;
		if (priceStep <= 0m)
			priceStep = 0.01m;

		var stepOffset = StepPoints * priceStep;
		if (stepOffset <= 0m)
			return;

		var price = candle.ClosePrice;

		// Check profit target
		var floatingProfit = (Portfolio?.CurrentValue ?? 0m) - _referenceBalance;
		if (MinProfit > 0m && floatingProfit >= MinProfit && _gridActive)
		{
			if (Position > 0)
				SellMarket(Position);
			else if (Position < 0)
				BuyMarket(Math.Abs(Position));

			_referenceBalance = Portfolio?.CurrentValue ?? _referenceBalance;
			_gridActive = false;
			_lastBuyGridPrice = 0m;
			_lastSellGridPrice = 0m;
			_currentBuyVolume = 0m;
			_currentSellVolume = 0m;
			return;
		}

		// Initialize grid on first candle
		if (!_gridActive)
		{
			_lastBuyGridPrice = price;
			_lastSellGridPrice = price;
			_currentBuyVolume = FirstVolume;
			_currentSellVolume = FirstVolume;
			_gridActive = true;
			_referenceBalance = Portfolio?.CurrentValue ?? _referenceBalance;
			return;
		}

		// Check if price dropped enough to trigger a buy grid level
		if (price <= _lastBuyGridPrice - stepOffset)
		{
			BuyMarket(_currentBuyVolume);
			_lastBuyGridPrice = price;
			_currentBuyVolume += IncrementVolume;
		}

		// Check if price rose enough to trigger a sell grid level
		if (price >= _lastSellGridPrice + stepOffset)
		{
			SellMarket(_currentSellVolume);
			_lastSellGridPrice = price;
			_currentSellVolume += IncrementVolume;
		}
	}
}