Auf GitHub ansehen

Gralsexperte MA

Überblick

Grail Expert MA ist ein StockSharp-Port des MetaTrader 4 Expert Advisors _GrailExpertMAV1_0. Das System sucht nach neuen Ausbrüchen jenseits des jüngsten Hoch-/Tief-Kanals und wartet auf einen Rückzug, bevor es sich der Bewegung anschließt. Ein exponentieller gleitender Durchschnitt des typischen Preises sorgt für die Richtungsabweichung: Trades sind nur zulässig, wenn der EMA in den letzten beiden abgeschlossenen Kerzen eine konfigurierbare Anzahl von Pips gewonnen oder verloren hat. Das Risikomanagement spiegelt den ursprünglichen Experten mit Pip-basierten Stop-Loss- und Take-Profit-Abständen wider und ignoriert neue Einträge, während eine Position aktiv ist.

Strategielogik

EMA Steigungstrendfilter

  • Am Ende jedes Balkens wird ein EMA berechnet, der auf dem typischen Preis ((Hoch + Tief + Schlusskurs)/3) berechnet wird.
  • Die Differenz zwischen den letzten beiden EMA-Werten muss den Schwellenwert EMA Slope (pips) überschreiten (umgerechnet in einen Preis mithilfe der Pip-Größe des Symbols).
  • Eine positive Steigung erlaubt lange Pullbacks, eine negative Steigung erlaubt kurze Pullbacks und flache Steigungen blockieren den Handel.

Verfolgung der Ausbruchsreichweite

  • Die Strategie behält das höchste Hoch und das niedrigste Tief über die letzten Range Period abgeschlossenen Balken bei.
  • Diese Ebenen bilden einen Kanal, dessen Höhe dazu verwendet wird, flache Bewegungen abzuwehren, die nicht genügend Abstand für die Pullback-Logik schaffen.

Einreisevorbereitung

  • Wenn der aktuelle Balken ein neues Hoch über dem gespeicherten Bereich anzeigt, wird ein potenzieller Long-Einstiegspreis bei High - Breakout Buffer - Take Profit Pips berechnet.
  • Wenn der aktuelle Balken ein neues Tief unterhalb des gespeicherten Bereichs anzeigt, wird ein potenzieller Short-Einstiegspreis bei Low + Breakout Buffer + Take Profit Pips berechnet.
  • Für den ursprünglichen EA musste der Abstand zwischen dem neuen Extrem und der gegenüberliegenden Seite des Bereichs mindestens 2 * Breakout Buffer + Take Profit betragen. Der Port behält die gleiche Validierung bei und verwirft den Eintrag, wenn die Spanne zu klein ist.

Eintrittsauslöser

  • Die vorbereiteten Preise bleiben für den Rest der Bar aktiv. Ein Long wird ausgeführt, wenn das Intrabar-Tief den gespeicherten Long-Einstiegspreis erreicht oder unterschreitet, während die EMA-Steigung positiv ist.
  • Ein Short wird ausgeführt, wenn das Intrabar-Hoch den gespeicherten Short-Einstiegspreis erreicht oder überschreitet, während die EMA-Steigung negativ ist.
  • Es kann jeweils nur ein Trade geöffnet sein; Der Hafen löscht beide ausstehenden Einstiegspreise, sobald eine Bestellung übermittelt wird, die dem Verhalten von MQL entspricht.

Exit-Management

  • Long-Positionen verwenden einen Stop bei Entry - Stop Loss Pips und ein Gewinnziel bei Entry + Take Profit Pips (Null deaktiviert das jeweilige Level).
  • Short-Positionen spiegeln die Berechnungen wider (Stopp oben, Ziel unten).
  • Exits werden ausgelöst, wenn die Kerzenextreme die Schutzniveaus berühren, was der balkenbasierten Annäherung der ursprünglichen Tick-Logik entspricht.

Zusätzliche Schutzmaßnahmen

  • Ausstehende Einträge werden gelöscht, wenn sie beim Schließen einer neuen Kerze außerhalb des aktualisierten Bereichs liegen.
  • Alle Pip-Abstände passen sich automatisch an die Tick-Größe des Instruments an (fünfstellige FX-Symbole ordnen einen Pip 10 Ticks zu).
  • Wenn der EMA noch nicht gebildet ist oder der Bereichspuffer nicht über genügend Verlauf verfügt, bleibt die Strategie inaktiv, bis genügend Daten verfügbar sind.

Parameter

  • Auftragsvolumen – Handelsvolumen in Lots/Kontrakten für Marktaufträge.
  • Take Profit (Pips) – Abstand zum festgelegten Gewinnziel; zum Deaktivieren auf 0 setzen.
  • Stop Loss (Pips) – Abstand zum Schutzstopp; zum Deaktivieren auf 0 setzen.
  • Bereichszeitraum – Anzahl der abgeschlossenen Kerzen, die zur Messung des Ausbruchskanals verwendet werden.
  • EMA Zeitraum – Länge des exponentiellen gleitenden Durchschnitts, angewendet auf den typischen Preis.
  • EMA Steigung (Pips) – minimaler Pip-Vor-/Abstieg zwischen aufeinanderfolgenden EMA-Werten, der erforderlich ist, um Eingaben zu ermöglichen.
  • Breakout-Puffer (Pips) – zusätzlicher Abstand vom neuen Extrem, bevor Pullback-Einstiege aktiviert werden.
  • Kerzentyp – vom Datenfeed angeforderter Zeitrahmen (Standard: 1-Stunden-Kerzen).

Hinweise zur Implementierung

  • Die Strategie verwendet Rohkerzenaktualisierungen (einschließlich Teilzuständen), um die ursprüngliche Intrabar-Hoch/Tief-Überwachung zu emulieren.
  • EMA-Werte werden nur bei fertigen Kerzen verarbeitet, um die MQL iMA-Aufrufe mit Verschiebungen von einem und zwei Balken zu reproduzieren.
  • Historische Bereiche werden mit begrenzten Warteschlangen anstelle von Indikatorsuchen verfolgt, um teure Neuscans zu vermeiden und gleichzeitig die Logik der Quelle treu zu halten.
  • Es wird keine Python-Version bereitgestellt. Das Paket API enthält nur die C#-Implementierung.
using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Grail Expert MA: SMA crossover with ATR stops.
/// </summary>
public class GrailExpertMaStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastSmaLength;
	private readonly StrategyParam<int> _slowSmaLength;
	private readonly StrategyParam<int> _atrLength;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;

	public GrailExpertMaStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe.", "General");

		_fastSmaLength = Param(nameof(FastSmaLength), 10)
			.SetDisplay("Fast SMA", "Fast SMA period.", "Indicators");

		_slowSmaLength = Param(nameof(SlowSmaLength), 40)
			.SetDisplay("Slow SMA", "Slow SMA period.", "Indicators");

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

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

	public int FastSmaLength
	{
		get => _fastSmaLength.Value;
		set => _fastSmaLength.Value = value;
	}

	public int SlowSmaLength
	{
		get => _slowSmaLength.Value;
		set => _slowSmaLength.Value = value;
	}

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

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

		_prevFast = 0;
		_prevSlow = 0;
		_entryPrice = 0;
	}

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

		_prevFast = 0;
		_prevSlow = 0;
		_entryPrice = 0;

		var fastSma = new SimpleMovingAverage { Length = FastSmaLength };
		var slowSma = new SimpleMovingAverage { Length = SlowSmaLength };
		var atr = new AverageTrueRange { Length = AtrLength };

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

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

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

		if (_prevFast == 0 || _prevSlow == 0 || atrVal <= 0)
		{
			_prevFast = fastVal;
			_prevSlow = slowVal;
			return;
		}

		var close = candle.ClosePrice;

		if (Position > 0)
		{
			if (fastVal < slowVal && _prevFast >= _prevSlow)
			{
				SellMarket();
				_entryPrice = 0;
			}
			else if (close <= _entryPrice - atrVal * 2m)
			{
				SellMarket();
				_entryPrice = 0;
			}
		}
		else if (Position < 0)
		{
			if (fastVal > slowVal && _prevFast <= _prevSlow)
			{
				BuyMarket();
				_entryPrice = 0;
			}
			else if (close >= _entryPrice + atrVal * 2m)
			{
				BuyMarket();
				_entryPrice = 0;
			}
		}

		if (Position == 0)
		{
			if (fastVal > slowVal && _prevFast <= _prevSlow)
			{
				_entryPrice = close;
				BuyMarket();
			}
			else if (fastVal < slowVal && _prevFast >= _prevSlow)
			{
				_entryPrice = close;
				SellMarket();
			}
		}

		_prevFast = fastVal;
		_prevSlow = slowVal;
	}
}