Gap DM ist eine konträre Gap-Trading-Strategie, die den Abstand zwischen dem Schluss der vorherigen Sitzung und der Eröffnung der nächsten Sitzung verfolgt. Wenn der Markt mit einem sichtbaren Gap eröffnet, handelt die Strategie sofort in die entgegengesetzte Richtung und erwartet, dass der Preis umkehrt und das Gap füllt. Die Implementierung folgt dem ursprünglichen MetaTrader 5-Algorithmus "Gap DM" von cmillion, angepasst an StockSharpss High-Level-API. Alle Handelsentscheidungen werden aus abgeschlossenen Kerzen des gewählten Zeitrahmens abgeleitet und gewährleisten deterministisches Verhalten in Backtests und Live-Ausführung.
Signallogik
Die durch CandleType angegebene Kerzenreihe abonnieren.
Warten, bis jede Kerze abgeschlossen ist (CandleStates.Finished).
Den Schlusskreis der vorherigen Kerze mit dem Eröffnungskurs der aktuellen Kerze vergleichen.
Die Gap-Größe in Pips berechnen unter Verwendung des Preisschritts des Instruments. Ein Multiplikator von 10 wird automatisch für 3- und 5-stellige Kurse angewendet, reproduzierend die MT5-Punkt-zu-Pip-Umrechnung.
Wenn die aktuelle Eröffnung unterhalb des vorherigen Schlusses um mindestens Minimum Gap (pips) liegt, als bärisches Gap behandeln und Long einsteigen.
Wenn die aktuelle Eröffnung oberhalb des vorherigen Schlusses um mindestens Minimum Gap (pips) liegt, als bullisches Gap behandeln und Short einsteigen.
Einstiege überspringen, wenn der Handel nicht erlaubt ist (z.B. Strategie ist getrennt oder noch im Aufwärmen).
Positionsgrößenbestimmung und Limits
Order Volume gibt die Lotgröße für jeden neuen Trade an. Die Strategie verwendet den Wert auch zum Schließen oder Umkehren bestehender Exposition, um die Nettoposition konsistent mit StockSharpss Netto-Buchungsmodell zu halten.
Max Positions definiert das maximale aggregierte Volumen (in Lots), das in einer Richtung gehalten werden kann. Wenn das Limit erreicht ist, werden neue Einstiege in derselben Richtung ignoriert.
Beim Umkehren von Short zu Long (oder umgekehrt) fügt die Strategie automatisch das notwendige Volumen hinzu, um die entgegengesetzte Exposition zu schließen, bevor die neue Position eröffnet wird.
Risikomanagement
Stop Loss (pips) platziert einen schützenden Stop relativ zum Einstiegspreis. Der Stop wird bei jeder abgeschlossenen Kerze ausgewertet. Wenn der Kerzenbereich durch das Stop-Niveau handelt, wird die Position sofort mit einer Marktorder geschlossen.
Take Profit (pips) funktioniert symmetrisch zum Stop-Loss. Den Parameter auf null setzen, um das Ziel zu deaktivieren.
Standardmäßig wird kein Trailing Stop angewendet; die Ausstiegslogik entspricht dem Quell-Expertenberater.
Parameter
Parameter
Beschreibung
Standard
Order Volume
Handelsvolumen für jeden Einstieg in Lots.
1
Stop Loss (pips)
Abstand des schützenden Stops. Auf 0 setzen zum Deaktivieren.
0
Take Profit (pips)
Abstand des Gewinnziels. Auf 0 setzen zum Deaktivieren.
0
Minimum Gap (pips)
Minimale Differenz zwischen dem vorherigen Schluss und der aktuellen Eröffnung, die für ein Signal erforderlich ist.
1
Max Positions
Maximale aggregierte Exposition in einer einzelnen Richtung (in Lots).
15
Candle Type
Zeitrahmen zur Messung von Sitzungsgaps.
1 Stunde
Ausführungsablauf
Gecachten Zustand bei jedem Neustart zurücksetzen (Gap-Schwellenwerte, Stop-Niveaus, vorheriger Schluss).
Kerzenabonnement starten und Chart-Elemente (Kerzen und Trades) zeichnen, wenn ein Chart-Bereich verfügbar ist.
Bei jeder fertigen Kerze:
Den aktiven Stop und das Ziel je nach aktueller Position aktualisieren oder zurücksetzen.
Gap-Bedingungen auswerten und Marktorders platzieren, wenn ein gültiges Signal erscheint.
Schutz-Orders neu prüfen, damit Stop-Loss- oder Take-Profit-Ereignisse innerhalb derselben Kerze ohne Verzögerung behandelt werden.
Den letzten Schluss für die nächste Auswertung speichern.
Hinweise und Unterschiede zur originalen MT5-Version
StockSharp-Strategien operieren mit Nettopositionen. Der Algorithmus emuliert mehrere Einstiege durch Skalierung der Netto-Exposition statt durch Erstellen separater Tickets.
Alle Kommentare im Quellcode sind auf Englisch, konform mit den Projektrichtlinien.
Geldverwaltung über Risikoanteil (Modus risk im MT5-Skript) wird nicht reproduziert; stattdessen wird ein fester Volumenparameter bereitgestellt.
Anforderungen
Kompatibel mit jedem Instrument, das einen gültigen PriceStep exponiert.
Funktioniert mit zeit-, volumen- oder rangbasierten Kerzen, die von StockSharp unterstützt werden, solange das Gap-Konzept sinnvoll ist.
Erfordert eine StockSharp-Umgebung, die Marktorders ausführen und eigene Trades überwachen kann.
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class GapDMStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<int> _takeProfitPoints;
private ExponentialMovingAverage _fast;
private ExponentialMovingAverage _slow;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
private int _cooldown;
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }
public GapDMStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
}
protected override void OnReseted()
{
base.OnReseted();
_fast = null; _slow = null;
_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fast = new ExponentialMovingAverage { Length = FastPeriod };
_slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
subscription.Bind(_fast, _slow, ProcessCandle);
subscription.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished) return;
if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }
var close = candle.ClosePrice;
var step = Security?.PriceStep ?? 1m;
if (Position > 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
else if (Position < 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }
_prevFast = fastValue; _prevSlow = slowValue;
}
}
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
from StockSharp.Algo.Strategies import Strategy
class gap_dm_strategy(Strategy):
def __init__(self):
super(gap_dm_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 14) \
.SetDisplay("Fast Period", "Fast MA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 50) \
.SetDisplay("Slow Period", "Slow MA period", "Indicator")
self._stop_loss_points = self.Param("StopLossPoints", 200) \
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 400) \
.SetDisplay("Take Profit", "Take-profit in price steps", "Risk")
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def stop_loss_points(self):
return self._stop_loss_points.Value
@property
def take_profit_points(self):
return self._take_profit_points.Value
def OnReseted(self):
super(gap_dm_strategy, self).OnReseted()
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(gap_dm_strategy, self).OnStarted2(time)
self._fast = ExponentialMovingAverage()
self._fast.Length = self.fast_period
self._slow = ExponentialMovingAverage()
self._slow.Length = self.slow_period
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(5)))
subscription.Bind(self._fast, self._slow, self._process_candle)
subscription.Start()
def _process_candle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast_value)
slow_val = float(slow_value)
if not self._fast.IsFormed or not self._slow.IsFormed:
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fast_val
self._prev_slow = slow_val
return
close = float(candle.ClosePrice)
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
if self.Position > 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close <= self._entry_price - self.stop_loss_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close >= self._entry_price + self.take_profit_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
elif self.Position < 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close >= self._entry_price + self.stop_loss_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close <= self._entry_price - self.take_profit_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._prev_fast <= self._prev_slow and fast_val > slow_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 100
elif self._prev_fast >= self._prev_slow and fast_val < slow_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return gap_dm_strategy()