Auf GitHub ansehen

Graal EMA Momentum-Strategie

Diese Strategie ist eine Umsetzung des MetaTrader 4 Expertenberaters 0Graal-CROSSmuvingi. Es werden Trendumkehrungen gehandelt, die auftreten, wenn ein schneller exponentieller gleitender Durchschnitt (EMA) bei Schlusskursen einen langsameren EMA, der bei Eröffnungskursen berechnet wird, kreuzt. Ein Momentum-Oszillator bestätigt die Ausbruchsrichtung und ein Take-Profit mit fester Distanz reproduziert das ursprüngliche MT4-Ausführungsmodell.

Handelsidee

  1. Fast EMA on close verfolgt die letzte Preisbewegung.
  2. Langsames EMA beim Öffnen hinkt hinterher und bildet die Crossover-Grundlinie.
  3. Momentum-Oszillator (Periode 14) misst, wie stark der Preis vom neutralen Wert (100) weg beschleunigt. Die Strategie wird nur gehandelt, wenn das Momentum um mehr als einen konfigurierbaren Filter von 100 abweicht und in die gleiche Richtung weiter zunimmt.
  4. Take Profit schließt Geschäfte nach einer vordefinierten Distanz, gemessen in Instrumentenpunkten, und spiegelt den MT4-Parameter TakeProfit wider.

Teilnahmebedingungen

  • Lange Einrichtung
    • Der schnelle EMA kreuzt den langsamen EMA der aktuellen fertigen Kerze, während der schnelle EMA im vorherigen Balken kleiner oder gleich dem langsamen EMA war.
    • Das Momentum (Wert minus 100) ist größer als der Schwellenwert MomentumFilter und auch höher als der Momentum-Wert des vorherigen Balkens.
    • Bestehende Short-Positionen werden geschlossen, bevor eine neue Long-Position eröffnet wird. Die neue Long-Größe entspricht dem konfigurierten Volume plus dem Betrag, der erforderlich ist, um einen offenen Short zu verkaufen.
  • Kurze Einrichtung
    • Der schnelle EMA kreuzt den langsamen EMA, während im vorherigen Balken der schnelle EMA über oder gleich dem langsamen EMA lag.
    • Das Momentum (Wert minus 100) liegt unter dem negativen MomentumFilter-Schwellenwert und unter dem Momentum-Wert des vorherigen Balkens.
    • Bestehende Long-Positionen werden geschlossen, bevor eine neue Short-Position eröffnet wird. Die neue Short-Größe entspricht dem konfigurierten Volume plus der Menge, die zur Deckung einer offenen Long-Position erforderlich ist.

Ausgangsregeln

  • Positionen werden automatisch geschlossen, wenn der Preis das berechnete Take-Profit-Ziel (TakeProfitPoints * PriceStep) erreicht.
  • Ein neues Gegensignal kehrt die Position ebenfalls sofort um, da die Ordergröße immer die Menge der aktuellen Position beinhaltet.

Parameter

Name Beschreibung Standard
FastPeriod Länge des EMA bei Schlusskursen. 13
SlowPeriod Länge des EMA bei Eröffnungskursen. 34
MomentumPeriod Rückblick auf den Impulsoszillator. 14
MomentumFilter Minimale absolute Momentum-Abweichung von 100, die für den Handel erforderlich ist. 0,1
TakeProfitPoints Abstand zum Gewinnziel in Preispunkten (multipliziert mit PriceStep). 200
CandleType Für Berechnungen verwendeter Kerzendatentyp (standardmäßig 15-Minuten-Zeitrahmen). 15-minütiger Zeitrahmen
Volume Bestellgröße, die für Neueingaben verwendet wird. Die Engine erbt es von der Basisklasse. 1

Implementierungshinweise

  • Signale werden nur bei geschlossenen Kerzen verarbeitet (CandleStates.Finished).
  • Die Strategie abonniert den gewählten Kerzentyp mit SubscribeCandles und bindet sowohl EMA als auch Momentumindikatoren über den High-Level API.
  • Der langsame EMA wird manuell mit Eröffnungspreisen innerhalb des Bind-Callbacks aktualisiert, um das MT4-Verhalten zu reproduzieren, bei dem PRICE_OPEN verwendet wurde.
  • Das Take-Profit-Management überwacht die Intrabar-Hochs und -Tiefs, um die punktbasierte Exit-Logik von MT4 nachzuahmen.
  • StartProtection() ist beim Start aktiviert, um vor unerwarteten offenen Positionen zu schützen, bevor die Strategie mit dem Handel beginnt.
using System;

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

namespace StockSharp.Samples.Strategies;

public class GraalEmaMomentumStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _momentumPeriod;
	private readonly StrategyParam<int> _cooldownCandles;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevFast;
	private decimal _prevSlow;
	private bool _hasPrev;
	private int _cooldownRemaining;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int MomentumPeriod { get => _momentumPeriod.Value; set => _momentumPeriod.Value = value; }
	public int CooldownCandles { get => _cooldownCandles.Value; set => _cooldownCandles.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public GraalEmaMomentumStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 20).SetDisplay("Fast EMA", "Fast EMA period", "Indicators");
		_slowPeriod = Param(nameof(SlowPeriod), 80).SetDisplay("Slow EMA", "Slow EMA period", "Indicators");
		_momentumPeriod = Param(nameof(MomentumPeriod), 14).SetDisplay("Momentum", "Momentum period", "Indicators");
		_cooldownCandles = Param(nameof(CooldownCandles), 100).SetDisplay("Cooldown", "Candles between signals", "General");
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame()).SetDisplay("Candle Type", "Candle timeframe", "General");
	}

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

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevFast = 0;
		_prevSlow = 0;
		_hasPrev = false;
		_cooldownRemaining = 0;

		var fast = new ExponentialMovingAverage { Length = FastPeriod };
		var slow = new ExponentialMovingAverage { Length = SlowPeriod };
		var mom = new Momentum { Length = MomentumPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(fast, slow, mom, ProcessCandle).Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow, decimal mom)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!_hasPrev) { _prevFast = fast; _prevSlow = slow; _hasPrev = true; return; }

		if (_cooldownRemaining > 0)
		{
			_cooldownRemaining--;
			_prevFast = fast;
			_prevSlow = slow;
			return;
		}

		if (_prevFast <= _prevSlow && fast > slow && mom > 0 && Position <= 0)
		{
			if (Position < 0) BuyMarket();
			BuyMarket();
			_cooldownRemaining = CooldownCandles;
		}
		else if (_prevFast >= _prevSlow && fast < slow && mom < 0 && Position >= 0)
		{
			if (Position > 0) SellMarket();
			SellMarket();
			_cooldownRemaining = CooldownCandles;
		}
		_prevFast = fast;
		_prevSlow = slow;
	}
}