English
preview
Swing-Extreme und Pullbacks in MQL5 (Teil 2): Automatisierung der Strategie mit einem Expert Advisor

Swing-Extreme und Pullbacks in MQL5 (Teil 2): Automatisierung der Strategie mit einem Expert Advisor

MetaTrader 5Beispiele |
19 2
Hlomohang John Borotho
Hlomohang John Borotho

Inhaltsverzeichnis

  1. Einführung
  2. Überblick über den Indikator
  3. Die ersten Schritte
  4. Indikator-Demo
  5. Schlussfolgerung


Einführung

Wenn Sie Trendwenden zu früh erkennen, gegen den Markttrend einsteigen oder nicht ganz verstehen, wo das wahre Extrem liegt, dann ist dieser Artikel genau das Richtige für Sie. Der Kurs kehrt selten mitten im Nirgendwo um – er dehnt sich aus, beschleunigt und dringt in Extremzonen vor, wo die Beteiligung nachlässt und sich das Risiko still und leise auf die andere Seite verlagert. Diese Momente zeigen sich in der Regel zuerst auf den niedrigeren Zeitrahmen, wo sich Swing-Hochs und -Tiefs häufen und der Kurs über seine jüngste Struktur hinausschießt. „Swing Extremes“ und der Pullback-Indikator basieren auf diesem Prinzip: Anstatt der Kursrichtung hinterherzulaufen, erkennen sie Überdehnungen – also genau jene Momente, in denen sich der Kurs im Verhältnis zu seiner unmittelbaren Marktstruktur zu weit und zu schnell bewegt hat.

Indem sich der Indikator auf das Swing-Verhalten in kürzeren Zeitrahmen konzentriert, zeigt er auf, wo sich der Kurs oberhalb oder unterhalb seiner jüngsten strukturellen Grenzen bewegt, und signalisiert damit Bedingungen, unter denen Kursrückgänge und eine Rückkehr zum Mittelwert (Mean Reversion) statistisch gesehen wahrscheinlich werden. Anstatt Hochs oder Tiefs vorherzusagen, reagiert das System auf strukturelle Anzeichen, sobald diese sich abzeichnen, und wandelt die Mikrostruktur in umsetzbare Umkehrzonen um. Das Ergebnis ist ein Ansatz, der Kursumkehrungen nicht als reine Spekulation betrachtet, sondern als messbare Reaktion darauf, dass der Kurs seine eigenen Extremwerte erreicht.


Überblick über den Indikator und seine Funktionsweise

Swing Extremes und Rücksetzer: Kursumkehrungen treten in der Regel auf, nachdem eine deutliche Überdehnung über die jüngste Struktur auf niedrigeren Zeitrahmen hinaus stattgefunden hat. In beiden Fällen ermittelt der Indikator zunächst das jüngste LTF-Hoch und das jüngste LTF-Tief, die zusammen eine kurzfristige strukturelle Spanne definieren. Dieser Bereich steht für ein auf dem niedrigeren Zeitrahmen ausgewogenes, akzeptiertes Kursverhalten – dort, wo der Markt zuvor Wert akzeptiert und sich innerhalb der Spanne normal hin- und herbewegt hat.

In dieser extrem bärischen Entwicklung fällt der Kurs kräftig unter das letzte Tief des LTF und geht damit weit über die jüngste Struktur hinaus. Dieser Bruch nach unten wird nicht als Fortsetzungssignal gewertet, sondern vielmehr als Anzeichen für eine Erschöpfung. Wenn der Kurs deutlich unterhalb sowohl des letzten Swing-Tiefs als auch der breiteren LTF-Strukturzone notiert, signalisiert der Indikator Zustände, in denen der Verkaufsdruck statistisch gesehen überzogen ist, was die Wahrscheinlichkeit einer bullischen Erholung oder einer Rückkehr zum Mittelwert in Richtung der vorherigen Struktur erhöht.

Extreme Abwärtsbewegung

Umgekehrt verdeutlicht die extrem bullische Entwicklung das gegenteilige Verhalten. Der Kurs steigt über das letzte Hoch des LTF hinaus, lässt die vorherige Struktur hinter sich und gerät in einen überdehnten Zustand. Anstatt dem Ausbruch hinterherzulaufen, interpretiert der Indikator diese Verschiebung als verwundbare Überdehnungszone – einen Bereich, in dem der Kaufdruck nachlassen könnte und ein Rücksetzer nach unten wahrscheinlicher wird. In beiden Fällen versucht der Indikator nicht, genaue Höchst- oder Tiefststände vorherzusagen; er konzentriert sich vielmehr darauf, strukturelle Ungleichgewichte zu erkennen, sodass Händler Kursumkehrungen als Reaktion darauf betrachten können, dass der Kurs seine eigenen Extreme erreicht hat, und nicht als willkürliche Gegentrend-Trades.

Extremer Aufwärtsbewegung


Die ersten Schritte

//+------------------------------------------------------------------+
//|                                               Swing Extremes.mq5 |
//|                                  Copyright 2025, MetaQuotes Ltd. |
//|                     https://www.mql5.com/en/users/johnhlomohang/ |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, MetaQuotes Ltd."
#property link      "https://www.mql5.com/en/users/johnhlomohang/"
#property version   "1.00"
#property indicator_chart_window
#property indicator_buffers 2
#property indicator_plots   2

#property indicator_label1  "Buy Signal"
#property indicator_type1   DRAW_ARROW
#property indicator_color1  clrLime
#property indicator_width1  2

#property indicator_label2  "Sell Signal"
#property indicator_type2   DRAW_ARROW
#property indicator_color2  clrRed
#property indicator_width2  2

//+------------------------------------------------------------------+
//| Inputs                                                           |
//+------------------------------------------------------------------+
input ENUM_TIMEFRAMES HTF = PERIOD_H1;
input ENUM_TIMEFRAMES LTF = PERIOD_M5;
input int             SwingBars = 3;
input int             ATR_Period      = 14;      // ATR calculation period
input double          ATR_Multiplier  = 1.0;     // How extreme price must move beyond swing
input bool            Visualize = true;

//+------------------------------------------------------------------+
//| Indicator Buffers                                                |
//+------------------------------------------------------------------+
double BuyBuffer[];
double SellBuffer[];

Zu Beginn definieren wir den Indikator als ein Chartfenster-Tool mit zwei Ausgabepuffern, die für visuelle Handelssignale vorgesehen sind. Diese Puffer sind so konfiguriert, dass sie Aufwärts- und Abwärtspfeile anzeigen, die Kauf- bzw. Verkaufssignale darstellen. Dank einer eindeutigen Farbcodierung und Größenanpassung sind Umkehrsignale im Chart sofort erkennbar. Anschließend legen wir die wichtigsten Konfigurationsparameter offen – die höheren und niedrigeren Zeitrahmen, die Tiefe der Swing-Erkennung und einen optionalen Schalter für die Visualisierung –, wodurch sich der Indikator an unterschiedliche Marktkontexte anpassen lässt, während seine strukturbasierte Logik flexibel bleibt. Die Kennzahlen „ATR_Period“ und „ATR_Multiplier“ helfen dabei, extreme Kursbewegungen im Verhältnis zur Struktur zu erfassen/bewerten. Abschließend werden die Kauf- und Verkaufspuffer deklariert, um die Signalwerte zu speichern und anzuzeigen, die später durch die Swing- und Marktstrukturberechnungen generiert werden.

//+------------------------------------------------------------------+
//| Swing Point Structure                                            |
//+------------------------------------------------------------------+
struct SwingPoint
{
   datetime time;
   double   price;
   int      type;   // 1 = High, -1 = Low
   int               barIndex;

   void Reset()
   {
      time = 0;
      price = 0;
      type = 0;
      barIndex =0;
   }
};

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

//+------------------------------------------------------------------+
//| Globals                                                          |
//+------------------------------------------------------------------+
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;
int atrHandle;
datetime lastBuySwingTime  = 0;
datetime lastSellSwingTime = 0;
double lastBuySwingPrice = 0;
double lastSellSwingPrice = 0;

In diesem Abschnitt stellen wir die zentralen Datenstrukturen vor, mit denen sich die Marktstruktur auf übersichtliche und wiederverwendbare Weise modellieren lässt. Die Struktur SwingPoint fasst alles zusammen, was der Indikator über ein Swing-Hoch oder ein Swing-Tief wissen muss: wann es auftrat, sein Kursniveau, ob es ein Hoch oder ein Tief darstellt und den Barindex, in dem es gebildet wurde. Durch die Bündelung dieser Informationen in einer einzigen Struktur und die Bereitstellung einer Reset()-Methode ermöglicht der Code das einfache und sichere Löschen und Neuerstellen von Swing-Daten, sobald die Struktur ungültig wird oder neu berechnet wird.

Die Trendrichtung des Marktes wird dann über die Enumeration „STRUCTURE_STATE“ ausgedrückt, die das Kursverhalten anhand des Verhältnisses zwischen aufeinanderfolgenden Swing-Hochs und -Tiefs als bullisch, bärisch oder neutral einstuft. Mithilfe dieses Rahmens werden separate globale Swing-Verfolgungen sowohl für den höheren Zeitrahmen (HTF) als auch für den niedrigeren Zeitrahmen (LTF) geführt, wodurch der Indikator die allgemeine Tendenz mit der lokalen Struktur vergleichen kann. Zusätzliche globale Variablen erfassen den Zeitpunkt und den Kurs des zuletzt verwendeten Kauf- oder Verkaufs-Swings. Dadurch wird sichergestellt, dass Signale nur einmal pro strukturellem Ereignis generiert werden, und wiederholte Auslösungen durch dasselbe Swing-Extrem werden verhindert.

//+------------------------------------------------------------------+
//| Indicator Init                                                   |
//+------------------------------------------------------------------+
int OnInit()
{
   atrHandle = iATR(_Symbol, LTF, ATR_Period);

   SetIndexBuffer(0, BuyBuffer, INDICATOR_DATA);
   SetIndexBuffer(1, SellBuffer, INDICATOR_DATA);

   PlotIndexSetInteger(0, PLOT_ARROW, 233); // arrow up
   PlotIndexSetInteger(1, PLOT_ARROW, 234); // arrow down

   ArrayInitialize(BuyBuffer, EMPTY_VALUE);
   ArrayInitialize(SellBuffer, EMPTY_VALUE);

   return INIT_SUCCEEDED;
}

//+------------------------------------------------------------------+
//| Indicator Calculation                                            |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
{
   static datetime lastLtfBar = 0;
   datetime currentLtfBar = iTime(_Symbol, LTF, 0);

   if(currentLtfBar == lastLtfBar)
      return rates_total;

   lastLtfBar = currentLtfBar;

   UpdateHTFStructure();
   UpdateLTFStructure();
   CheckStructureBreak();

   int bar = rates_total - 1;

   BuyBuffer[bar]  = EMPTY_VALUE;
   SellBuffer[bar] = EMPTY_VALUE;

   if(CheckBuyCondition())
      BuyBuffer[bar] = low[bar] - (10 * _Point);

   if(CheckSellCondition())
      SellBuffer[bar] = high[bar] + (10 * _Point);

   if(Visualize)
      UpdateVisualization();

   return rates_total;
}

Dieser Abschnitt initialisiert die Ausführungslogik des Indikators und steuert diese. In „OnInit“ initialisieren wir den ATR-Indikator; anschließend werden die Kauf- und Verkaufspuffer an die Plot-Ausgaben des Indikators gebunden, so konfiguriert, dass sie Aufwärts- und Abwärtspfeile anzeigen, und mit leeren Werten vorbelegt, um unerwünschte Signale zu vermeiden. Die Funktion „OnCalculate“ verarbeitet den Indikator dann erst, wenn eine neue Bar im niedrigeren Zeitrahmen erscheint, wodurch effiziente Aktualisierungen gewährleistet werden und wiederholte Neuberechnungen bei jedem Tick vermieden werden. Bei jeder neuen LTF-Bar aktualisiert der Indikator die Marktstruktur der höheren und niedrigeren Zeitrahmen, prüft auf Strukturbrüche und bewertet die Kauf- und Verkaufsbedingungen; wird ein gültiges Setup erkannt, zeichnet er zur besseren Übersichtlichkeit einen Signalpfeil leicht oberhalb oder unterhalb der aktuellen Bar ein und aktualisiert dabei optional alle visuellen Strukturelemente im Chart.

//+------------------------------------------------------------------+
//| 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);
}

Diese beiden Funktionen dienen dazu, die Marktstruktur aus Kursdaten abzuleiten, indem sie Swing-Hochs und Swing-Tiefs sowohl auf höheren als auch auf niedrigeren Zeitrahmen identifizieren. Jede Funktion beginnt damit, ein festgelegtes Zeitfenster mit historischen Kerzen zu laden und sicherzustellen, dass genügend Daten vorhanden sind, um Swings auf der Grundlage der konfigurierten SwingBars-Tiefe auszuwerten. Die Swing-Erkennungslogik überprüft anschließend jede infrage kommende Bar und vergleicht deren Höchst- und Tiefstwert mit den benachbarten Kerzen auf beiden Seiten, wobei ein Swing-Hoch oder Swing-Tief nur dann bestätigt wird, wenn sich der Kurs deutlich von der umgebenden Struktur abhebt. Wird ein neuer Swing-Punkt erkannt, wird der vorherige Swing beibehalten und der jüngste Swing aktualisiert, wodurch ein fortlaufender Verlauf der Struktur anstelle eines einzelnen, isolierten Punktes erfasst wird.

Sobald die Swing-Punkte aktualisiert sind, wandeln die Funktionen diese Rohpreisinformationen in einen Richtungskontext um. Für den höheren Zeitrahmen werden die jüngsten und vorherigen Swings ausgewertet, um die allgemeine Markttendenz zu ermitteln, während die Struktur des niedrigeren Zeitrahmens separat klassifiziert wird, um das kurzfristige Verhalten zu erfassen. Diese Trennung ermöglicht es dem Indikator, Umkehrbewegungen auf Mikroebene mit der Struktur auf Makroebene in Einklang zu bringen, indem dieselbe Swing-Logik auf verschiedenen Zeitrahmen angewendet wird, um ein kohärentes Bild von Trend, Konsolidierung oder Überdehnung zu erstellen, ohne auf nachlaufende Indikatoren zurückgreifen zu müssen.

//+------------------------------------------------------------------+
//| 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;
}

//+------------------------------------------------------------------+
//| New Sell Condition                                               |
//+------------------------------------------------------------------+
bool CheckSellCondition()
{
   // Only allow sells if HTF bias is BEARISH
   if(htfBias != STRUCT_BEARISH)
      return false;

   // Need valid LTF swing points
   if(ltf_lastHigh.price == 0 || ltf_lastLow.price == 0)
      return false;
   
   double currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   
   double atr = GetCurrentATR();
   if(atr == 0) return false;
   
   bool priceAboveHigh = currentPrice > (ltf_lastHigh.price + atr * ATR_Multiplier);
   bool priceAboveLow  = currentPrice > ltf_lastLow.price;
   
   bool isNewSwing = (ltf_lastHigh.time != lastSellSwingTime) || 
                     (ltf_lastHigh.price != lastSellSwingPrice);
   
   return (priceAboveHigh && priceAboveLow && isNewSwing);
}

//+------------------------------------------------------------------+
//| New Buy Condition                                                |
//+------------------------------------------------------------------+
bool CheckBuyCondition()
{
   // Only allow buys if HTF bias is BULLISH
   if(htfBias != STRUCT_BULLISH)
      return false;

   // Need valid LTF swing point
   if(ltf_lastLow.price == 0)
      return false;
   
   double currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   
   double atr = GetCurrentATR();
   if(atr == 0) return false;
   
   bool priceBelowLow = currentPrice < (ltf_lastLow.price - atr * ATR_Multiplier);
   
   bool isNewSwing = (ltf_lastLow.time != lastBuySwingTime) || 
                     (ltf_lastLow.price != lastBuySwingPrice);
   
   return (priceBelowLow && isNewSwing);
}

In diesem Abschnitt wird erläutert, wie Rohdaten zu Swings in aussagekräftige Marktzusammenhänge umgewandelt werden. Die Funktion „DetectStructure“ wertet die Beziehung zwischen aufeinanderfolgenden Swing-Hochs und -Tiefs aus, um das Kursverhalten als bullisch, bärisch oder neutral einzustufen. Eine bullische Struktur gilt erst dann als bestätigt, wenn sowohl ein höheres Hoch als auch ein höheres Tief vorliegt, während eine bärische Struktur sowohl ein tieferes Tief als auch ein tieferes Hoch erfordert. Sind diese Bedingungen nicht erfüllt, wird der Markt als neutral betrachtet, wodurch Annahmen hinsichtlich der Richtung verhindert werden, wenn die Struktur unklar oder unvollständig ist.

Die Kauf- und Verkaufsbedingungen bauen auf diesem strukturellen Rahmen auf und berücksichtigen dabei sowohl die Ausrichtung auf HTF-Tendenz als auch einen ATR-basierten Schwellenwert für Überdehnung. Eine Verkaufsbedingung wird nur dann berücksichtigt, wenn die Tendenz im höheren Zeitrahmen bärisch ist und der Kurs um mehr als den festgelegten ATR-Multiplikator über das jüngste LTF-Swing-Hoch hinaussteigt (wobei er über der entsprechenden Swing-Struktur bleibt), was eher auf ein volatilitätsbereinigtes Extrem als auf einen geringfügigen Ausbruch hindeutet. Ebenso gilt eine Kaufbedingung nur dann als erfüllt, wenn die HTF-Tendenz bullisch ist und der Kurs um einen ATR-bereinigten Abstand unter das jüngste LTF-Tief fällt, was auf eine signifikante Überdehnung nach unten hindeutet. In beiden Fällen stellt die Logik sicher, dass jedes Swing-Extrem nur einmal gehandelt bzw. verwendet wird, indem ein Abgleich mit zuvor erfasster Zeit und Kurs des Swings erfolgt. So konzentriert sich der Indikator weiterhin auf strukturierte, sich nicht wiederholende Umkehrchancen, anstatt auf unbedeutende Swings zu reagieren.

Wenn Sie ein Signal erhalten, warten Sie zunächst auf die Übereinstimmung mit der im Chart dargestellten Tendenz des höheren Zeitrahmens (HTF), da Trades nur in Richtung dieser Tendenz gültig sind (eine bullische Tendenz erlaubt nur Käufe, eine bärische Tendenz nur Verkäufe). Eine Handelsbedingung wird aktiv, wenn der Kurs über das vom Indikator angezeigte Swing-Extrem des entsprechenden niedrigeren Zeitrahmens (LTF) hinausgeht – bei Kaufpositionen unter das letzte Tief des LTF bzw. bei Verkaufspositionen über das letzte Hoch (und Tief) des LTF – und die Zustandsanzeige ein aktives Setup bestätigt. Zum Risikomanagement können Stopps jenseits des entsprechenden LTF-Swing-Extrems gesetzt werden (beispielsweise oberhalb des letzten LTF-Hochs bei Verkaufspositionen und unterhalb des letzten LTF-Tiefs bei Kaufpositionen), da diese Niveaus innerhalb der Logik des Indikators strukturelle Ungültigkeitspunkte darstellen. Bei Gewinnzielen können Verkaufspositionen auf das letzte Tief des LTF oder den Mittelwert zwischen den Swings abzielen, während Kaufpositionen je nach Risikobereitschaft das letzte Hoch des LTF oder den Mittelwert anvisieren können.

//+------------------------------------------------------------------+
//| 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();
}

In diesem Abschnitt befassen wir uns mit der strukturellen Validierung und stellen sicher, dass der Indikator weiterhin dem aktuellen Marktverhalten entspricht. Die Funktion „CheckStructureBreak“ überwacht kontinuierlich die aktuellste Kerze des niedrigeren Zeitrahmens und vergleicht deren Schlusskurs mit den jüngsten Swing-Niveaus. Wenn eine bullische Struktur dadurch ungültig wird, dass der Kurs unter dem letzten Swing-Tief schließt, oder eine bärische Struktur dadurch durchbrochen wird, dass der Kurs über dem letzten Swing-Hoch schließt, gilt die bestehende Struktur als gescheitert. Daraufhin setzt die Funktion „ResetLTFStructure“ die Signale zurück und aktualisiert sie, wodurch verhindert wird, dass veraltete Swing-Beziehungen zukünftige Signale beeinflussen, und sichergestellt wird, dass nur gültige, intakte Strukturen verwendet werden.

Sobald die Struktur bestätigt oder neu aufgebaut ist, setzt die Funktion „UpdateVisualization“ die gesamte interne Logik in einen klaren Chart-Kontext in Echtzeit um. Es zeichnet Swing-Punkte aus höheren und niedrigeren Zeitrahmen, aktuelle Kursreferenzwerte sowie wichtige strukturelle Niveaus nach, die Kauf- und Verkaufszonen definieren. Durch die visuelle Trennung von HTF- und LTF-Swings sowie die Einblendung situationsspezifischer Niveaus und Beschriftungen ermöglicht der Indikator den Händlern, nicht nur sofort zu erkennen, wo ein Signal auftritt, sondern auch, warum es existiert – wodurch jedes Setup auf einer beobachtbaren Marktstruktur basiert und nicht auf abstrakten Signalen.

//+------------------------------------------------------------------+
//| 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 = "Potential BUY Condition: ACTIVE (Price < LTF Low)";
   else if(CheckSellCondition())
      conditionText = "Potential SELL Condition: ACTIVE (Price > LTF High & Low)";
   else
      conditionText = "No Anticipated 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);
}

//+------------------------------------------------------------------+
//|  Get Current ATR                                                 |
//+------------------------------------------------------------------+
double GetCurrentATR()
{
   double atr[];
   ArraySetAsSeries(atr, true);
   
   if(CopyBuffer(atrHandle, 0, 0, 1, atr) <= 0)
      return 0;
      
   return atr[0];
}

In diesem letzten Abschnitt geht es darum, die internen Swing- und Strukturdaten in eine visuelle Darstellung umzuwandeln, die Händler auf dem Chart sofort interpretieren können. Die Funktion „DrawSwingPoint“ erstellt an jedem erkannten Swing-Hoch oder -Tief Pfeilobjekte, wobei die Pfeilrichtung, die Farbe und der Tooltip anzeigen, ob es sich um einen Höchst- oder einen Tiefststand handelt. So lassen sich die genauen Zeitpunkte, an denen der Markt einen signifikanten Swing verzeichnete, leicht erkennen, wodurch die Lücke zwischen rohen Zahlenangaben und verwertbaren Erkenntnissen geschlossen wird. Jeder Swing-Punkt ist deutlich gekennzeichnet, was Tradern hilft, die Kurs-Extreme und deren zeitlichen Verlauf zu verstehen, ohne die Kerzenmuster manuell analysieren zu müssen.

Zusammen mit den Swing-Punkten bieten die Funktion „DrawStructureLabels“ und Hilfsfunktionen wie „CreateLabel“ und „CreateHorizontalLine“ einen dynamischen Überblick über die Marktlage und potenzielle Handelsbedingungen. Es werden die HTF- und LTF-Trends angezeigt, die Aufschluss darüber geben, ob der allgemeine Trend bullisch, bärisch oder neutral ist und ob der aktuelle Kurs in eine potenzielle Kauf- oder Verkaufszone eingetreten ist. Horizontale Linien markieren wichtige Swing-Hochs und -tiefs, und farbcodierte Beschriftungen verdeutlichen Zonen und Bedingungen visuell. Anschließend ruft die Funktion „GetCurrentATR“ den aktuellsten ATR-Wert aus dem ATR-Indikatorpuffer ab und gibt ihn zurück; falls kein Zugriff auf die Daten möglich ist, wird 0 zurückgegeben. Zusammen ergeben diese Elemente eine dynamische, interaktive Darstellung der Marktstruktur, die Händlern dabei hilft, Trendwenden und Kursrückgänge zu antizipieren.



Indikator-Demo



Schlussfolgerung

Zusammenfassend lässt sich sagen, dass wir die „Swing Extremes“ und den „Pull-Back-Indikator“ entwickelt haben, indem wir die Swing-Erkennung über mehrere Zeitrahmen hinweg mit einer Echtzeit-Marktanalyse auf niedrigerer Zeitebene kombiniert haben. Der Indikator identifiziert wichtige Swing-Hochs und -Tiefs sowohl in höheren als auch in niedrigeren Zeitrahmen, bewertet die aktuelle Marktstruktur als bullisch, bärisch oder neutral und markiert potenzielle Überdehnungszonen, in denen eine Kursumkehr wahrscheinlich ist. Visuelle Hinweise, darunter Pfeile für Swing-Punkte, horizontale Linien für wichtige Niveaus sowie dynamische Beschriftungen für die HTF-Tendenz, die LTF-Struktur und aktive Handelsbedingungen, machen es leicht, dem inneren Rhythmus des Marktes zu folgen. Durch die Integration von Strukturvalidierung, Überdehnungsprüfungen und Swing-Resets stellt der Indikator sicher, dass die Signale zeitnah und relevant sind und sich nicht an denselben Swing-Extremen wiederholen.

Zusammenfassend lässt sich sagen, dass dieser Indikator den Handel verbessern kann, indem er die rohen Kursbewegungen in eine klare, umsetzbare Übersicht über potenzielle Trendwenden und Korrekturen umwandelt. Trader können Kursreaktionen bei extremen Swings vorhersehen, schnell erkennen, ob die Bedingungen für bullische oder bärische Konstellationen sprechen, und fundierte Entscheidungen treffen, ohne die Swings über mehrere Zeitrahmen hinweg manuell verfolgen zu müssen. Durch die Echtzeit-Visualisierung der Marktstruktur und der Handelszonen werden Spekulationen reduziert, die Analyse optimiert und sicherere, präzisere Ein- und Ausstiege ermöglicht – wodurch die Lücke zwischen Beobachtung und Ausführung effektiv geschlossen wird. Nachdem Sie diesen Artikel gelesen haben, sollten Sie zumindest in der Lage sein, das LTF-Extrem zu ermitteln, es mithilfe einer HTF-Voreinstellung zu filtern und strukturierte Regeln für Einstieg, Stop und Ausstieg anzuwenden.

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

Beigefügte Dateien |
Swing_Extremes.mq5 (17.93 KB)
Letzte Kommentare | Zur Diskussion im Händlerforum (2)
Educator Jay
Educator Jay | 28 Feb. 2026 in 12:18
Pfeile erscheinen und verschwinden. Noch nicht stabil
Jacryptoking
Jacryptoking | 1 März 2026 in 11:48
Gute Idee, aber nicht richtig umgesetzt.

Die Kauf- und Verkaufspfeile verschwinden.
Die fraktalen Objekte verschwinden.

Für die LTF-Struktur.

Verwende den internen Choch, um HH/LL zu identifizieren.

Nachdem der Choch LH/HL identifiziert hat, ist es wahrscheinlich, dass der Choch HH/LL innerhalb eines HTF erzeugt. Der Trend folgt auf den LTF-Liquiditätsschwung.

Sobald kein neuer Choch mehr auftritt, können keine weiteren Einträge bei LH/HL erfolgen

Der „Lux Algo Ultimate Smart Concept MT5“-Indikator verfügt über eine gute interne Choch-/BOS-Formel.


Wenn der HTF ein bullisches Signal gibt
, gibt der LTF ein neutrales Signal, aber die HH und HL sind auf dem Chart zu sehen. ..


Ich bin ein profitabler Trader. Ich liebe Automatisierung und Tools, die mich bei meinem Trading unterstützen.
Eines Tages wirst du etwas Solides entwickeln, da du ein sehr guter Programmierer bist.

Danke.
Vom Neuling zum Experten:  Erweiterung einer Liquiditätsstrategie mit Trendfiltern Vom Neuling zum Experten: Erweiterung einer Liquiditätsstrategie mit Trendfiltern
Der Artikel erweitert eine liquiditätsbasierte Strategie um einen einfachen Trendfilter: Liquiditätszonen sollen nur in Richtung des EMA(50) gehandelt werden. Darin werden Filterregeln erläutert, die wiederverwendbare Klasse „TrendFilter.mqh“ sowie die EA-Integration in MQL5 vorgestellt und Tests mit und ohne Filter miteinander verglichen. Die Leser profitieren von einer klaren Trendorientierung, weniger übermäßigen Handelsaktivitäten in Gegentrendphasen und einsatzbereiten Quelldateien.
MQL5-Trading Tools (Teil 19): Entwicklung einer interaktiven Werkzeugpalette zum Zeichnen in Charts MQL5-Trading Tools (Teil 19): Entwicklung einer interaktiven Werkzeugpalette zum Zeichnen in Charts
In diesem Artikel erstellen wir in MQL5 eine interaktive Werkzeugpalette zum Zeichnen in Charts mit verschiebbaren und in der Größe anpassbaren Panels sowie der Möglichkeit, das Design zu wechseln. Wir fügen Schaltflächen für Werkzeuge wie Fadenkreuz, Trendlinien, Linien, Rechtecke, Fibonacci, Text und Pfeile hinzu und verarbeiten Mausereignisse zur Aktivierung und für Anweisungen. Dieses System verbessert die Handelsanalyse durch eine anpassbare Benutzeroberfläche und unterstützt Echtzeit-Interaktionen mit den Charts.
MQL5 Trading Tools (Teil 20): Grafiken auf Zeichenflächen mit statistischer Korrelations- und linearer Regressionsanalyse MQL5 Trading Tools (Teil 20): Grafiken auf Zeichenflächen mit statistischer Korrelations- und linearer Regressionsanalyse
In diesem Artikel erstellen wir in MQL5 ein canvasbasiertes grafisches Tool für statistische Korrelations- und lineare Regressionsanalysen zwischen zwei Symbolen, das sich per Drag & Drop verschieben und in der Größe anpassen lässt. Wir nutzen ALGLIB für Regressionsberechnungen, dynamische Achsenbeschriftungen, Datenpunkte und ein Statistikfeld, in dem Steigung, Achsenabschnitt, Korrelation und R-Quadrat angezeigt werden. Diese interaktive Visualisierung liefert wertvolle Erkenntnisse für Pair-Trading-Strategien und bietet anpassbare Designs, Rahmen sowie Echtzeit-Aktualisierungen bei neuen Bars.
Verwendung des MQL5-Wirtschaftskalenders zur Nachrichtenfilterung (Teil 1): Implementierung von Zeitfenstern vor und nach Nachrichtenereignissen in MQL5 Verwendung des MQL5-Wirtschaftskalenders zur Nachrichtenfilterung (Teil 1): Implementierung von Zeitfenstern vor und nach Nachrichtenereignissen in MQL5
Wir entwickeln einen kalendergesteuerten Nachrichtenfilter vollständig in MQL5 und verzichten dabei auf Webanfragen und externe DLLs. Teil 1 behandelt das Laden und Zwischenspeichern von Ereignissen, deren Zuordnung zu Symbolen nach Währung, das Filtern nach Bedeutung, die Definition von Vor- und Nachlaufzeitfenstern sowie das Sperren neuer Trades während aktiver Nachrichten, mit der optionalen Möglichkeit, Positionen vor Nachrichtenereignissen zu schließen. Das Ergebnis ist ein konfigurierbarer, prop-firm-tauglicher Kontrollmechanismus, der falsche Handelspausen vermeidet und Einstiegspunkte bei hohen Schwankungen schützt.