English 日本語
preview
Entwicklung des Price Action Analysis Toolkit (Teil 21): Das Tool Market Structure Flip Detector

Entwicklung des Price Action Analysis Toolkit (Teil 21): Das Tool Market Structure Flip Detector

MetaTrader 5Handelssysteme |
128 1
Christian Benjamin
Christian Benjamin

Inhalt


Einführung

Dieser EA bewältigt eine der schwierigsten Herausforderungen im Handel: falsche Umkehrsignale. Eine unruhige Kursentwicklung wirft einfache Pivot-Routinen über den Haufen und führt dazu, dass Händler in die Falle von Zick-Zack-Bewegungen geraten. Der Market Structure Flip Detector löst dieses Problem, indem er den ATR in einen flexiblen Filter der Anzahl der Balken umwandelt. Er ignoriert geringfügige Schwankungen, erfasst nur gültige Hochs und Tiefs und markiert dann einen „bearish flip“, wenn ein höheres Hoch zu einem niedrigeren Hoch wird, oder einen „bullish flip“, wenn ein niedrigeres Tief zu einem höheren Tief wird. Auf dem Weg dorthin werden Sie Folgendes sehen:

  • Der ATR wird in ein tiefes Maß umgewandelt, das sich in wilden Märkten ausdehnt und in ruhigen Märkten zusammenzieht.
  • Pivots werden bestätigt, indem eine genaue Anzahl von Balken auf jeder Seite eines möglichen Hochs oder Tiefs abgetastet werden.
  • Die Trendrichtung wird beibehalten, sodass Umschwünge erst nach dem Bruch der vorherigen Struktur ausgelöst werden.
  • Signale werden auf dem Chart mit Pfeilen, Pivot-Beschriftungen und einem Live-Statistikfeld angezeigt, die die Anzahl der Flips und das Timing verfolgt.

Am Ende haben wir einen robusten EA, der das Marktrauschen durchbricht und nur die saubersten, regelbasierten Umkehrhinweise liefert, komplett mit Sound- und Push-Benachrichtigungen, wenn echte Flips auftreten.


Warum ist dieses Tool wichtig?

Falsche Umkehrsignale können mehr als die Hälfte aller Pivot-basierten Auslöser in unruhigen Märkten ausmachen, was zu einem häufigen Hin und Her und einer negativen Erwartungshaltung führt. Durch die Verknüpfung unseres Filters für Umkehrpunkte mit dem Average True Range (ATR), den Wilder 1978 einführte, um die Volatilität genauer zu messen als einfache Hoch-Tief-Spannen, passen wir die erforderliche „Tiefe“ für einen Umkehrpunkt in Echtzeit an. Wenn der ATR während volatiler Ausbrüche ansteigt, weitet sich unser Filter, um kleine, erratische Schwankungen zu ignorieren; wenn sich die Märkte beruhigen, wird er enger, um echte Wendungen sofort zu erfassen.

Der True Range (TR) pro Balken ist definiert als:

TR Fomel


ATR ist der n-periodische einfache gleitende Durchschnitt des TR (Standard n = 14)

Wir konvertieren ATR in eine Tiefe d der Balken über:

ATR

Dabei ist Point die kleinste Preisänderung, m der ATR-Multiplikator und f der Lockerungsfaktor. 

Eine Abwärts-Umkehr tritt ein, wenn wir zunächst ein höheres Hoch verzeichnen und der nächste hohe Umkehrpunkt dann unter diesen Höchststand fällt. Eine Aufwärtsbewegung funktioniert auch andersherum. Wenn wir davon ausgehen, dass die Renditen einer Normalverteilung mit einem Durchschnitt von Null und einer Varianz, die wir als „Volatilität im Quadrat“ bezeichnen, folgen, dann ist die Chance, dass ein einzelner Balken innerhalb einer Spanne von „zwei mal unserer Tiefeneinstellung plus eins“ der höchste ist, einfach eins über eben diese „zwei mal Tiefe plus eins“. Da wir unsere Tiefeneinstellung direkt mit der Marktvolatilität verknüpfen, indem wir die Volatilität als ATR geteilt durch die Quadratwurzel von einem halben pi schätzen, können wir die Rate der Fehlsignale direkt steuern. In der Praxis bedeutet dies, dass wir Eingaben wählen können, die das Rauschen auf etwa 5 % begrenzen.

Die TradeStation-Forschung hat gezeigt, dass ATR-basierte Pivot-Fenster die von Rauschen getriebenen Handelsgeschäfte um ~40% reduzieren und den Nettogewinn um ~22% über 5 Jahre auf S&P 500-Daten steigern. QuantifiedStrategies.com berichtet, dass ATR-gefilterte Umkehrpunkte die Trefferquote von ~35% auf ~58% verbessern und das durchschnittliche Gewinn-Risiko-Verhältnis von ~1,1 auf ~1,8 in Backtests auf EURUSD und ES-Futures erhöhen. Das Feedback der Community auf TradingView hebt hervor, dass ATR-gefensterte Pivot-Tools eng mit institutionellen Orderflow-Breaks übereinstimmen, insbesondere auf 1-H- und 4-H-Charts.


Gliederung des Aktionsplans

Dieser EA filtert Marktrauschen heraus, indem er den aktuellen ATR-Wert in ein flexibles „Tiefen“-Fenster umwandelt, das bei volatilen Bedingungen breiter und bei ruhigen Bedingungen schmaler ist, und dann jedes Hoch oder Tief eines geschlossenen Balkens mit seinen Nachbarn innerhalb dieses Fensters vergleicht. Er merkt sich die letzten beiden bestätigten Hochs und Tiefs und verfolgt ein einfaches Bias-Flag, die auf „aufwärts“ umschaltet, wenn ein neues Hoch das vorherige übersteigt, oder auf „abwärts“, wenn ein neues Tief unter sein vorheriges fällt. Wenn sich die Marktstruktur umkehrt, d. h. wenn in einem Aufwärtstrend der letzte Höchststand niedriger ist als der vorherige Höchststand (bearish flip) oder in einem Abwärtstrend der letzte Tiefststand höher ist als der vorherige Tiefststand (bullish flip), lässt der EA einen farbigen Pfeil auf dem Chart erscheinen, kennzeichnet beide Pivots, aktualisiert ein Live-Statistikfeld und kann Ton- oder Push-Warnungen auslösen. Dieser Ansatz stellt sicher, dass Sie nur echte Umkehrungen wie „Hoch-Hoch zu Tief-Hoch“ oder „Tief-Tief zu Hoch-Tief“ sehen.

Bearish Flip

Ein Aufwärtstrend ist erkennbar, wenn der Kurs zwei aufeinanderfolgende höhere Hochs erreicht. Der Expert Advisor (EA) sucht anschließend nach einem hohen Umkehrpunkt, der niedriger ist als der vorherige hohe Umkehrpunkt. Ein hoher Umkehrpunkt ist definiert als der höchste Punkt innerhalb eines Fensters, das auf der Average True Range (ATR) basiert. Befindet sich der EA in einem „Aufwärts“-Zustand und erkennt dieses tiefere Hoch, markiert er den entsprechenden Balken mit einem roten Pfeil mit der Aufschrift „LH“. Zusätzlich zu diesem visuellen Hinweis generiert der EA eine Warnung und zeichnet den Abwärts-Umschwung auf, was signalisiert, dass die Verkäufer beginnen, die Kontrolle zu übernehmen.

Bearish Flip

Bullish Flip

Ein Abwärtstrend entsteht, wenn der Kurs zwei aufeinanderfolgende Tiefs verzeichnet. Anschließend ermittelt der Expert Advisor (EA) einen tiefen Umkehrpunkt, der über dem vorherigen tiefen Umkehrpunkt liegt. Ein tiefer Umkehrpunkt ist definiert als der niedrigste Punkt innerhalb eines Fensters, das auf der Average True Range (ATR) basiert. Befindet sich der EA in einem „Abwärts“-Zustand und erkennt dieses höhere Tief, zeichnet er einen grünen Pfeil mit der Aufschrift „HL“ auf den entsprechenden Balken. Darüber hinaus generiert es eine Warnmeldung und zeichnet die Aufwärtsbewegung auf, um eine mögliche Rückkehr des Kaufinteresses anzuzeigen.

Bullish Flip


Anatomie des Expert Advisors

Wenn wir eine beliebige MQL5-Datei starten, fügen wir #property-Zeilen hinzu, um die Szene festzulegen. Hier aktivieren wir die strikte Kompilierung, damit der Compiler alle unsicheren Casts oder veralteten Aufrufe erkennt. Wir fügen auch unsere Copyright-, Link- und Versions-Tags ein, damit jeder, der unseren Code liest, weiß, wer ihn geschrieben hat, wo er weitere Details finden kann und welche Iteration er gerade sieht. Diese Zeilen haben keinen Einfluss auf die Logik; sie sind unsere Art, die Identität der Datei zu markieren.

#property copyright "Copyright 2025, MetaQuotes Ltd."
#property link      "https://www.mql5.com/en/users/lynnchris"
#property version   "1.0"
#property strict

Als Nächstes legen wir alle Einstellungen offen, die ein Händler möglicherweise ändern möchte. Die ersten drei Eingaben bestimmen, wie wir Pivots mit ATR erkennen.

  • InpAtrPeriod legt fest, wie viele Balken in die ATR-Berechnung eingehen. Eine kurze Spanne (z. B. 7) reagiert schnell, kann aber Rauschen auffangen. Eine längere Spanne (z. B. 21) glättet Spitzen auf Kosten der Verzögerung.
  • InpAtrMultiplier wandelt diesen ATR-Bereich in eine Mindestschwingbreite um. Bei 1,0 benötigen wir eine ATR-Bewegung, um einen Pivot zu markieren. Wenn wir den Wert auf 1,5 oder 2,0 erhöhen, wird der Filter noch selektiver.
  • InpAtrLoosenFactor skaliert diese Breite nach unten zwischen 0 und 1. Ein Faktor von 0,5 halbiert die Anforderung, sodass Pivots früher angezeigt werden, was bei geringer Volatilität hilfreich sein kann.

Als Nächstes wird das Chartlayout behandelt.

  • InpAutoShift reserviert leere Balken auf der rechten Seite, wenn neue Balken erscheinen.
  • InpShiftBars legt fest, wie viele leere Balken übrig bleiben sollen (standardmäßig fünf).

Durch diesen einfachen Abstand wird verhindert, dass unsere Pfeile, Beschriftungen und Statistiken die Kursbewegung überlagern.

Schließlich bieten wir zwei Warnmethoden an:

  • InpEnableSound lässt den EA bei jedem Umkehrpunkt eine WAV-Datei abspielen.
  • InpSoundFile ist der Ort, an dem Sie die Datei aus Ihrem MetaTrader 5 Sounds-Ordner auswählen.
  • InpEnablePush sendet eine Push-Nachricht an Ihre MetaTrader 5 Mobile App.

Mit diesen Optionen können Sie wählen, ob Sie einen Alarm auf Ihrem Desktop hören, einen Ping auf Ihrem Telefon erhalten oder beides.

input int    InpAtrPeriod       = 14;      // How many bars for ATR
input double InpAtrMultiplier   = 1.0;     // Scale ATR into bar‑depth
input double InpAtrLoosenFactor = 0.5;     // Optional: loosen the swing filter
input bool   InpAutoShift       = true;    // Push bars left for visibility
input int    InpShiftBars       = 5;       // Number of bars for right margin
input bool   InpEnableSound     = true;    // Play a sound on flip
input string InpSoundFile       = "alert.wav";
input bool   InpEnablePush      = false;   // Send push notifications

Wenn der EA startet, prüft OnInit zunächst InpAutoShift und ruft, falls aktiviert, ChartSetInteger mit CHART_SHIFT und InpShiftBars auf, um neue Balken nach links zu schieben und freien Platz für Anmerkungen zu reservieren. Dann fordert er über iATR ein Handle für den in MetaTrader eingebauten ATR-Indikator an, speichert es in atrHandle und bricht sofort mit INIT_FAILED ab, wenn das Handle ungültig ist. Schließlich wird ein Ecklabel (ein OBJ_LABEL mit dem Namen panelName) erstellt, an der oberen linken Ecke angeheftet, horizontal und vertikal um 10 Pixel versetzt, die Schriftgröße auf 10 und die Farbe auf gelb gesetzt und INIT_SUCCEEDED zurückgegeben, um zu bestätigen, dass der ATR-Datenzugriff und das Statistik-Panel für OnTick bereit sind.

int OnInit()
{
   if(InpAutoShift)
      ChartSetInteger(0, CHART_SHIFT, InpShiftBars);

   atrHandle = iATR(_Symbol, _Period, InpAtrPeriod);
   if(atrHandle == INVALID_HANDLE)
      return INIT_FAILED;

   ObjectCreate(0, panelName, OBJ_LABEL, 0, 0, 0);
   ObjectSetInteger(0, panelName, OBJPROP_CORNER,    CORNER_LEFT_UPPER);
   ObjectSetInteger(0, panelName, OBJPROP_XDISTANCE, 10);
   ObjectSetInteger(0, panelName, OBJPROP_YDISTANCE, 10);
   ObjectSetInteger(0, panelName, OBJPROP_FONTSIZE,  10);
   ObjectSetInteger(0, panelName, OBJPROP_COLOR,     clrYellow);

   return INIT_SUCCEEDED;
}

Immer wenn wir den EA entfernen oder MetaTrader 5 herunterfahren, löscht OnDeinit alle Pfeile und Textobjekte, die wir erstellt haben, löscht die Beschriftung und gibt den ATR-Handle frei. Dies vermeidet Unordnung und setzt Ressourcen frei.

void OnDeinit(const int reason)
{
   ObjectsDeleteAll(0, -1, OBJ_ARROW);
   ObjectsDeleteAll(0, -1, OBJ_TEXT);
   ObjectDelete(0, panelName);
   if(atrHandle != INVALID_HANDLE)
      IndicatorRelease(atrHandle);
}

In unserem OnTick verhindern wir, dass unsere Pivot-Logik bei jeder einzelnen Preisaktualisierung ausgeführt wird, indem wir eine statische Datumsangabe lastBareinführen. Wir holen thisBar mit iTime(...,1), das die Eröffnungszeit des zuletzt geschlossenen Balkens liefert, und vergleichen sie mit lastBar. Wenn sie sich nicht verändert hat, wissen wir, dass wir uns immer noch in derselben Kerze befinden und kehren einfach zurück. Sobald thisBar abweicht, aktualisieren wir lastBar und fahren fort.

Dann rufen wir CopyBuffer auf dem ATR-Handle auf, um nur den letzten ATR-Wert abzurufen. Wenn dieser Aufruf fehlschlägt, brechen wir ab, um die Arbeit mit ungültigen Daten zu vermeiden. Mit einer gültigen ATR in der Hand berechnen wir unsere „Tiefe“ der Umkehrpunkte, indem wir die ATR-Kurseinheiten in eine Balkenanzahl umwandeln. Wir dividieren durch den kleinsten Preisschritt (SYMBOL_POINT), multiplizieren mit InpAtrMultiplier und InpAtrLoosenFactor und erzwingen dann mit MathMax ein Minimum von einem Balken. Dadurch erhalten wir eine dynamische Tiefe, die sich vergrößert, wenn die Volatilität ansteigt (was größere Ausschläge zur Markierung von Pivots erfordert), und sich verkleinert, wenn sich der Markt beruhigt (was engere Ausschläge zulässt), bevor wir an die eigentlichen Pivot-Erkennungsfunktionen weitergeben.

void OnTick()
{
   static datetime lastBar=0;
   datetime thisBar = iTime(_Symbol,_Period,1);
   if(thisBar == lastBar) return;
   lastBar = thisBar;

   double atrBuf[];
   if(CopyBuffer(atrHandle, 0, 1, 1, atrBuf) <= 0) return;
   double atr = atrBuf[0];

   int depth = MathMax(1,
      int(atr / SymbolInfoDouble(_Symbol, SYMBOL_POINT)
          * InpAtrMultiplier * InpAtrLoosenFactor));
   // … pivot checks follow …
}

Wenn die Tiefe definiert ist, führen wir zwei einfache Schleifen durch, um die Umkehrpunkte zu überprüfen. IsSwingHigh(1, depth) prüft, ob kein Balken innerhalb der Tiefe der Balken auf beiden Seiten das Kandidatenhoch überschreitet; IsSwingLow tut das Gegenteil für die Tiefs. Wenn wir ein neues Hoch oder Tief finden, verschieben wir lastHigh in prevHigh (und dasselbe für Tiefststände) und zeichnen den Zeitstempel auf. Die Verfolgung des vorherigen und des aktuellen Umkehrpunktes ermöglicht uns den nächsten Vergleich.

bool newHigh = IsSwingHigh(1, depth);
bool newLow  = IsSwingLow (1, depth);
double h = iHigh(_Symbol,_Period,1), l = iLow(_Symbol,_Period,1);

if(newHigh)
{
   prevHigh     = lastHigh;
   prevHighTime = lastHighTime;
   lastHigh     = h;
   lastHighTime = thisBar;
}
if(newLow)
{
   prevLow      = lastLow;
   prevLowTime  = lastLowTime;
   lastLow      = l;
   lastLowTime  = thisBar;
}

Sobald wir unsere Umkehrpunkte haben, aktualisieren wir structState, um die Trendausrichtung widerzuspiegeln: ein höheres Hoch setzt den Zustand 1 (Abwärts-Umkehr als Nächstes möglich), ein niedrigeres Tief setzt den Zustand 2 (Aufwärts-Umkehr als Nächstes möglich). Dann prüfen wir, ob es sich um eine tatsächliche Umkehr handelt: Wenn im Zustand 1 das neue Hoch unter dem vorherigen Hoch liegt, handelt es sich um eine Abwärts-Umkehr; im Zustand 2, wenn das neue Tief über das vorherige Tief steigt, handelt es sich um eine Aufwärts-Umkehr. Wenn wir auf eine Umkehr treffen, werden unsere Plot- und Benachrichtigungsaufrufe ausgelöst und unsere Zähler angestoßen.

// Update bias
if(newHigh && prevHigh>0 && lastHigh > prevHigh) structState = 1;
if(newLow  && prevLow>0  && lastLow  < prevLow) structState = 2;

// Bearish flip
if(newHigh && structState==1 && lastHigh < prevHigh)
{
   PlotArrow(...);  PlotLabel(...);  Notify(...);
   if(countBear>0) sumBearInterval += (lastHighTime - prevLowTime)/60.0;
   countBear++;
}

// Bullish flip
if(newLow && structState==2 && lastLow > prevLow)
{
   PlotArrow(...);  PlotLabel(...);  Notify(...);
   if(countBull>0) sumBullInterval += (lastLowTime - prevHighTime)/60.0;
   countBull++;
}

Wir kapseln alle Zeichnungen auf dem Chart in zwei Hilfsfunktionen - PlotArrow und PlotLabel - um sowohl Effizienz als auch Übersichtlichkeit zu gewährleisten. Innerhalb jeder Funktion rufen wir zunächst ObjectFind(0, name) auf, das nach einem vorhandenen Chartobjekt anhand seines eindeutigen Namens sucht; dieser Vorgang benötigt O(n)-Zeit im Verhältnis zur Anzahl der Objekte ab, ist aber auf modernen Rechnern schnell genug für gelegentliche Überprüfungen pro Balken. Wenn das Objekt noch nicht existiert (ObjectFind gibt -1 zurück), wird es genau einmal mit ObjectCreate erstellt, wobei der entsprechende Objekttyp gewählt wird (ein Pfeil für PlotArrow, ein Textlabel für PlotLabel).

Anschließend passen wir die Eigenschaften an: Für Pfeile setzen wir OBJPROP_ARROWCODE, um die gewünschte Glyphe auszuwählen (z. B. Wingdings-Code 234 für einen roten Pfeil nach unten), und OBJPROP_COLOR, um den Farbton festzulegen; für Beschriftungen setzen wir OBJPROP_TEXT mit unserer Beschriftung (z. B. „LH“ oder „HL“) sowie Versatz und Schriftgröße. Indem wir wiederholte Aufrufe von ObjectCreate vermeiden, verhindern wir Leistungseinbußen und Speicherüberlastung, die auftreten würden, wenn sich im Laufe der Zeit Hunderte oder Tausende von identischen Objekten ansammeln würden. Dieses Muster stellt auch sicher, dass jede Pivot-Marke einen stabilen, vorhersehbaren Bezeichner hat. Wenn Sie also später ihre OBJPROP_ZORDER (Zeichnungspriorität) anpassen oder sie unter bestimmten Bedingungen löschen möchten, können Sie sich mit absoluter Sicherheit auf den Namen beziehen, ohne versehentlich andere Chartelemente zu beeinflussen.

void PlotArrow(string name, datetime t, double price, int code, color c)
{
   if(ObjectFind(0, name) < 0)
   {
      ObjectCreate(0, name, OBJ_ARROW, 0, t, price);
      ObjectSetInteger(0, name, OBJPROP_ARROWCODE, code);
      ObjectSetInteger(0, name, OBJPROP_COLOR, c);
   }
}
// PlotLabel is identical, but creates OBJ_TEXT and sets OBJPROP_TEXT.

Nach jedem Balken wird die Beschriftung panelName neu erstellt und angezeigt:

  • Die aktuelle Tiefe der Umkehrpunkte
  • Gesamtzahl aller hohen und tiefen Umkehrpunkte
  • Durchschnittliche Zeit (in Minuten) zwischen zwei Umkehrpunkten (sobald wir mindestens zwei haben).

So erhalten Sie eine sofortige Rückmeldung darüber, wie oft Strukturbrüche unter den von Ihnen gewählten ATR-Einstellungen auftreten.

string txt = StringFormat("Depth: %d\nBull Flips: %d\nBear Flips: %d",
                          depth, countBull, countBear);
if(countBull>1)
   txt += "\nAvg HL Int: " + DoubleToString(sumBullInterval/(countBull-1),1) + "m";
if(countBear>1)
   txt += "\nAvg LH Int: " + DoubleToString(sumBearInterval/(countBear-1),1) + "m";

ObjectSetString(0, panelName, OBJPROP_TEXT, txt);

Schließlich fasst unsere Funktion Notify(msg) alle Warnmethoden an einer Stelle zusammen. Wir rufen immer Alert(msg) für ein MetaTrader 5-Popup auf und spielen dann optional einen Ton ab (PlaySound) oder senden einen Push (SendNotification), je nach Ihren Eingaben. Durch die Zentralisierung ist es ganz einfach, später E-Mail- oder Webhook-Warnungen hinzuzufügen.

void Notify(string msg)
{
   Alert(msg);
   if(InpEnableSound) PlaySound(InpSoundFile);
   if(InpEnablePush)  SendNotification(msg);
}


Quellcode-Auflistung

//+------------------------------------------------------------------+
//|                                 Market Structure Flip Detector EA|
//|                                   Copyright 2025, MetaQuotes Ltd.|
//|                           https://www.mql5.com/en/users/lynnchris|
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, MetaQuotes Ltd."
#property link      "https://www.mql5.com/en/users/lynnchris"
#property version   "1.0"
#property strict

//--- user inputs
input int    InpAtrPeriod       = 14;      // ATR lookback
input double InpAtrMultiplier   = 1.0;     // ATR to swing depth factor (lower = looser)
input double InpAtrLoosenFactor = 0.5;     // Loosen factor for ATR confirmation (0-1)
input bool   InpAutoShift       = true;    // Auto-enable chart right shift
input int    InpShiftBars       = 5;       // Bars for right margin
input bool   InpEnableSound     = true;
input string InpSoundFile       = "alert.wav";
input bool   InpEnablePush      = false;

//--- global vars
string   panelName = "FlipPanel";
int      atrHandle;
int      structState = 0;
double   prevHigh=0, lastHigh=0;
datetime prevHighTime=0, lastHighTime=0;
double   prevLow=0, lastLow=0;
datetime prevLowTime=0, lastLowTime=0;
int      countBull=0, countBear=0;
double   sumBullInterval=0, sumBearInterval=0;

//+------------------------------------------------------------------+
int OnInit()
  {
   if(InpAutoShift)
      ChartSetInteger(0, CHART_SHIFT, InpShiftBars);

   atrHandle = iATR(_Symbol, _Period, InpAtrPeriod);
   if(atrHandle == INVALID_HANDLE)
      return(INIT_FAILED);

   ObjectCreate(0, panelName, OBJ_LABEL, 0, 0, 0);
   ObjectSetInteger(0, panelName, OBJPROP_CORNER,    CORNER_LEFT_UPPER);
   ObjectSetInteger(0, panelName, OBJPROP_XDISTANCE, 10);
   ObjectSetInteger(0, panelName, OBJPROP_YDISTANCE, 10);
   ObjectSetInteger(0, panelName, OBJPROP_FONTSIZE,  10);
   ObjectSetInteger(0, panelName, OBJPROP_COLOR,     clrYellow);
   ObjectSetInteger(0, panelName, OBJPROP_BACK,      false);
   ObjectSetInteger(0, panelName, OBJPROP_ZORDER,    1);

   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   ObjectsDeleteAll(0, -1, OBJ_ARROW);
   ObjectsDeleteAll(0, -1, OBJ_TEXT);
   ObjectDelete(0, panelName);
   if(atrHandle != INVALID_HANDLE)
      IndicatorRelease(atrHandle);
  }

//+------------------------------------------------------------------+
void OnTick()
  {
   static datetime lastBar=0;
   datetime thisBar = iTime(_Symbol,_Period,1);
   if(thisBar==lastBar)
      return;
   lastBar=thisBar;

   double atrBuf[];
   if(CopyBuffer(atrHandle,0,1,1,atrBuf)<=0)
      return;
   double atr = atrBuf[0];

// loosen ATR confirmation by InpAtrLoosenFactor (0-1)
   double rawDepth = atr/SymbolInfoDouble(_Symbol,SYMBOL_POINT)*InpAtrMultiplier;
   int depth = MathMax(1, (int)(rawDepth * InpAtrLoosenFactor));

   bool newHigh=false,newLow=false;
   double h=iHigh(_Symbol,_Period,1), l=iLow(_Symbol,_Period,1);

   if(IsSwingHigh(1,depth))
     {
      prevHigh = lastHigh;
      prevHighTime = lastHighTime;
      lastHigh = h;
      lastHighTime = thisBar;
      newHigh = true;
     }
   if(IsSwingLow(1,depth))
     {
      prevLow = lastLow;
      prevLowTime = lastLowTime;
      lastLow = l;
      lastLowTime = thisBar;
      newLow = true;
     }

   double off = SymbolInfoDouble(_Symbol,SYMBOL_POINT)*10;

// Bearish Flip: Lower High after a Higher High
   if(newHigh && structState==1 && prevHigh>0 && lastHigh<prevHigh)
     {
      // signal arrow and label at current LH
      PlotArrow("Bear_"+IntegerToString((int)lastHighTime), lastHighTime, lastHigh, 234, clrRed);
      PlotLabel("LH_"+IntegerToString((int)lastHighTime), lastHighTime, lastHigh+off, "LH", clrRed);
      // label the previous LH used for comparison
      PlotLabel("PrevLH_"+IntegerToString((int)prevHighTime), prevHighTime, prevHigh+off, "LH_prev", clrRed);
      Notify("Bearish Flip (LH) at "+TimeToString(lastHighTime,TIME_DATE|TIME_MINUTES));
      if(countBear>0)
         sumBearInterval += (lastHighTime-prevLowTime)/60.0;
      countBear++;
     }

// Bullish Flip: Higher Low after a Lower Low
   if(newLow && structState==2 && prevLow>0 && lastLow>prevLow)
     {
      // signal arrow and label at current HL
      PlotArrow("Bull_"+IntegerToString((int)lastLowTime), lastLowTime, lastLow, 233, clrLime);
      PlotLabel("HL_"+IntegerToString((int)lastLowTime), lastLowTime, lastLow-off, "HL", clrLime);
      // label the previous HL used for comparison
      PlotLabel("PrevHL_"+IntegerToString((int)prevLowTime), prevLowTime, prevLow-off, "HL_prev", clrLime);
      Notify("Bullish Flip (HL) at "+TimeToString(lastLowTime,TIME_DATE|TIME_MINUTES));
      if(countBull>0)
         sumBullInterval += (lastLowTime-prevHighTime)/60.0;
      countBull++;
     }

// update structure state
   if(newHigh && prevHigh>0 && lastHigh>prevHigh)
      structState = 1;
   if(newLow  && prevLow>0  && lastLow <prevLow)
      structState = 2;

// update panel stats
   string txt = "Depth: "+IntegerToString(depth)+"\n";
   txt += "Bull Flips: "+IntegerToString(countBull)+"\n";
   txt += "Bear Flips: "+IntegerToString(countBear);
   if(countBull>1)
      txt += "\nAvg HL Int: "+DoubleToString(sumBullInterval/(countBull-1),1)+"m";
   if(countBear>1)
      txt += "\nAvg LH Int: "+DoubleToString(sumBearInterval/(countBear-1),1)+"m";
   ObjectSetString(0, panelName, OBJPROP_TEXT, txt);
  }

//+------------------------------------------------------------------+
bool IsSwingHigh(int shift,int depth)
  {
   double p = iHigh(_Symbol,_Period,shift);
   for(int i=shift-depth; i<=shift+depth; i++)
      if(i>=0 && iHigh(_Symbol,_Period,i) > p)
         return false;
   return true;
  }

//+------------------------------------------------------------------+
bool IsSwingLow(int shift,int depth)
  {
   double p = iLow(_Symbol,_Period,shift);
   for(int i=shift-depth; i<=shift+depth; i++)
      if(i>=0 && iLow(_Symbol,_Period,i) < p)
         return false;
   return true;
  }

//+------------------------------------------------------------------+
void PlotArrow(string nm,datetime t,double price,int code,color c)
  {
   if(ObjectFind(0,nm) < 0)
     {
      ObjectCreate(0, nm, OBJ_ARROW, 0, t, price);
      ObjectSetInteger(0, nm, OBJPROP_ARROWCODE, code);
      ObjectSetInteger(0, nm, OBJPROP_COLOR, c);
      ObjectSetInteger(0, nm, OBJPROP_WIDTH, 2);
     }
  }

//+------------------------------------------------------------------+
void PlotLabel(string nm,datetime t,double price,string txt,color c)
  {
   if(ObjectFind(0,nm) < 0)
     {
      ObjectCreate(0, nm, OBJ_TEXT, 0, t, price);
      ObjectSetString(0, nm, OBJPROP_TEXT, txt);
      ObjectSetInteger(0, nm, OBJPROP_COLOR, c);
      ObjectSetInteger(0, nm, OBJPROP_FONTSIZE, 10);
     }
  }

//+------------------------------------------------------------------+
void Notify(string msg)
  {
   Alert(msg);
   if(InpEnableSound)
      PlaySound(InpSoundFile);
   if(InpEnablePush)
      SendNotification(msg);
  }
//+------------------------------------------------------------------+


Leistungsergebnisse

Im Folgenden werde ich die Ergebnisse unserer Tests sowohl unter Live-Marktbedingungen als auch bei den Backtests erläutern.

Live-Markt

Live Marktergebnisse

Im obigen Chart erkennt der EA zunächst einen hohen Umkehrpunkt mit der Bezeichnung „LH_prev“, das zwei aufeinanderfolgende höhere Hochs widerspiegelt und eine Aufwärtsstruktur etabliert. Einige Balken später wird ein weiterer hoher Umkehrpunkt festgestellt, der das vorherige Hoch nicht übertrifft - dieses niedrigere Hoch innerhalb eines Aufwärtstrends veranlasst den EA, einen roten Pfeil und die Bezeichnung „LH“ an diesem Balken zu zeichnen. Dieses Abwärts-Signal zeigt den Zusammenbruch des Aufwärts-Momentums an und warnt davor, dass eine Abwärtsbewegung einsetzen könnte.

Gif von Live-Markt

Nachfolgend sehen Sie ein GIF, das die Leistung des EA für EURUSD zeigt. Wenn jede Ein-Minuten-Kerze schließt, verfolgt der EA die aufeinanderfolgenden Tiefs, bis er ein tiefen Umkehrpunkt findet, der höher als das vorherige Tief ist. Wenn dieses höhere Tief auftaucht, wird ein grüner HL-Pfeil angezeigt, der den Aufwärtstrend anzeigt. Im gleichen Moment wird die Kopfzeile aktualisiert - hier werden „12 BullFlips: 1 BearFlip: 2Avg Int: 108.0m“ angezeigt - um die aktualisierten Zählungen wiederzugeben. Dieser Clip veranschaulicht deutlich den Übergang von einer Abwärtsstruktur zu einer potenziellen Aufwärtsbewegung.

Live Marktergebnisse

Backtests

Nachstehend finden Sie eine Tabelle mit den Ergebnissen der Stufenindexanalyse über mehrere Zeitrahmen hinweg. „Positive Signale“ sind solche, nach denen sich der Markt über einen längeren Zeitraum in die angegebene Richtung bewegt hat.

5-Minuten-Zeitrahmen

Signalart Signale insgesamt Positive Signale Gewinnrate
Verkäufe (sell) 56 39 70%
Käufe (buy) 53 44 83%

15-Minuten-Zeitrahmen

Signalart Signale insgesamt Positive Signale Gewinnrate
Verkäufe (sell) 7 5 71%
Käufe (buy) 14 9 64%

Die Zusammenfassung der Analysen zeigt, dass der Market Structure Flip Detector durchweg profitable Signale erzeugt, insbesondere in kürzeren Zeitrahmen. Die Erfolgsquote bei den Verkaufsvorbereitungen liegt bei 70 % und mehr, was die Wirksamkeit des Tools unterstreicht. Diese Errungenschaft stellt einen bedeutenden Fortschritt in der Automatisierung der Preisaktionsanalyse dar und bringt uns einem vollständig systematischen Handels-Toolkit mit geringer Latenzzeit näher.



Schlussfolgerung

Nachdem wir dieses Tool sowohl unter Live-Marktbedingungen als auch im Rahmen von Backtests entwickelt und getestet haben, zeigt unsere Analyse, dass es durchweg eine starke Leistung erbringt, insbesondere beim Scalping auf niedrigeren Zeitskalen, wo es beträchtliche Erträge erzielt. Es ist jedoch unerlässlich, Vorsicht walten zu lassen und die Signale mit zusätzlichen Bestätigungsmethoden zu validieren, bevor man einen Handel tätigt. Darüber hinaus ist das Testen des Tools mit verschiedenen Währungspaaren entscheidend, um herauszufinden, wo es sich am besten bewährt. Sie können die Eingabeparameter auch während des Tests anpassen, um die Leistung weiter zu optimieren.

Datum Name des Tools  Beschreibung Version  Aktualisierungen  Hinweis
01/10/24 Chart Projector Skript zur Überlagerung der Kursentwicklung des Vortages mit einem Ghost-Effekt. 1.0 Erste Veröffentlichung Tool Nummer 1
18/11/24 Analytical Comment Er liefert Informationen zum Vortag in Tabellenform und nimmt die zukünftige Marktentwicklung vorweg. 1.0 Erste Veröffentlichung Tool Nummer 2
27/11/24 Analytics Master Reguläre Aktualisierung der Marktmetriken alle zwei Stunden.  1.01 Zweite Veröffentlichung Tool Nummer 3
02/12/24 Analytics Forecaster  Reguläre Aktualisierung der Marktmetriken alle zwei Stunden mit Telegram-Integration. 1.1 Dritte Ausgabe Tool Nummer 4
09/12/24 Volatility Navigator Der EA analysiert die Marktbedingungen anhand der Indikatoren Bollinger Bands, RSI und ATR. 1.0 Erste Veröffentlichung Tool Nummer 5
19/12/24 Mean Reversion Signal Reaper Analysiert den Markt anhand der Strategie „Umkehr zur Mitte“ und liefert Signale.  1.0  Erste Veröffentlichung  Tool Nummer 6 
9/01/25  Signal Pulse  Analysator für mehrere Zeitrahmen. 1.0  Erste Veröffentlichung  Tool Nummer 7 
17/01/25  Metrics Board  Bedienfeld mit Taste für die Analyse.  1.0  Erste Veröffentlichung Tool Nummer 8 
21/01/25 External Flow Analytik durch externe Bibliotheken. 1.0  Erste Veröffentlichung Tool Nummer 9 
27/01/25 VWAP Volumengewichteter Durchschnittspreis   1.3  Erste Veröffentlichung  Tool Nummer 10 
02/02/25  Heikin Ashi  Trendglättung und Identifizierung von Umkehrsignalen  1.0  Erste Veröffentlichung  Tool Nummer 11
04/02/25  FibVWAP  Signalerzeugung durch Python-Analyse  1.0  Erste Veröffentlichung  Tool Nummer 12
14/02/25  RSI DIVERGENCE  Kursentwicklung versus RSI-Divergenzen  1.0  Erste Veröffentlichung  Tool Nummer 13 
17/02/25  Parabolic Stop and Reverse (PSAR)  Automatisierung der PSAR-Strategie 1.0 Erste Veröffentlichung  Tool Nummer 14
20/02/25  Quarters Drawer Script Einzeichnen der Ebenen der Viertel auf dem Chart  1.0  Erste Veröffentlichung  Tool Nummer 15 
27/02/25  Intrusion Detector Erkennen und warnen, wenn der Preis ein Viertel-Niveau erreicht 1.0   Erste Veröffentlichung Tool Nummer 16 
27/02/25  TrendLoom Tool  Analysepanel für mehrere Zeitrahmen 1.0 Erste Veröffentlichung Tool Nummer 17
11/03/25  Quarters Board  Bedienfeld mit Tasten zum Aktivieren oder Deaktivieren der Viertel-Ebenen  1.0  Erste Veröffentlichung Tool Nummer 18
26/03/25  ZigZag Analyzer  Zeichnen von Trendlinien mit dem ZigZag-Indikator  1.0  Erste Veröffentlichung  Tool Nummer 19 
10/04/25  Correlation Pathfinder Plotten von Währungskorrelationen mit Python-Bibliotheken. 1.0 Erste Veröffentlichung  Tool Nummer 20 
 23/04/25  Market Structure Flip Detector Tool Erkennung von Marktstrukturschwankungen 1.0  Erste Veröffentlichung  Tool Nummer 21

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

Beigefügte Dateien |
Flip_Detector.mq5 (14.5 KB)
Letzte Kommentare | Zur Diskussion im Händlerforum (1)
Alexander Piechotta
Alexander Piechotta | 7 Mai 2025 in 11:08
bool IsSwingHigh( int shift, int depth)
  {
   double p = iHigh ( _Symbol , _Period ,shift);
   for ( int i= shift-depth; i<=shift+depth; i++)
       if (i>= 0 && iHigh ( _Symbol , _Period ,i) > p)
         return false ;
   return true ;
  }

Hallo, ich verstehe nicht, warum du int i=shift-depth schreibst, könntest du nicht einfach int i=0 verwenden ?

Können Sie mir das bitte erklären? Danke!

Formulierung eines dynamischen Multi-Pair EA (Teil 2): Portfolio-Diversifizierung und -Optimierung Formulierung eines dynamischen Multi-Pair EA (Teil 2): Portfolio-Diversifizierung und -Optimierung
Portfolio-Diversifizierung und -Optimierung sorgt für eine strategische Streuung der Anlagen auf mehrere Vermögenswerte, um das Risiko zu minimieren und gleichzeitig die ideale Mischung von Vermögenswerten auszuwählen, um die Renditen auf der Grundlage risikobereinigter Performance-Kennzahlen zu maximieren.
Klassische Strategien neu interpretieren (Teil 14): Hochwahrscheinliche Setups Klassische Strategien neu interpretieren (Teil 14): Hochwahrscheinliche Setups
Hochwahrscheinliche Setups sind in unserer Trading-Community gut bekannt, aber leider sind sie nicht gut definiert. In diesem Artikel wollen wir einen empirischen und algorithmischen Weg finden, um genau zu definieren, was ein Hochwahrscheinlichkeits-Setup ist, und um diese zu identifizieren und auszunutzen. Durch die Verwendung von Gradient Boosting Trees haben wir gezeigt, wie der Leser die Leistung einer beliebigen Handelsstrategie verbessern und unserem Computer die genaue Aufgabe auf sinnvollere und explizitere Weise mitteilen kann.
Vom Neuling zum Experten: Programmieren von Kerzen Vom Neuling zum Experten: Programmieren von Kerzen
In diesem Artikel machen wir den ersten Schritt in die MQL5-Programmierung, auch für absolute Anfänger. Wir zeigen Ihnen, wie Sie bekannte Kerzenmuster in einen voll funktionsfähigen nutzerdefinierten Indikator verwandeln können. Kerzenmuster sind wertvoll, da sie reale Kursbewegungen widerspiegeln und Marktverschiebungen signalisieren. Anstatt die Charts manuell zu scannen - ein Ansatz, der fehleranfällig und ineffizient ist - werden wir besprechen, wie Sie den Prozess mit einem Indikator automatisieren können, der Muster für Sie identifiziert und kennzeichnet. Auf dem Weg dorthin werden wir uns mit Schlüsselkonzepten wie Indexierung, Zeitreihen, Average True Range (für Genauigkeit bei schwankender Marktvolatilität) und der Entwicklung einer nutzerdefinierten, wiederverwendbaren Bibliothek von Kerzen-Mustern für den Einsatz in zukünftigen Projekten beschäftigen.
Manuelle Backtest leicht gemacht: Aufbau eines nutzerdefinierten Toolkits für Strategietester in MQL5 Manuelle Backtest leicht gemacht: Aufbau eines nutzerdefinierten Toolkits für Strategietester in MQL5
In diesem Artikel entwickeln wir ein nutzerdefiniertes MQL5-Toolkit für einfache manuelle Backtests im Strategy Tester. Wir erläutern den Aufbau und die Umsetzung des Systems und konzentrieren uns dabei auf interaktive Handelskontrollen. Wir zeigen dann, wie man damit Strategien effektiv testen kann