English
preview
Swing-Extreme und Rücksetzer in MQL5 (Teil 1): Entwicklung eines Multi-Timeframe-Indikators

Swing-Extreme und Rücksetzer in MQL5 (Teil 1): Entwicklung eines Multi-Timeframe-Indikators

MetaTrader 5Beispiele |
20 4
Hlomohang John Borotho
Hlomohang John Borotho

Inhaltsverzeichnis

  1. Einführung
  2. Überblick über die Automatisierung
  3. Die ersten Schritte
  4. Backtest-Ergebnisse
  5. Schlussfolgerung


Einführung

Zuvor hatten wir Swing Extremes und den Indikator für Rücksetzer entwickelt, indem wir Swing-Hochs und Swing-Tiefs innerhalb der Kursentwicklung auf niedrigeren Zeitrahmen systematisch identifiziert haben. Der Ansatz konzentrierte sich darauf, kurzfristige strukturelle Veränderungen – höhere Hochs, tiefere Tiefs, Rücksetzer und Phasen nachlassender Dynamik – zu erfassen, um potenzielle Trendumkehrungen zu antizipieren, bevor sie auf höheren Zeitrahmen offensichtlich wurden. Durch die Kombination von Logik zur Swing-Erkennung mit Regeln zur Rücksetzer-Validierung haben wir ein Rahmenwerk geschaffen, das die rohe LTF-Volatilität in ein strukturiertes, aussagekräftiges Modell des Marktverhaltens umwandelt.

Nun besteht der nächste Schritt darin, den Indikator vollständig zu automatisieren, indem diese strukturelle Logik direkt in den Code eingebettet wird. Dazu gehören die algorithmische Definition gültiger Swing-Punkte, das Herausfiltern von Rauschen, die Bestätigung von Rücksetzern anhand regelbasierter Bedingungen sowie die dynamische Aktualisierung der Struktur in Echtzeit. Durch die Automatisierung wird das System die LTF-Marktstruktur kontinuierlich ohne manuelles Eingreifen analysieren und auswerten, und so objektive, konsistente und schnellere Prognosen zu Trendwenden liefern – was letztlich die Präzision der Trade-Ausführung und die Effizienz der Entscheidungsfindung verbessert.


Überblick über die Automatisierung

Aus Sicht der Automatisierung wird die extrem bärische Kursbewegung in ein präzises, regelbasiertes Ausführungsmodell umgesetzt. Wenn der Kurs deutlich unter dem letzten Tiefststand des LTF notiert und der Chart aktualisiert wird, prüft das System, ob sowohl LTF_LastHigh als auch LTF_LastLow über dem aktuellen Marktkurs liegen – was eine strukturelle Verlagerung unterhalb der jüngsten Kursspanne bestätigt. Sobald diese Bedingungen erfüllt sind, interpretiert die Logik die Kursbewegung als Erschöpfung statt als Fortsetzung und öffnet automatisch eine Kaufposition. Als Take-Profit-Niveau verwendet das System dann LTF_LastHigh und zielt auf eine Rückkehr zum Mittelwert in Richtung der vorherigen Struktur ab. Dadurch entfällt jeglicher Ermessensspielraum: Der Handel wird ausschließlich durch strukturelle Ungleichgewichte und vordefinierte Schwellenwerte ausgelöst, wodurch eine konsistente Trade-Ausführung gewährleistet ist, sobald die Abwärtsbewegung statistisch gesehen überzogen ist.

Extreme Abwärtsbewegung

Umgekehrt wendet die Automatisierung bei einer extrem bullischen Marktbewegung die umgekehrte Logik an. Wenn der Kurs über das letzte Hoch auf LTF-Ebene steigt und der Chart aktualisiert wird, prüft der Algorithmus, ob sowohl LTF_LastHigh als auch LTF_LastLow nun unter dem aktuellen Marktkurs liegen – was eine strukturelle Überdehnung nach oben bestätigt. Sobald die Validierung erfolgt ist, eröffnet das System automatisch eine Verkaufsposition, da es erkennt, dass der Kurs seine jüngsten strukturellen Grenzen überschritten hat. In diesem Fall wird LTF_LastLow als Take-Profit-Niveau übernommen, wodurch die Position so platziert wird, dass sie von einem Rückzug in Richtung der vorherigen Struktur profitiert.

Extreme Aufwärtsbewegung


Die ersten Schritte

//+------------------------------------------------------------------+
//|                                          Auto Swing Extremes.mq5 |
//|                                  Copyright 2025, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"

#include <Trade/Trade.mqh>
CTrade trade;

//+------------------------------------------------------------------+
//| Input Parameters                                                 |
//+------------------------------------------------------------------+
input ENUM_TIMEFRAMES HTF = PERIOD_H1;      // Higher Timeframe
input ENUM_TIMEFRAMES LTF = PERIOD_M5;      // Lower Timeframe
input int             SwingBars = 3;        // Bars for swing detection
input double          RiskPercent = 1.0;    // Risk per trade (%)
input int             Stop_Loss = 2000;     // StopLoss
input bool            Visualize = true;     // Show swing points & structure

//+------------------------------------------------------------------+
//| Swing Point Structure                                            |
//+------------------------------------------------------------------+
struct SwingPoint
{
   datetime          time;
   double            price;
   int               type;     // 1 = High, -1 = Low
   int               barIndex;
   
   void Reset()
   {
      time = 0;
      price = 0.0;
      type = 0;
      barIndex = 0;
   }
};

Zu Beginn initialisieren wir die Handelsumgebung, indem wir <Trade/Trade.mqh> einbinden und ein CTrade-Objekt erstellen, das eine strukturierte Möglichkeit bietet, Handelsgeschäfte programmgesteuert auszuführen und zu verwalten. Anschließend definieren wir wichtige Eingabeparameter, die das Verhalten des Systems steuern: den höheren Zeitrahmen (HTF) und den niedrigeren Zeitrahmen (LTF) für die Strukturanalyse über mehrere Zeitrahmen hinweg, die Anzahl der SwingBars zur Erkennung gültiger Swing-Hochs und -Tiefs, den prozentualen Risikoanteil pro Trade, einen festen Stop-Loss-Wert sowie eine Option zur Visualisierung, mit der strukturelle Elemente optional im Chart angezeigt werden können. Durch diese Eingaben lässt sich das System konfigurieren, sodass sich die Strategie an unterschiedliche Marktbedingungen, Symbole und Händlerpräferenzen anpassen kann, ohne dass die Kernlogik geändert werden muss.

Anschließend definieren wir eine Struktur SwingPoint, um die Marktstruktur im Code formal abzubilden. Diese Struktur speichert den Zeitstempel des Swings, das Kursniveau, den Typ (1 für ein Swing-Hoch und -1 für ein Swing-Tief) sowie den Bar-Index und fasst damit alle wesentlichen Daten zusammen, die zur Erfassung struktureller Wendepunkte erforderlich sind. Die integrierte Funktion Reset() stellt sicher, dass ein Swing-Punkt gelöscht und sicher wiederverwendet werden kann, wodurch die Datenintegrität bei Aktualisierungen gewahrt bleibt. Indem wir Swing-Informationen in einem strukturierten Objekt organisieren, legen wir den Grundstein für die automatisierte Erkennung von Marktstrukturen.

//+------------------------------------------------------------------+
//| Market Structure States                                          |
//+------------------------------------------------------------------+
enum STRUCTURE_STATE
{
   STRUCT_NEUTRAL,
   STRUCT_BULLISH,
   STRUCT_BEARISH
};

//+------------------------------------------------------------------+
//| Global Variables                                                 |
//+------------------------------------------------------------------+
SwingPoint htf_lastHigh, htf_prevHigh;
SwingPoint htf_lastLow, htf_prevLow;
SwingPoint ltf_lastHigh, ltf_prevHigh;
SwingPoint ltf_lastLow, ltf_prevLow;

STRUCTURE_STATE htfBias = STRUCT_NEUTRAL;
STRUCTURE_STATE ltfStructure = STRUCT_NEUTRAL;

datetime lastHtfUpdate = 0;
datetime lastLtfUpdate = 0;

// Track last traded swing to avoid multiple entries on same swing
datetime lastBuySwingTime = 0;
datetime lastSellSwingTime = 0;
double lastBuySwingPrice = 0;
double lastSellSwingPrice = 0;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
   // Validate timeframe combination
   if(PeriodSeconds(HTF) <= PeriodSeconds(LTF))
   {
      Alert("HTF must be higher than LTF!");
      return INIT_PARAMETERS_INCORRECT;
   }
   
   // Initialize swing points
   htf_lastHigh.Reset(); htf_prevHigh.Reset();
   htf_lastLow.Reset(); htf_prevLow.Reset();
   ltf_lastHigh.Reset(); ltf_prevHigh.Reset();
   ltf_lastLow.Reset(); ltf_prevLow.Reset();
   
   // Initialize trade tracking
   lastBuySwingTime = 0;
   lastSellSwingTime = 0;
   lastBuySwingPrice = 0;
   lastSellSwingPrice = 0;
   
   // Initial structure detection
   UpdateHTFStructure();
   UpdateLTFStructure();
   
   return INIT_SUCCEEDED;
}

In diesem Abschnitt definieren wir den Rahmen der Strukturlogik, der die richtungsbezogene Interpretation der Strategie bestimmt. Die Enumeration STRUCTURE_STATE definiert drei mögliche Marktbedingungen – neutral, bullisch und bärisch –, wodurch sowohl die Tendenz auf höheren Zeitrahmen (HTF) als auch die Struktur auf niedrigeren Zeitrahmen (LTF) zu jedem beliebigen Zeitpunkt eindeutig klassifiziert werden können. Globale SwingPoint-Variablen werden deklariert, um sowohl die jüngsten als auch die vorherigen Hochs und Tiefs für HTF und LTF zu erfassen, wodurch ein Vergleich zwischen aufeinanderfolgenden strukturellen Punkten (z. B. höhere Hochs oder tiefere Tiefs) ermöglicht wird. Zusätzliche Zustandsvariablen wie htfBias und ltfStructure speichern die aktive Strukturrichtung, während Zeitstempel (lastHtfUpdate, lastLtfUpdate) sicherstellen, dass die Struktur nur dann aktualisiert wird, wenn sich neue Bars bilden. Dadurch wird die Effizienz gewahrt und redundante Neuberechnungen werden vermieden.

Außerdem sind im globalen Umfang Handelskontrollmechanismen integriert, um doppelte Einstiege auf demselben Swing-Niveau zu verhindern. Durch die Speicherung von lastBuySwingTime, lastSellSwingTime und den dazugehörigen Kursen verhindert das System, dass mehrere Trades aufgrund identischer struktureller Signale ausgeführt werden. Innerhalb von OnInit() überprüft das Programm zunächst, ob der ausgewählte HTF tatsächlich höher ist als der LTF, und stellt so die korrekte Hierarchie zwischen den verschiedenen Zeitrahmen sicher. Anschließend werden alle Swing-Strukturen und Variablen zur Handelsverfolgung zurückgesetzt, um einen sauberen Startzustand zu gewährleisten. Anschließend führt es über die Funktionen UpdateHTFStructure() und UpdateLTFStructure() einen ersten Struktur-Scan durch, um das System vor Beginn des Live-Handels an die aktuellen Marktbedingungen anzupassen.

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   // Clean up objects
   ObjectsDeleteAll(0, "MS_");
   ChartRedraw();
}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
   static datetime lastLtfBar = 0;
   datetime currentLtfBar = iTime(_Symbol, LTF, 0);
   
   // Process on new LTF bar
   if(currentLtfBar != lastLtfBar)
   {
      lastLtfBar = currentLtfBar;
      
      // Update HTF bias if needed (on new HTF bar)
      if(TimeCurrent() - lastHtfUpdate >= PeriodSeconds(HTF))
      {
         UpdateHTFStructure();
         lastHtfUpdate = TimeCurrent();
      }
      
      // Store previous LTF structure for comparison
      STRUCTURE_STATE prevLtfStructure = ltfStructure;
      
      // Update LTF structure
      UpdateLTFStructure();
      
      // Check for structure breaks
      CheckStructureBreak();
      
      // Try to execute trades if structure changed or conditions met
      if(ltfStructure != prevLtfStructure || 
         (ltf_lastHigh.price > 0 && ltf_lastLow.price > 0))
      {
         TryExecuteTrade();
      }
            
      // Update visualization
      if(Visualize) UpdateVisualization();
   }
}

Hier steuert der Code sowohl die Aufräumarbeiten als auch den Echtzeit-Ausführungsablauf des Expert Advisors. In der Funktion OnDeinit() werden alle Chart-Objekte, deren Namen mit MS_ beginnen, entfernt, um sicherzustellen, dass alle strukturbezogenen Zeichnungen gelöscht werden, wenn der EA vom Chart entfernt wird; anschließend wird der Chart für eine stimmige Darstellung neu gezeichnet. In der Funktion OnTick() führt das System die Logik ausschließlich bei der Bildung einer neuen Bar im niedrigeren Zeitrahmen (LTF) durch, indem es Zeitstempel vergleicht, wodurch redundante Berechnungen bei jedem Tick vermieden werden. Wenn sich eine neue Bar bildet, aktualisiert das System unter bestimmten Voraussetzungen die Tendenz des höheren Zeitrahmens (HTF), sofern ausreichend Zeit verstrichen ist, aktualisiert die LTF-Struktur, prüft auf Strukturbrüche und vergleicht den vorherigen mit dem aktuellen Strukturzustand. Wenn eine strukturelle Verschiebung eintritt – oder gültige Swing-Daten vorliegen –, wird versucht, den Handel über TryExecuteTrade() auszuführen. Wenn die Visualisierung aktiviert ist, werden die Darstellungen im Chart aktualisiert, um den aktuellen Strukturzustand widerzuspiegeln, wodurch eine Synchronisation von Logik und visuellem Feedback gewährleistet wird.

//+------------------------------------------------------------------+
//| Update Higher Timeframe Structure                                |
//+------------------------------------------------------------------+
void UpdateHTFStructure()
{
   // Get HTF data
   MqlRates htfRates[];
   ArraySetAsSeries(htfRates, true);
   CopyRates(_Symbol, HTF, 0, 100, htfRates);
   
   if(ArraySize(htfRates) < SwingBars * 2 + 1) return;
   
   // Detect swing points on HTF
   for(int i = SwingBars; i < ArraySize(htfRates) - SwingBars; i++)
   {
      bool isHigh = true;
      bool isLow = true;
      
      // Check if current bar is swing high
      for(int j = 1; j <= SwingBars; j++)
      {
         if(htfRates[i].high <= htfRates[i-j].high || 
            htfRates[i].high <= htfRates[i+j].high)
         {
            isHigh = false;
         }
         
         if(htfRates[i].low >= htfRates[i-j].low || 
            htfRates[i].low >= htfRates[i+j].low)
         {
            isLow = false;
         }
      }
      
      // Update swing highs
      if(isHigh)
      {
         htf_prevHigh = htf_lastHigh;
         htf_lastHigh.time = htfRates[i].time;
         htf_lastHigh.price = htfRates[i].high;
         htf_lastHigh.type = 1;
         htf_lastHigh.barIndex = i;
      }
      
      // Update swing lows
      if(isLow)
      {
         htf_prevLow = htf_lastLow;
         htf_lastLow.time = htfRates[i].time;
         htf_lastLow.price = htfRates[i].low;
         htf_lastLow.type = -1;
         htf_lastLow.barIndex = i;
      }
   }
   
   // Update HTF bias
   htfBias = DetectStructure(htf_lastHigh, htf_prevHigh, htf_lastLow, htf_prevLow);
}

//+------------------------------------------------------------------+
//| Update Lower Timeframe Structure                                 |
//+------------------------------------------------------------------+
void UpdateLTFStructure()
{
   // Get LTF data
   MqlRates ltfRates[];
   ArraySetAsSeries(ltfRates, true);
   CopyRates(_Symbol, LTF, 0, 100, ltfRates);
   
   if(ArraySize(ltfRates) < SwingBars * 2 + 1) return;
   
   // Detect swing points on LTF
   for(int i = SwingBars; i < ArraySize(ltfRates) - SwingBars; i++)
   {
      bool isHigh = true;
      bool isLow = true;
      
      // Check if current bar is swing high
      for(int j = 1; j <= SwingBars; j++)
      {
         if(ltfRates[i].high <= ltfRates[i-j].high || 
            ltfRates[i].high <= ltfRates[i+j].high)
         {
            isHigh = false;
         }
         
         if(ltfRates[i].low >= ltfRates[i-j].low || 
            ltfRates[i].low >= ltfRates[i+j].low)
         {
            isLow = false;
         }
      }
      
      // Update swing highs
      if(isHigh)
      {
         ltf_prevHigh = ltf_lastHigh;
         ltf_lastHigh.time = ltfRates[i].time;
         ltf_lastHigh.price = ltfRates[i].high;
         ltf_lastHigh.type = 1;
         ltf_lastHigh.barIndex = i;
      }
      
      // Update swing lows
      if(isLow)
      {
         ltf_prevLow = ltf_lastLow;
         ltf_lastLow.time = ltfRates[i].time;
         ltf_lastLow.price = ltfRates[i].low;
         ltf_lastLow.type = -1;
         ltf_lastLow.barIndex = i;
      }
   }
   
   // Update LTF structure
   ltfStructure = DetectStructure(ltf_lastHigh, ltf_prevHigh, ltf_lastLow, ltf_prevLow);
}

Die Funktion UpdateHTFStructure() analysiert Kursdaten aus höheren Zeitrahmen, um anhand des definierten SwingBars-Parameters gültige Swing-Hochs und Swing-Tiefs zu identifizieren. Sie ruft die letzten 100 HTF-Kerzen ab, stellt sicher, dass genügend Daten vorliegen, und überprüft anschließend jede infrage kommende Kerze, um festzustellen, ob sie als strukturelles Extrem gilt. Eine Bar wird als Swing-Hoch klassifiziert, wenn ihr Höchstwert über den Höchstwerten der umgebenden Bars innerhalb des festgelegten Bereichs liegt, und als Swing-Tief, wenn ihr Tiefstwert unter den benachbarten Tiefstwerten liegt. Wenn ein neuer Swing bestätigt wird, bleibt der vorherige erhalten, und der neueste wird mit Zeit, Kurs, Art und Index aktualisiert. Nachdem alle Kandidaten verarbeitet wurden, ermittelt die Funktion die allgemeine Tendenz auf einem höheren Zeitrahmen (bullisch, bärisch oder neutral), indem sie mithilfe von DetectStructure() die jüngsten und die vorherigen Swing-Hochs und -Tiefs vergleicht.

Die Funktion UpdateLTFStructure() spiegelt diese Logik auf dem niedrigeren Zeitrahmen wider und wendet denselben Prozess zur Erkennung von Swings auf detailliertere Kursdaten an. Sie wertet jede Bar innerhalb des zulässigen Bereichs aus, bestätigt strukturelle Hochs und Tiefs anhand symmetrischer Vergleiche und aktualisiert die gespeicherten LTF-Swing-Variablen entsprechend. Durch die Erfassung sowohl der jüngsten als auch der vorherigen Swing-Punkte kann das System feststellen, ob der Markt höhere Hochs oder tiefere Tiefs bildet oder sich in eine strukturelle Veränderung begibt. Im letzten Schritt wird der aktualisierte LTF-Strukturzustand mithilfe von DetectStructure() zugewiesen, wodurch sichergestellt wird, dass die Handelslogik und die Umkehrerkennung auf einer kontinuierlich aktualisierten und objektiv definierten Marktstruktur über beide Zeitrahmen hinweg basieren.

//+------------------------------------------------------------------+
//| Detect Market Structure                                          |
//+------------------------------------------------------------------+
STRUCTURE_STATE DetectStructure(SwingPoint &lastHigh, SwingPoint &prevHigh,
                                SwingPoint &lastLow, SwingPoint &prevLow)
{
   // Need at least two highs and two lows
   if(lastHigh.price == 0 || prevHigh.price == 0 || 
      lastLow.price == 0 || prevLow.price == 0)
      return STRUCT_NEUTRAL;
   
   bool isHigherHigh = lastHigh.price > prevHigh.price;
   bool isHigherLow = lastLow.price > prevLow.price;
   bool isLowerLow = lastLow.price < prevLow.price;
   bool isLowerHigh = lastHigh.price < prevHigh.price;
   
   if(isHigherHigh && isHigherLow)
      return STRUCT_BULLISH;
   
   if(isLowerLow && isLowerHigh)
      return STRUCT_BEARISH;
   
   return STRUCT_NEUTRAL;
}
Die Funktion DetectStructure() ermittelt die aktuelle Marktstruktur, indem sie die jüngsten und die vorherigen Swing-Hochs und -Tiefs miteinander vergleicht. Zunächst wird sichergestellt, dass genügend Swing-Daten vorliegen – dazu sind mindestens zwei bestätigte Höchststände und zwei bestätigte Tiefststände erforderlich –, andernfalls wird ein neutraler Status zurückgegeben. Die Funktion wertet anschließend die strukturelle Entwicklung aus: Ist das jüngste Hoch höher als das vorherige Hoch und das jüngste Tief höher als das vorherige Tief, wird der Markt als bullisch eingestuft (höhere Hochs und höhere Tiefs). Ist hingegen das jüngste Tief niedriger als das vorherige Tief und das jüngste Hoch niedriger als das vorherige Hoch, gilt die Struktur als bärisch (niedrigere Tiefs und niedrigere Hochs). Wird keine der beiden Bedingungen vollständig erfüllt, gibt die Funktion standardmäßig einen neutralen Zustand zurück, der auf eine Konsolidierung oder einen strukturellen Wandel hindeutet.
//+------------------------------------------------------------------+
//| New Sell Condition                                               |
//+------------------------------------------------------------------+
bool CheckSellCondition()
{
   // Need valid LTF swing points
   if(ltf_lastHigh.price == 0 || ltf_lastLow.price == 0)
      return false;
   
   // Get current price
   double currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   
   // Check if price is above both last high and last low
   bool priceAboveHigh = currentPrice > ltf_lastHigh.price;
   bool priceAboveLow = currentPrice > ltf_lastLow.price;
   
   // Check if this is a new swing (not already traded)
   bool isNewSwing = (ltf_lastHigh.time != lastSellSwingTime) || 
                     (ltf_lastHigh.price != lastSellSwingPrice);
   
   return (priceAboveHigh && priceAboveLow && isNewSwing);
}

//+------------------------------------------------------------------+
//| New Buy Condition                                                |
//+------------------------------------------------------------------+
bool CheckBuyCondition()
{
   // Need valid LTF swing points
   if(ltf_lastLow.price == 0)
      return false;
   
   // Get current price
   double currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   
   // Check if price is below last low
   bool priceBelowLow = currentPrice < ltf_lastLow.price;
   
   // Check if this is a new swing (not already traded)
   bool isNewSwing = (ltf_lastLow.time != lastBuySwingTime) || 
                     (ltf_lastLow.price != lastBuySwingPrice);
   
   return (priceBelowLow && isNewSwing);
}

//+------------------------------------------------------------------+
//| Execute trade                                                    |
//+------------------------------------------------------------------+
void ExecuteTrade(ENUM_ORDER_TYPE tradeType, string symbol)
{
   // Get current price
   double price = (tradeType == ORDER_TYPE_BUY)
                  ? SymbolInfoDouble(symbol, SYMBOL_ASK)
                  : SymbolInfoDouble(symbol, SYMBOL_BID);
   price = NormalizeDouble(price, _Digits);
   
   // Calculate stop loss
   double sl = CalculateSL(tradeType == ORDER_TYPE_BUY);
   sl = NormalizeDouble(sl, _Digits);
   
   // Calculate take profit based on your requirements
   double tp = 0;
   if(tradeType == ORDER_TYPE_BUY)
   {
      // For Buy: TP at next structure level (last high)
      tp = (ltf_lastHigh.price > 0) ? ltf_lastHigh.price : price + (MathAbs(price - sl) * 2);
   }
   else if(tradeType == ORDER_TYPE_SELL) // ORDER_TYPE_SELL
   {
      // For Sell: TP at last low (as requested)
      tp = (ltf_lastLow.price > 0) ? ltf_lastLow.price : price - (MathAbs(price - sl) * 2);
   }
   tp = NormalizeDouble(tp, _Digits);
   
   // Ensure TP is valid (not too close to entry)
   double minDistance = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_SIZE) * 10;
   if(MathAbs(price - tp) < minDistance)
   {
      Print("TP too close to entry, adjusting...");
      if(tradeType == ORDER_TYPE_BUY)
         tp = price + (MathAbs(price - sl) * 2);
      else
         tp = price - (MathAbs(price - sl) * 2);
   }
   
   // Calculate lot size based on risk
   double volume = CalculateLotSize(price, sl);
   
   // Ensure volume is within valid range
   double volume_step = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
   double volume_min = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
   double volume_max = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);
   
   // Normalize volume to step size
   volume = floor(volume / volume_step) * volume_step;
   volume = MathMax(volume, volume_min);
   volume = MathMin(volume, volume_max);
   
   // Prepare trade comment
   string comment = StringFormat("MS_%s_%s",
      (tradeType == ORDER_TYPE_BUY) ? "BUY" : "SELL",
      TimeToString(TimeCurrent(), TIME_DATE|TIME_MINUTES));
   
   // Execute trade using CTrade
   bool success = trade.PositionOpen(symbol, tradeType, volume, price, sl, tp, comment);
   
   if(success)
   {
      // Update swing tracking to avoid multiple entries
      if(tradeType == ORDER_TYPE_BUY)
      {
         lastBuySwingTime = ltf_lastLow.time;
         lastBuySwingPrice = ltf_lastLow.price;
      }
      else
      {
         lastSellSwingTime = ltf_lastHigh.time;
         lastSellSwingPrice = ltf_lastHigh.price;
      }
      
      Print(StringFormat("Trade Opened: %s | Price: %.5f | SL: %.5f | TP: %.5f | Lots: %.2f",
         (tradeType == ORDER_TYPE_BUY) ? "BUY" : "SELL",
         price, sl, tp, volume));
   }
   else
   {
      Print("Trade failed: ", trade.ResultRetcodeDescription());
   }
}

//+------------------------------------------------------------------+
//| Try Execute Trade                                                |
//+------------------------------------------------------------------+
void TryExecuteTrade()
{
   // Check if position already exists
   if(PositionSelect(_Symbol)) 
   {
      Print("Position already exists, waiting for closure...");
      return;
   }
   
   // For Sell positions: Price above both last high and last low
   if(CheckSellCondition())
   {
      // Optional: Check HTF bias filter
      if(htfBias == STRUCT_BEARISH || htfBias == STRUCT_NEUTRAL)
      {
         Print("SELL condition met: Price above LTF high and low");
         OpenSell();
         return;
      }
   }
   
   // For Buy positions: Price below last low
   if(CheckBuyCondition())
   {
      // Optional: Check HTF bias filter
      if(htfBias == STRUCT_BULLISH || htfBias == STRUCT_NEUTRAL)
      {
         Print("BUY condition met: Price below LTF low");
         OpenBuy();
         return;
      }
   }
}

Die Funktionen CheckSellCondition() und CheckBuyCondition() definieren die strukturellen Auslöser für den Handelseinstieg auf der Grundlage der Verschiebung im niedrigeren Zeitrahmen (LTF). Bei einem Verkaufs-Setup stellt das System zunächst sicher, dass gültige Swing-Daten vorliegen, und prüft anschließend, ob der aktuelle Geldkurs sowohl über dem letzten LTF-Swing-Hoch als auch über dem letzten LTF-Swing-Tief liegt – was auf eine bullische Überdehnung über die jüngsten strukturellen Grenzen hinaus deutet. Außerdem wird durch den Abgleich der gespeicherten Zeit- und Kurswerte überprüft, ob dieser Swing bereits gehandelt wurde, wodurch doppelte Einstiege verhindert werden. Ebenso bestätigt die Kaufbedingung, dass der Kurs unter das letzte LTF-Tief gefallen ist, was auf eine Erschöpfung der Abwärtsbewegung hindeutet. Außerdem wird überprüft, ob der Swing neu ist, bevor true zurückgegeben wird. Zusammen wandeln diese Bedingungen strukturelle Extreme in objektive, regelbasierte Einstiegssignale um und sorgen gleichzeitig für die Einhaltung der Regel „ein Trade pro Swing“.

Die Funktion ExecuteTrade() übernimmt die gesamte Handelsabwicklung und das Risikomanagement. Sie ermittelt den korrekten Einstiegskurs (Ask-Kurs bei Käufen, Bid-Kurs bei Verkäufen), berechnet den Stop-Loss über eine separate Funktion und legt den Take-Profit dynamisch auf der Grundlage der Struktur fest – wobei der letzte LTF-Höchststand als TP für Kaufgeschäfte und der letzte LTF-Tiefststand als TP für Verkaufsgeschäfte verwendet wird. Wenn keine strukturellen TP-Werte verfügbar sind oder diese zu nahe am Einstiegskurs liegen, verwendet das System standardmäßig eine Ersatzberechnung auf Basis des Chance-Risiko-Verhältnisses. Die Positionsgröße wird anhand des Risikoprozentsatzes des Kontos berechnet und anschließend an die Vorgaben des Brokers angepasst, wie beispielsweise Minimum, Maximum und Schrittweite des Volumens. Der Handel wird mithilfe des CTrade-Objekts ausgeführt, und im Erfolgsfall werden die entsprechenden Swing-Details gespeichert, um wiederholte Einstiege auf derselben Strukturebene zu verhindern.

Und dann fungiert die Funktion TryExecuteTrade() als Entscheidungsinstanz. Zunächst wird geprüft, ob für das Symbol bereits eine Position offen ist, um sich überschneidende Trades zu vermeiden. Wenn keine Position besteht, werden die Verkaufsbedingungen geprüft (Kurs liegt sowohl über dem letzten Hoch als auch über dem letzten Tief) und optional anhand der Tendenz des höheren Zeitrahmens gefiltert – Trades werden nur zugelassen, wenn die Struktur des höheren Zeitrahmens übereinstimmt oder neutral ist. Wenn diese Bedingung erfüllt ist, wird ein Verkaufsauftrag ausgelöst. Anschließend werden die Kaufbedingungen (Kurs unter dem letzten Tiefststand) geprüft, wobei wiederum optional eine Filterung nach HTF-Tendenzen vorgenommen wird, die eine bullische oder neutrale Struktur begünstigen. Dieser mehrstufige Prozess stellt sicher, dass Handelsgeschäfte nur dann ausgeführt werden, wenn strukturelle Verlagerungen, die Aktualität der Swings und die übergeordnete Marktrichtung gemeinsam übereinstimmen, was eine disziplinierte, strukturorientierte Automatisierung fördert.

//+------------------------------------------------------------------+
//| Check Structure Break                                            |
//+------------------------------------------------------------------+
void CheckStructureBreak()
{
   MqlRates current[];
   ArraySetAsSeries(current, true);
   CopyRates(_Symbol, LTF, 0, 1, current);
   
   if(ArraySize(current) < 1) return;
   
   // Bullish structure break
   if(ltfStructure == STRUCT_BULLISH && current[0].close < ltf_lastLow.price)
   {
      ResetLTFStructure();
   }
   
   // Bearish structure break
   if(ltfStructure == STRUCT_BEARISH && current[0].close > ltf_lastHigh.price)
   {
      ResetLTFStructure();
   }
}

//+------------------------------------------------------------------+
//| Reset LTF Structure                                              |
//+------------------------------------------------------------------+
void ResetLTFStructure()
{
   ltf_lastHigh.Reset();
   ltf_prevHigh.Reset();
   ltf_lastLow.Reset();
   ltf_prevLow.Reset();
   ltfStructure = STRUCT_NEUTRAL;
   
   // Also reset trade tracking
   lastBuySwingTime = 0;
   lastSellSwingTime = 0;
   lastBuySwingPrice = 0;
   lastSellSwingPrice = 0;
}

//+------------------------------------------------------------------+
//| Update Visualization                                             |
//+------------------------------------------------------------------+
void UpdateVisualization()
{
   ObjectsDeleteAll(0, "MS_");
   
   // Draw HTF swing points
   if(htf_lastHigh.price > 0)
      DrawSwingPoint(htf_lastHigh, "HTF_High", clrRed, HTF);
   
   if(htf_prevHigh.price > 0)
      DrawSwingPoint(htf_prevHigh, "HTF_PrevHigh", clrRed, HTF);
   
   if(htf_lastLow.price > 0)
      DrawSwingPoint(htf_lastLow, "HTF_Low", clrBlue, HTF);
   
   if(htf_prevLow.price > 0)
      DrawSwingPoint(htf_prevLow, "HTF_PrevLow", clrBlue, HTF);
   
   // Draw LTF swing points
   if(ltf_lastHigh.price > 0)
      DrawSwingPoint(ltf_lastHigh, "LTF_High", clrOrange, LTF);
   
   if(ltf_prevHigh.price > 0)
      DrawSwingPoint(ltf_prevHigh, "LTF_PrevHigh", clrOrange, LTF);
   
   if(ltf_lastLow.price > 0)
      DrawSwingPoint(ltf_lastLow, "LTF_Low", clrGreen, LTF);
   
   if(ltf_prevLow.price > 0)
      DrawSwingPoint(ltf_prevLow, "LTF_PrevLow", clrGreen, LTF);
   
   // Draw current price lines for reference
   double currentBid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   double currentAsk = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   
   CreateHorizontalLine("Current_Bid", currentBid, clrYellow, STYLE_DASH);
   CreateHorizontalLine("Current_Ask", currentAsk, clrYellow, STYLE_DASH);
   
   // Draw trade condition zones
   if(ltf_lastHigh.price > 0)
      CreateHorizontalLine("Sell_Zone_Min", ltf_lastHigh.price, clrRed, STYLE_SOLID);
   
   if(ltf_lastLow.price > 0)
   {
      CreateHorizontalLine("Buy_Zone_Max", ltf_lastLow.price, clrBlue, STYLE_SOLID);
      CreateHorizontalLine("Sell_TP_Level", ltf_lastLow.price, clrGreen, STYLE_DOT);
   }
   
   // Draw structure labels
   DrawStructureLabels();
}

Hier überwacht der Code die Integrität der Marktstruktur im niedrigeren Zeitrahmen (LTF) und setzt sie automatisch zurück, falls ein Strukturbruch auftritt. Die Funktion CheckStructureBreak() ruft die letzte LTF-Kerze ab und vergleicht deren Schlusskurs mit den aktuellen strukturellen Extremwerten. Befindet sich der Markt in einer bullischen Struktur und schließt der Kurs unterhalb des letzten Swing-Tiefs, signalisiert dies einen Durchbruch, wodurch ResetLTFStructure() ausgelöst wird. Umgekehrt gilt: Befindet sich der Markt in einer bärischen Struktur und schließt der Kurs über dem letzten Swing-Hoch, wird die Struktur ebenfalls zurückgesetzt. Die Funktion ResetLTFStructure() löscht alle LTF-Swing-Punkte, setzt den Strukturzustand auf neutral und setzt die Variablen zur Handelsverfolgung zurück, wodurch sichergestellt wird, dass die nachfolgende Logik nur neue Strukturformationen berücksichtigt.

Die Funktion UpdateVisualization() bietet eine übersichtliche visuelle Darstellung in Echtzeit sowohl der Swings in höheren und niedrigeren Zeitrahmen als auch des aktuellen Kurses und der wichtigsten Handelszonen. Zunächst werden alle vorhandenen Objekte mit dem Präfix MS_ entfernt, anschließend werden alle für HTF und LTF relevanten Swing-Punkte mithilfe farbcodierter Markierungen eingezeichnet. Die aktuellen Geld- und Briefkurse werden zur Orientierung durch gelbe gestrichelte Linien dargestellt, während Handelszonen wie Verkaufs- und Kaufniveaus sowie Take-Profit-Niveaus durch durchgezogene oder gepunktete Linien eingezeichnet sind.

//+------------------------------------------------------------------+
//| Draw Swing Point                                                 |
//+------------------------------------------------------------------+
void DrawSwingPoint(SwingPoint &sp, string name, color clr, ENUM_TIMEFRAMES tf)
{
   ObjectCreate(0, "MS_" + name, OBJ_ARROW, 0, sp.time, sp.price);
   ObjectSetInteger(0, "MS_" + name, OBJPROP_ARROWCODE, sp.type == 1 ? 218 : 217);
   ObjectSetInteger(0, "MS_" + name, OBJPROP_COLOR, clr);
   ObjectSetInteger(0, "MS_" + name, OBJPROP_WIDTH, 3);
   ObjectSetString(0, "MS_" + name, OBJPROP_TOOLTIP, 
                  StringFormat("%s: %.5f", sp.type == 1 ? "High" : "Low", sp.price));
}

//+------------------------------------------------------------------+
//| Draw Structure Labels                                            |
//+------------------------------------------------------------------+
void DrawStructureLabels()
{
   string biasText = "HTF Bias: ";
   switch(htfBias)
   {
      case STRUCT_BULLISH: biasText += "BULLISH"; break;
      case STRUCT_BEARISH: biasText += "BEARISH"; break;
      default: biasText += "NEUTRAL"; break;
   }
   
   string ltfText = "LTF Structure: ";
   switch(ltfStructure)
   {
      case STRUCT_BULLISH: ltfText += "HH+HL"; break;
      case STRUCT_BEARISH: ltfText += "LL+LH"; break;
      default: ltfText += "NEUTRAL"; break;
   }
   
   // Current trade conditions
   string conditionText = "";
   if(CheckBuyCondition())
      conditionText = "BUY Condition: ACTIVE (Price < LTF Low)";
   else if(CheckSellCondition())
      conditionText = "SELL Condition: ACTIVE (Price > LTF High & Low)";
   else
      conditionText = "No Trade Condition Met";
   
   // Create label objects
   CreateLabel("MS_BiasLabel", biasText, 10, 20, clrDarkOrange);
   CreateLabel("MS_LTFLabel", ltfText, 10, 40, clrDarkOrange);
   CreateLabel("MS_ConditionLabel", conditionText, 10, 60, 
               (CheckBuyCondition() || CheckSellCondition()) ? clrLime : clrGray);
   
   // Display LTF swing prices
   if(ltf_lastHigh.price > 0)
      CreateLabel("MS_LTFHighLabel", StringFormat("LTF High: %.5f", ltf_lastHigh.price), 10, 80, clrOrange);
   
   if(ltf_lastLow.price > 0)
      CreateLabel("MS_LTFLowLabel", StringFormat("LTF Low: %.5f", ltf_lastLow.price), 10, 100, clrGreen);
}

//+------------------------------------------------------------------+
//| Create Text Label                                                |
//+------------------------------------------------------------------+
void CreateLabel(string name, string text, int x, int y, color clr)
{
   ObjectCreate(0, "MS_" + name, OBJ_LABEL, 0, 0, 0);
   ObjectSetString(0, "MS_" + name, OBJPROP_TEXT, text);
   ObjectSetInteger(0, "MS_" + name, OBJPROP_XDISTANCE, x);
   ObjectSetInteger(0, "MS_" + name, OBJPROP_YDISTANCE, y);
   ObjectSetInteger(0, "MS_" + name, OBJPROP_COLOR, clr);
   ObjectSetInteger(0, "MS_" + name, OBJPROP_FONTSIZE, 10);
}

//+------------------------------------------------------------------+
//| Create Horizontal Line                                           |
//+------------------------------------------------------------------+
void CreateHorizontalLine(string name, double price, color clr, ENUM_LINE_STYLE style)
{
   ObjectCreate(0, "MS_" + name, OBJ_HLINE, 0, 0, price);
   ObjectSetInteger(0, "MS_" + name, OBJPROP_COLOR, clr);
   ObjectSetInteger(0, "MS_" + name, OBJPROP_STYLE, style);
   ObjectSetInteger(0, "MS_" + name, OBJPROP_WIDTH, 1);
}

In diesem Codeabschnitt bietet das System eine umfassende Visualisierung der Marktstruktur und der Handelsbedingungen im Chart. Die Funktion DrawSwingPoint() markiert Swing-Hochs und -Tiefs mit Pfeilen, die zur besseren Übersichtlichkeit farblich gekennzeichnet sind, und zeigt in Tooltips den genauen Kurs sowie die Art (Hoch oder Tief) an. DrawStructureLabels() ergänzt dies durch die Anzeige von Textinformationen zur Tendenz im höheren Zeitrahmen (HTF), zur aktuellen Struktur im niedrigeren Zeitrahmen (LTF) sowie zu den aktuellen Handelsbedingungen und verschafft Händlern so einen schnellen Überblick über den Marktkontext. Die Markierungen heben zudem die LTF-Hochs und -Tiefs hervor, was dabei hilft, potenzielle Umkehrzonen zu visualisieren und die sofortige Entscheidungsfindung zu unterstützen.

Zur Unterstützung dieser visuellen Elemente sorgen die Hilfsfunktionen CreateLabel() und CreateHorizontalLine() für eine einheitliche Erstellung von Textbeschriftungen und horizontalen Referenzlinien. Beschriftungen werden an bestimmten Bildschirmkoordinaten mit anpassbaren Farben und Schriftgrößen platziert, während horizontale Linien wichtige Kursniveaus wie aktuelle Geld- und Briefkurse, Swing-Hochs und -Tiefs sowie Handelszonen anzeigen. Zusammen wandeln diese Funktionen rohe Strukturdaten in ein übersichtliches, umsetzbares visuelles Rahmenwerk um, sodass Händler sowohl Marktextreme als auch potenzielle Handelsmöglichkeiten direkt auf dem Chart in Echtzeit erkennen können.


Backtest-Ergebnisse

Das Backtesting wurde auf dem H1-Zeitrahmen über den Testzeitraum vom 1. Dezember 2025 bis 1. Januar 2026 mit den Standardeinstellungen ausgewertet:

Kapitalkurven

Ergebnisse des Backtests



Schlussfolgerung

Zusammenfassend lässt sich sagen, dass wir Swing Extremes und den Indikator für Rücksetzer automatisiert haben, indem wir die Marktstruktur aus niedrigeren und höheren Zeitrahmen in ein vollständig programmgesteuertes System umgesetzt haben. Die Automatisierung erkennt Swing-Hochs und -Tiefs sowohl auf hoher als auch auf niedriger Zeitebene, überwacht kontinuierlich die Kursentwicklung auf strukturelle Durchbrüche und identifiziert überzogene Kursbewegungen, die auf mögliche Trendumkehren hindeuten. Es wurden Regeln für die Trade-Ausführung integriert, darunter Bedingungen wie ein Kauf, wenn der Kurs unter dem letzten LTF-Tief schließt, oder der Verkauf, wenn der Kurs sowohl über das letzte LTF-Hoch als auch über das letzte LTF-Tief steigt, wobei die Take-Profit-Niveaus von den entsprechenden Swing-Punkten übernommen wurden. Auch die Visualisierungsfunktionen wurden automatisiert, sodass Swings, Handelszonen und Angaben zur Markttendenz in Echtzeit grafisch dargestellt werden können, was einen sofortigen, strukturierten Überblick über den Markt ohne manuelle Analyse ermöglicht.

Zusammenfassend lässt sich sagen, dass diese Automatisierung den Entscheidungsprozess strafft und emotionale Verzerrungen sowie manuelle Fehler reduziert, indem sie Handelsgeschäfte auf der Grundlage struktureller Extreme objektiv ausführt. Händler können sich darauf verlassen, dass das System mehrere Zeitrahmen gleichzeitig überwacht, potenzielle Rücksetzer oder Trendwenden frühzeitig erkennt und Trades mit vorab berechneten Risiko- und Gewinnzielen ausführt. Durch die Kombination aus automatisierter Trade-Ausführung und intuitivem visuellen Feedback steigert das Tool die Effizienz, Konsistenz und Übersichtlichkeit und macht den Handel einfacher, schneller und disziplinierter.

Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/21330

Beigefügte Dateien |
Letzte Kommentare | Zur Diskussion im Händlerforum (4)
Elijah sanchey
Elijah sanchey | 25 Feb. 2026 in 22:12
Wie erhalten wir diesen Indikator?
Juvenille Emperor Limited
Eleni Anna Branou | 25 Feb. 2026 in 22:13
Elijah sanchey #:
Wie erhalten wir diesen Indikator?
Lesen Sie den Artikel.
Jacryptoking
Jacryptoking | 26 Feb. 2026 in 12:43
Du kannst zwar programmieren, musst aber mehr Zeit darauf verwenden, die Strategie zu verstehen, die du umsetzt.


Deine EAs scheitern an der Logik, die dem Konzept zugrunde liegt, das du umsetzt.
Educator Jay
Educator Jay | 26 Feb. 2026 in 13:10
Probier es aus und gib mir Bescheid
Neurostrukturelle Trading-Engine – NSTE (Teil I): Aufbau eines prop-firm-tauglichen Multi-Account-Systems Neurostrukturelle Trading-Engine – NSTE (Teil I): Aufbau eines prop-firm-tauglichen Multi-Account-Systems
Dieser Artikel beschreibt die Systemarchitektur für eine algorithmische Handelslösung mit mehreren Konten, die Kryptowährungs-CFDs auf MetaTrader 5 handelt und dabei die Vorgaben für Prop-Firmen berücksichtigt. Er definiert drei Kernprinzipien – festes Dollar-Risiko, ein Skript pro Konto und zentralisierte Konfiguration – und erläutert anschließend die Aufteilung zwischen Python und MQL5, die 60-Sekunden-Verarbeitungsschleife sowie die JSON-basierte Signalübermittlung. Die Leser erhalten praktische Anleitungen zur Berechnung der Lot-Größe, zu Sicherheitsprüfungen und zu praxisnahen Mustern für das Positionsmanagement und für einen zuverlässigen Live-Einsatz.
MQL5-Handelswerkzeuge (Teil 21): Hinzufügen eines Cyberpunk-Theme zu Regressionsdiagrammen MQL5-Handelswerkzeuge (Teil 21): Hinzufügen eines Cyberpunk-Theme zu Regressionsdiagrammen
In diesem Artikel erweitern wir das Tool zur Darstellung von Regressionsdiagrammen in MQL5 um einen Cyberpunk-Modus mit Neon-Leuchteffekten, Animationen und holografischen Effekten für eine eindrucksvolle Visualisierung. Wir integrieren die Umschaltung zwischen Themenmodi, dynamische Hintergründe mit Sternen, leuchtende Rahmen sowie Neonpunkte und -linien, wobei die Kompatibilität mit dem Standardmodus gewährleistet bleibt. Dieses System mit zwei Themenmodi wertet die Analyse von Symbolpaaren durch eine futuristische Ästhetik auf und unterstützt Echtzeitaktualisierungen sowie Interaktionen für aufschlussreiche Handelserkenntnisse.
Erstellen benutzerdefinierter Indikatoren in MQL5 (Teil 7): Hybride Time-Price-Opportunity-(TPO)-Marktprofile für die Analyse von Handelssitzungen Erstellen benutzerdefinierter Indikatoren in MQL5 (Teil 7): Hybride Time-Price-Opportunity-(TPO)-Marktprofile für die Analyse von Handelssitzungen
In diesem Artikel entwickeln wir einen benutzerdefinierten Indikator in MQL5 für hybride Time Price Opportunity (TPO)-Marktprofile, der mehrere Zeitrahmen wie Intraday, täglich, wöchentlich, monatlich sowie feste Zeitintervalle mit Zeitzonenanpassungen unterstützt. Der Indikator unterteilt die Kurse in ein Raster, erfasst Sitzungsdaten wie Höchst-, Tiefst-, Eröffnungs- und Schlusskurse und berechnet wichtige Kennzahlen wie den Point of Control und den Wertebereich auf der Grundlage von TPO-Zählwerten. Der Indikator stellt Profile visuell im Chart dar, wobei die Farben für TPO-Buchstaben, Single Prints, Wertebereiche, POC und Abschlussmarkierungen individuell angepasst werden können, was eine detaillierte Analyse von Handelssitzungen ermöglicht.
Vom Anfänger zum Experten: Automatisierung von Intraday-Strategien Vom Anfänger zum Experten: Automatisierung von Intraday-Strategien
Wir setzen das Konzept des EMA-50-Retests in einen verhaltensgesteuerten Expert Advisor für den Intraday-Handel um. Die Studie formalisiert Trendrichtung, EMA-Interaktionen (Pierce-and-Close), Reaktionsbestätigungen und optionale Filter und implementiert diese anschließend in MQL5 mithilfe modularer Funktionen und ressourcensicherer Handles. Durch visuelle Tests im Strategy Tester wird die Richtigkeit der Signale überprüft. Das Ergebnis ist eine übersichtliche Vorlage für die Codierung diskretionär gehandelter Abpraller.