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
- Fast EMA on close verfolgt die letzte Preisbewegung.
- Langsames EMA beim Öffnen hinkt hinterher und bildet die Crossover-Grundlinie.
- 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.
- 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;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage, Momentum
from StockSharp.Algo.Strategies import Strategy
class graal_ema_momentum_strategy(Strategy):
def __init__(self):
super(graal_ema_momentum_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 20) \
.SetDisplay("Fast EMA", "Fast EMA period", "Indicators")
self._slow_period = self.Param("SlowPeriod", 80) \
.SetDisplay("Slow EMA", "Slow EMA period", "Indicators")
self._momentum_period = self.Param("MomentumPeriod", 14) \
.SetDisplay("Momentum", "Momentum period", "Indicators")
self._cooldown_candles = self.Param("CooldownCandles", 100) \
.SetDisplay("Cooldown", "Candles between signals", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Candle timeframe", "General")
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev = False
self._cooldown_remaining = 0
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def momentum_period(self):
return self._momentum_period.Value
@property
def cooldown_candles(self):
return self._cooldown_candles.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(graal_ema_momentum_strategy, self).OnReseted()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev = False
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(graal_ema_momentum_strategy, self).OnStarted2(time)
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev = False
self._cooldown_remaining = 0
fast = ExponentialMovingAverage()
fast.Length = self.fast_period
slow = ExponentialMovingAverage()
slow.Length = self.slow_period
mom = Momentum()
mom.Length = self.momentum_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(fast, slow, mom, self.process_candle).Start()
def process_candle(self, candle, fast, slow, mom):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast)
slow_val = float(slow)
mom_val = float(mom)
if not self._has_prev:
self._prev_fast = fast_val
self._prev_slow = slow_val
self._has_prev = True
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._prev_fast <= self._prev_slow and fast_val > slow_val and mom_val > 0 and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._cooldown_remaining = self.cooldown_candles
elif self._prev_fast >= self._prev_slow and fast_val < slow_val and mom_val < 0 and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._cooldown_remaining = self.cooldown_candles
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return graal_ema_momentum_strategy()