Entwicklung des Price Action Analysis Toolkit (Teil 39): Automatisierung der BOS- und ChoCH-Erkennung in MQL5
Einführung
Willkommen zu Teil 39 der Serie Price Action Analysis Toolkit Development. In dieser Folge bauen wir ein praktisches MQL5-System auf, das Händlern hilft, das Chart mit Klarheit zu lesen, anstatt auf Kerzen zu starren und Strukturverschiebungen zu verpassen. Das Ziel ist einfach: Fraktale Umkehrpunkt (pivots) und Strukturregeln in zuverlässige, nicht nachmalende Signale zu verwandeln, auf die Sie sich sowohl im Live-Handel als auch bei historischen Tests verlassen können.
Wir verwenden fraktale Umkehrpunkte als zuverlässige lokale Ankerpunkte und erkennen zwei komplementäre Signale: ChoCH (Change of Character), das anzeigt, wenn der Markt seine bisherigen Trend verliert – zum Beispiel, wenn ein Aufwärtstrend kein höheres Hoch oder ein Abwärtstrend kein niedrigeres Tief erreicht – und BOS (Break of Structure), das bestätigt, dass sich der Trend verschoben hat, wenn der Preis entscheidend über einem vorherigen Hoch oder Tief schließt. Betrachten Sie ChoCH als eine Frühwarnung und BOS als Bestätigung.

Die Kombination von Fraktalen mit ChoCH/BOS bietet sauberere, nicht übermalende Anker für die Analyse, eine frühere Warnung vor potenziellen Umschwüngen und eine Klarheit über mehrere Zeitrahmen, die hilft, das Rauschen auf niedrigeren Charts zu filtern. Dieselben Regeln lassen sich problemlos automatisieren, protokollieren und Backtests unterziehen, was sie ideal für einen EA macht.
In diesem Artikel werden das Design des Algorithmus und die vollständige MQL5-Implementierung erläutert: Scannen von Fraktalen mit geschlossenem Balken, speichersichere Fraktalspeicherung, sicheres Zeichnen von persistenten Objekten und Ereignisbehandlung, die jedes bestätigte BOS/ChoCH (Desktop, Mobile und Sound) protokolliert und meldet. Am Ende haben Sie einen funktionierenden Detektor, den Sie kompilieren, testen und einsetzen können.
Wir beginnen mit der Strategielogik, gehen dann zur MQL5-Implementierung, zu Warnungen, Protokollierungs- und Benachrichtigungsoptionen, Tests und Ergebnissen über und schließen mit einem Fazit. Siehe das nachstehende Inhaltsverzeichnis.
- Einführung
- Strategische Logik
- MQL5-Implementierung
- Optionen für Alarme, Protokollierung und Benachrichtigung
- Tests und Ergebnisse
- Schlussfolgerung
Strategische Logik
In diesem Abschnitt werden wir die grundlegende strategische Logik hinter dem System, das wir aufbauen, erläutern. Im Mittelpunkt dieser Technik stehen der fraktale Indikator und die daraus abgeleiteten Struktursignale. Falls Sie es verpasst haben, haben wir in einem früheren Artikel über eine Fraktal-Breakout-Trendstrategie berichtet – Fraktale sind vielseitig: Über Breakouts hinaus bieten sie zuverlässige Ankerpunkte, die viele Arten von Preisaktionsanalysen unterstützen. Hier verwenden wir sie, um Change of Character (ChoCH) und Break of Structure (BOS) zu erkennen.
Ein fraktaler Pivot ist ein lokaler Umkehrpunkt, der entsteht, wenn das Hoch (oder Tief) eines mittleren Balkens höher (oder niedriger) ist als eine symmetrische Anzahl von benachbarten Balken. Bei einer Fensterlänge, die als g_length = 2*p + 1 definiert ist, ist der zentrale Balken ein hohes Fraktal, wenn sein Hoch größer oder gleich jedem Hoch im Fenster ist, und ein tiefes Fraktal, wenn sein Tief kleiner oder gleich jedem Tief im Fenster ist. Fraktale erzeugen konsistente, nicht übermalende Anker, da die Erkennung das gesamte Fenster der bestätigenden Balken erfordert.
Ein Break of Structure (BOS) ist eine entscheidende Verletzung der aktuellen Marktstruktur: Der Kurs schließt über einem vorherigen hohen Umkehrpunkt (ein Aufwärts-BOS) oder unter einem vorherigen tiefen Umkehrpunkt (ein Abwärts-BOS). Ein BOS signalisiert, dass sich das Momentum und die kurz- bis mittelfristige Markttendenz in Richtung des Durchbruchs verschoben haben – es ist die Bestätigung, die Händler in der Regel nutzen, um sich auf die neue Richtung festzulegen.
Ein Change of Character (ChoCH) ist ein früheres, schwächeres Signal dafür, dass sich die Ausrichtung des Marktes ändert. Typische Beispiele sind das Ausbleiben eines höheren Hochs während eines Aufwärtstrends oder das Ausbleiben eines niedrigeren Tiefs während eines Abwärtstrends. ChoCH sollte als Warnung betrachtet werden: Sie geht oft einem BOS voraus, wenn die nachfolgende Kursbewegung an Überzeugungskraft gewinnt, und gibt Ihnen die Möglichkeit, sich vorzubereiten (Stopps nachziehen, das Engagement reduzieren oder nach Umkehreinstiegen suchen).
Entscheidend ist, dass alle Signale nur mit geschlossenen Balken ausgewertet werden, um ein sich wiederholendes Neuzeichnen zu vermeiden. In der Praxis bedeutet das, dass wir den Schlusskurs des abgeschlossene Balken (z.B. prevClose vs. curClose) mit gespeicherten Fraktal-Ebenen vergleichen; wir deklarieren niemals einen BOS oder ChoCH auf einem unbestätigten, sich noch bildenden Balken. Die Verwendung der Logik der Schlusskurse abgeschlossener Balken gewährleistet, dass die Ereignisse reproduzierbar und in Backtests verwendet werden können.
Der Algorithmus ist absichtlich einfach und deterministisch: Er erkennt zuverlässige, fraktale Anker auf geschlossenen Balken, achtet auf Kreuzungen dieser Anker, markiert und zeichnet jeden Bruch nur einmal und gibt ein einziges Protokoll/eine einzige Meldung pro bestätigtes Ereignis aus. Dieser Ansatz mit geschlossenem Balken vermeidet ein Repaiting und erzeugt reproduzierbare, backtestbare Signale.- Fluss auf hoher Ebene

- Warten auf einen neuen geschlossenen Balken (verwenden Sie den Zeitstempel des geschlossenen Balkens als Auslöser).
- Erkenne ein Fraktal in der Mitte eines symmetrischen Fensters der Länge g_length = 2*p + 1.
- Wenn gefunden, speicher das Fraktal (Zeit, Preis) und setze marked = false.
- Vergleiche bei jedem neuen geschlossenen Balken prevClose und curClose mit jedem gespeicherten Fraktalpreis, um Kreuzungen zu erkennen.
- Wenn eine Kreuzung auftritt (Bruch eines geschlossenen Balkens), markiere dieses Fraktal, zeichnen eine horizontale Linie/Trendlinie und ein verankertes Kennzeichen und gib ein Protokoll/eine Warnung aus.
- Entferne regelmäßig alte Fraktale, damit die Felder begrenzt bleiben.
Pseudocode des Kern (kompakt):
if new_closed_bar(): ScanForFractals() // detect & append new fractal anchors PruneFractals(maxKeepBars) // remove very old anchors for each fractal in stored_fractals: if not fractal.marked and crossed(prevClose, curClose, fractal.price): DrawBreak(fractal) LabelAndLog(fractal) fractal.marked = true
MQL5-Implementierung
Header und Metadaten
Am Anfang der Datei werden in kurzen Kommentaren wichtige Metadaten wie Dateiname, Autor, Copyright und Verweislinks angegeben. Diese Angaben sind wichtig für die Versionskontrolle und für die spätere Bezugnahme bei der Weitergabe oder Wiederverwendung der Datei. Die #property-Direktiven bestimmen das Kompilierungsverhalten; insbesondere #property strict erzwingt strengere Typ- und API-Prüfungen, was dazu beiträgt, subtile Fehler in der Entwicklung frühzeitig zu erkennen. Die Direktive #include <stdlib.mqh> bindet die Standard-Hilfsbibliothek ein, die häufige Programmieraufgaben vereinfacht und die Hauptcodebasis sauberer und besser wartbar hält. Wenn Leser diese Bibliothek nicht zur Verfügung haben, sollten sie sie entweder bereitstellen oder das Include entfernen, um Kompilierungsfehler zu vermeiden.
//+------------------------------------------------------------------+ //| Fractal Reaction System.mq5| //| Copyright 2025, Christian Benjamin.| //| https://www.mql5.com/en/users/lynnchris| //+------------------------------------------------------------------+ #property copyright "Copyright 2025, Christian Benjamin." #property link "https://www.mql5.com/en/users/lynnchris" #property version "1.0" #property strict #include <stdlib.mqh>
Nutzereingaben
Der Eingabeblock stellt Konfigurationsparameter zur Verfügung, die das Verhalten des EAs steuern, ohne dass eine Neukompilierung erforderlich ist. AutoDetectLength ermöglicht die automatische Auswahl einer geeigneten fraktalen Länge entsprechend dem Zeitrahmen des Charts, während LengthInput die manuelle Eingabe ermöglicht. Beachten Sie, dass die Erkennung von Fraktalen eine ungerade Fenstergröße (z. B. 3, 5 oder 7) erfordert, um einen einzelnen mittleren Balken mit symmetrischen Nachbarn zu gewährleisten. Anzeigeoptionen wie ShowBull und ShowBear sowie Farbeinstellungen (BullColor, BearColor) verbessern die visuelle Klarheit und beschleunigen die Interpretation über mehrere Charts hinweg.
Die Parameter HorizontalRightBars und HorizontalLeftExtend legen fest, wie die Trendlinien visuell nach rechts oder links erweitert werden, um die Interpretation der Relevanz der Pegel zu erleichtern. DebugMode ermöglicht die Diagnoseprotokollierung für Entwicklung und Tests, und MaxFractalHistoryBars begrenzt, wie viele historische Fraktale aufbewahrt werden, um ein unbegrenztes Speicherwachstum bei längerem Betrieb zu verhindern.
// User-configurable inputs input bool AutoDetectLength = false; input int LengthInput = 5; input bool ShowBull = true; input color BullColor = clrLime; input bool ShowBear = true; input color BearColor = clrRed; input int HorizontalRightBars = 0; input int HorizontalLeftExtend = 3; input bool DebugMode = false; input int MaxFractalHistoryBars = 2000;
Globale Variablen und Datenstrukturen
Die globalen Variablen verwalten den Laufzeitstatus und die dauerhafte Speicherung. g_chart_id speichert den Chart-Identifikator, sodass alle grafischen Objekte explizit mit dem richtigen Chart verknüpft sind. g_length und p_half stehen für die Länge des Fraktalfensters bzw. seine halbe Größe; diese Werte werden einmal bei der Initialisierung berechnet und dann wiederverwendet. ea_digits, ea_point und ea_point_pips normalisieren die Preispräzision bei verschiedenen Brokern und Symbolen und sorgen für konsistente Offsets und die Platzierung von Kennzeichnungen. Das System speichert hohe und tiefe Fraktale in äquivalenten Arrays (*_time[], *_price[], *_marked[]), wobei Zeitstempel und Preis jedes Fraktal identifizieren und die Markierung eine doppelte Verarbeitung verhindert. os_state schließlich kapselt die Marktneigung des Systems: 0 für neutral, 1 für steigend und -1 für fallend.
// Internal globals long g_chart_id; int g_length; int p_half; int ea_digits; double ea_point, ea_point_pips; datetime bull_time[]; double bull_price[]; bool bull_marked[]; datetime bear_time[]; double bear_price[]; bool bear_marked[]; int os_state = 0; // 0: none, 1: bullish, -1: bearish
Initialisierung (OnInit)
OnInit() führt die Initialisierung und Eingabesanitisierung durch. Die Routine erhält die Chart-ID, bestimmt die fraktale Länge (entweder automatisch erkannt oder vom Nutzer angegeben), erzwingt einen Mindestwert und stellt sicher, dass die Länge ungerade ist. Sie berechnet p_half und liest die Symbolgenauigkeit, um die Punkt- und Pip-Größen für eine genaue Darstellung zu berechnen. Die fraktalen Arrays werden auf einen leeren Zustand zurückgesetzt. Wenn der DebugMode aktiviert ist, gibt die Funktion eine Initialisierungsnachricht aus, die Schlüsselwerte enthält, um die korrekte Konfiguration zu bestätigen, bevor die Ausführung fortgesetzt wird.
int OnInit() { g_chart_id = ChartID(); // Determine fractal length if(AutoDetectLength) { if(_Period <= PERIOD_H1) g_length = 5; else if(_Period <= PERIOD_H4) g_length = 7; else if(_Period <= PERIOD_D1) g_length = 9; else g_length = 11; } else { g_length = LengthInput; } // Ensure odd length >=3 if(g_length < 3) g_length = 5; if((g_length % 2) == 0) g_length++; p_half = g_length / 2; // Get symbol info ea_digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS); ea_point = Point(); ea_point_pips = ea_point; if(ea_digits == 3 || ea_digits == 5) ea_point_pips = Point() * 10.0; // Clear fractal arrays ArrayResize(bull_time,0); ArrayResize(bull_price,0); ArrayResize(bull_marked,0); ArrayResize(bear_time,0); ArrayResize(bear_price,0); ArrayResize(bear_marked,0); if(DebugMode) PrintFormat("EA INIT: AutoDetect=%s LengthInput=%d g_length=%d p_half=%d chart=%d", AutoDetectLength ? "true" : "false", LengthInput, g_length, p_half, g_chart_id); return(INIT_SUCCEEDED); }
Aufräumen (OnDeinit)
OnDeinit() übernimmt die Bereinigung, wenn der EA aus dem Chart entfernt wird. Er ruft CleanupObjectsByPrefix() mit einem konsistenten Präfix auf, um alle vom EA erstellten grafischen Objekte (wie Trendlinien und Beschriftungen) zu entfernen. Auf diese Weise wird verhindert, dass verwaiste Objekte das Chart unübersichtlich machen und nachfolgende Analysen oder Werkzeuge beeinträchtigen – ein wichtiger Aspekt für professionelle Einsätze und Demonstrationen.
void OnDeinit(const int reason) { CleanupObjectsByPrefix("CHB_"); }
Hauptschleife (OnTick)
OnTick() implementiert ein Modell „einmal pro geschlossenem Balken“ für deterministisches Verhalten. Die Funktion prüft den Zeitstempel des letzten geschlossenen Balkens und führt keine weitere Arbeit aus, wenn sich der Balken seit dem letzten Aufruf nicht verändert hat. Wenn ein neuer geschlossener Balken erkannt wird, löst OnTick() drei primäre Funktionen aus: ScanForFractals(), um neu validierte Fraktale zu erkennen, PruneFractals(), um Einträge zu entfernen, die älter sind als das konfigurierte Historienlimit, und ProcessFractalCrosses(), um zu ermitteln, ob der Preis gespeicherte Fraktalebenen überschritten hat. Diese Sequenzierung bewahrt die Effizienz und stellt sicher, dass alle Erkennungen und Verarbeitungen an abgeschlossenen Balken stattfinden, was für reproduzierbare Backtests und ein zuverlässiges Live-Verhalten wichtig ist.
void OnTick() { static datetime last_checked = 0; datetime t = iTime(_Symbol, _Period, 1); if(t == last_checked) return; last_checked = t; ScanForFractals(); PruneFractals(MaxFractalHistoryBars); ProcessFractalCrosses(); }
Fraktalerkennung (ScanForFractals)
ScanForFractals() prüft, ob genügend historische Daten vorhanden sind, und untersucht dann den mittleren Balken bei der Verschiebung p_half, der die Mitte des Fraktalfensters darstellt. Es ruft IsFractalHighAtShift() und IsFractalLowAtShift() auf, um festzustellen, ob der mittlere Balken als hohes oder niedriges Fraktal eingestuft werden kann. Diese Hilfsmittel führen strenge Vergleiche zwischen dem mittleren Balken und seinen Nachbarn durch. Wenn ein gültiges Fraktal identifiziert wird und noch nicht aufgezeichnet ist (Vermeidung von Duplikaten durch Zeitstempel), fügt die Routine den Zeitstempel und den Preis an die entsprechenden Felder an und markiert es als unverarbeitet, damit es später an der Erkennung von Kreuzungen teilnehmen kann.
void ScanForFractals() { int bars = iBars(_Symbol, _Period); if(bars <= g_length) return; int centerShift = p_half; if(centerShift >= bars) return; // High fractal detection if(IsFractalHighAtShift(centerShift)) { datetime t_fr = (datetime)iTime(_Symbol, _Period, centerShift); double p_fr = iHigh(_Symbol, _Period, centerShift); // Store if new bool exists = false; for(int i=0;i<ArraySize(bull_time);i++) if(bull_time[i]==t_fr) exists = true; if(!exists) { int n = ArraySize(bull_time); ArrayResize(bull_time, n+1); ArrayResize(bull_price, n+1); ArrayResize(bull_marked, n+1); bull_time[n] = t_fr; bull_price[n] = p_fr; bull_marked[n] = false; if(DebugMode) PrintFormat("FRAC_BULL DETECTED: t=%s price=%G", TimeToString(t_fr, TIME_DATE|TIME_SECONDS), p_fr); } } // Low fractal detection if(IsFractalLowAtShift(centerShift)) { datetime t_fr = (datetime)iTime(_Symbol, _Period, centerShift); double p_fr = iLow(_Symbol, _Period, centerShift); // Store if new bool exists = false; for(int i=0;i<ArraySize(bear_time);i++) if(bear_time[i]==t_fr) exists = true; if(!exists) { int n = ArraySize(bear_time); ArrayResize(bear_time, n+1); ArrayResize(bear_price, n+1); ArrayResize(bear_marked, n+1); bear_time[n] = t_fr; bear_price[n] = p_fr; bear_marked[n] = false; if(DebugMode) PrintFormat("FRAC_BEAR DETECTED: t=%s price=%G", TimeToString(t_fr, TIME_DATE|TIME_SECONDS), p_fr); } } }
Hilfsmittel zur fraktalen Validierung
IsFractalHighAtShift() und IsFractalLowAtShift() erzwingen die Fraktaldefinition durch Iteration durch das symmetrische Fenster um den mittleren Balken. Sie geben false zurück, wenn ein Nachbar die Dominanz des Zentrums ungültig macht oder wenn das gesamte Fenster nicht verfügbar ist. Diese strengen Prüfungen verhindern verfrühte oder falsche Fraktalansprüche und schützen vor Indexfehlern zu Beginn der historischen Daten oder nach Zeitrahmenänderungen. Bei größeren Datensätzen sollten Sie das massenhafte Kopieren historischer Zeitreihen mit CopyHigh/CopyLow in Betracht ziehen, um wiederholte API-Aufrufe pro Balken zu reduzieren und die Leistung zu verbessern.
bool IsFractalHighAtShift(int shift) { int bars = iBars(_Symbol,_Period); int p = p_half; if(shift < 0 || shift >= bars) return false; double center = iHigh(_Symbol,_Period,shift); for(int k=-p; k<=p; k++) { if(k == 0) continue; int s = shift + k; if(s < 0 || s >= bars) return false; // incomplete window if(iHigh(_Symbol,_Period,s) > center) return false; } return true; }
Hilfsmittel: Prüfung auf tiefe Fraktalwerte
bool IsFractalLowAtShift(int shift) { int bars = iBars(_Symbol,_Period); int p = p_half; if(shift < 0 || shift >= bars) return false; double center = iLow(_Symbol,_Period,shift); for(int k=-p; k<=p; k++) { if(k == 0) continue; int s = shift + k; if(s < 0 || s >= bars) return false; if(iLow(_Symbol,_Period,s) < center) return false; } return true; }
Verarbeitung von Kreuzungen (ProcessFractalCrosses)
ProcessFractalCrosses() wandelt gespeicherte Fraktal-Levels in umsetzbare Signale um, indem sie prüft, ob der letzte bestätigte Schlusskurs einen Fraktalpreis überschritten hat. Die Funktion verwendet einen konservativen Ansatz für geschlossene Balken: Sie vergleicht prevClose (den vorhergehenden abgeschlossenen Balken) und curClose (den letzten abgeschlossenen Balken) und wendet CrossedOver() oder CrossedUnder() an, um Kreuzungen zu bestimmen. Wenn bei einem unverarbeiteten Fraktal eine Kreuzung erkannt wird, weist der EA ein eindeutiges, kennzeichnendes Objekt zu, klassifiziert das Ereignis auf der Grundlage von os_state als Break of Structure (BOS) oder Change of Character (ChoCH), zeichnet optional eine Bruchlinie und ein Etikett, aktualisiert os_state, markiert das Fraktal als verarbeitet und gibt Warnmeldungen aus. Diese einmalige Verarbeitung pro Fraktal macht das Verhalten deterministisch und eignet sich gut für Backtests.
void ProcessFractalCrosses() { double prevClose = iClose(_Symbol, _Period, 2); double curClose = iClose(_Symbol, _Period, 1); datetime curTime = (datetime)iTime(_Symbol, _Period, 1); // Process bullish fractals for(int i=0; i<ArraySize(bull_time); i++) { if(bull_marked[i]) continue; double level = bull_price[i]; if(CrossedOver(prevClose, curClose, level)) { datetime fr_time = bull_time[i]; string tag = "CHB_BULL_" + IntegerToString((int)fr_time); bool isChoCH = (os_state == -1); string niceName = isChoCH ? "Bull ChoCH" : "Bull BOS"; if(ShowBull) { DrawBreak(tag, fr_time, level, curTime, true); CreateAnchoredLabel(tag + "_lbl", niceName, fr_time, level + 3*ea_point, BullColor); } os_state = 1; bull_marked[i] = true; string msg = StringFormat("%s detected: %s %s at %s price=%s", niceName, _Symbol, TimeframeToString(_Period), TimeToString(curTime, TIME_DATE|TIME_MINUTES), DoubleToString(level, ea_digits)); EmitLogAlert(msg); } } // Process bearish fractals for(int i=0; i<ArraySize(bear_time); i++) { if(bear_marked[i]) continue; double level = bear_price[i]; if(CrossedUnder(prevClose, curClose, level)) { datetime fr_time = bear_time[i]; string tag = "CHB_BEAR_" + IntegerToString((int)fr_time); bool isChoCH = (os_state == 1); string niceName = isChoCH ? "Bear ChoCH" : "Bear BOS"; if(ShowBear) { DrawBreak(tag, fr_time, level, curTime, false); CreateAnchoredLabel(tag + "_lbl", niceName, fr_time, level - 3*ea_point, BearColor); } os_state = -1; bear_marked[i] = true; string msg = StringFormat("%s detected: %s %s at %s price=%s", niceName, _Symbol, TimeframeToString(_Period), TimeToString(curTime, TIME_DATE|TIME_MINUTES), DoubleToString(level, ea_digits)); EmitLogAlert(msg); } } }
Hinweisgebung ((EmitLogAlert)
EmitLogAlert() zentralisiert die Benachrichtigungslogik. Die Meldung wird zu Prüf- und Kontrollzwecken immer in das Expertenprotokoll gedruckt. Optional wird ein Popup-Alarm erzeugt, eine Push-Benachrichtigung an einen konfigurierten MetaTrader-Mobilclient gesendet und eine Tondatei abgespielt. Diese Multi-Channel-Alarmierung stellt sicher, dass die Händler unabhängig von ihrem aktuellen Kontext zeitnah benachrichtigt werden. Wenn Push-Benachrichtigungen erforderlich sind, stellen Sie sicher, dass die MetaQuotes-ID im Terminal konfiguriert ist.
void EmitLogAlert(const string msg) { Print(msg); if(EnableAlerts) Alert(msg); if(EnableNotifications) SendNotification(msg); if(EnableSound && StringLen(AlertSoundFile) > 0) PlaySound(AlertSoundFile); }
Funktionen zur Visualisierung
DrawBreak(), CreateTrendLine() und CreateAnchoredLabel() verwalten Chartgrafiken. DrawBreak() berechnet die Balkenindizes für das Fraktal und die Unterbrechungszeiten mit iBarShift(), erweitert die ältere Seite um HorizontalLeftExtend und verschiebt optional die neuere Kante in Richtung Gegenwart gemäß HorizontalRightBars. CreateTrendLine() erstellt ein verankertes Trendobjekt zwischen zwei Zeitpunkten und wendet ein visuelles Styling an. CreateAnchoredLabel() platziert einen beschreibenden Text an einem Zeitstempel und einem Preis, zur besseren Lesbarkeit um einige Punkte versetzt. Jede Erstellungsroutine verwendet SafeDelete(), um kollidierende Objekte mit demselben Namen zu entfernen, bevor neue erstellt werden; dadurch wird sichergestellt, dass das Chart übersichtlich und konsistent bleibt.
void DrawBreak(const string tag, datetime fract_time, double fract_price, datetime break_time, bool bullish) { int barFr = iBarShift(_Symbol, _Period, fract_time, false); int barBreak = iBarShift(_Symbol, _Period, break_time, false); int bars = iBars(_Symbol, _Period); if(barFr == -1 || barBreak == -1) return; int older_shift = MathMax(barFr, barBreak); int newer_shift = MathMin(barFr, barBreak); // Extend left older_shift = MathMin(older_shift + HorizontalLeftExtend, bars - 1); // Extend right towards current bar if(HorizontalRightBars > 0) newer_shift = MathMax(newer_shift - HorizontalRightBars, 0); // Swap if necessary if(older_shift < newer_shift) { int tmp = older_shift; older_shift = newer_shift; newer_shift = tmp; } datetime tLeft = (datetime)iTime(_Symbol, _Period, older_shift); datetime tRight = (datetime)iTime(_Symbol, _Period, newer_shift); string lineName = tag + "_line"; CreateTrendLine(lineName, tLeft, fract_price, tRight, (bullish ? BullColor : BearColor), false); } void CreateAnchoredLabel(const string name, const string txt, datetime when, double price, color col) { SafeDelete(name); if(ObjectCreate(g_chart_id, name, OBJ_TEXT, 0, when, price)) { ObjectSetString(g_chart_id, name, OBJPROP_TEXT, txt); ObjectSetInteger(g_chart_id, name, OBJPROP_COLOR, (int)col); ObjectSetInteger(g_chart_id, name, OBJPROP_FONTSIZE, 10); ObjectSetInteger(g_chart_id, name, OBJPROP_BACK, false); ObjectMove(g_chart_id, name, 0, when, price); } } void CreateTrendLine(const string name, datetime tLeft, double price, datetime tRight, color col, bool dashed=false) { SafeDelete(name); if(ObjectCreate(g_chart_id, name, OBJ_TREND, 0, tLeft, price, tRight, price)) { ObjectSetInteger(g_chart_id, name, OBJPROP_COLOR, (int)col); ObjectSetInteger(g_chart_id, name, OBJPROP_WIDTH, 2); ObjectSetInteger(g_chart_id, name, OBJPROP_STYLE, dashed ? STYLE_DASH : STYLE_SOLID); ObjectSetInteger(g_chart_id, name, OBJPROP_BACK, false); ObjectSetInteger(g_chart_id, name, OBJPROP_SELECTABLE, false); } } void SafeDelete(const string name) { if(ObjectFind(g_chart_id, name) >= 0) ObjectDelete(g_chart_id, name); }
Hilfsfunktionen
CrossedOver() und CrossedUnder() kapseln die Kreuzungs-Bedingungen für geschlossene Balken(prevClose <= level && curClose > level, und die Umkehrung).
bool CrossedOver(double prevClose, double curClose, double level) { return (prevClose <= level && curClose > level); } bool CrossedUnder(double prevClose, double curClose, double level) { return (prevClose >= level && curClose < level); }
TimeframeToString() konvertiert Zeitrahmenkonstanten in lesbare Zeichenketten für eine klarere Protokollausgabe.
string TimeframeToString(int period) { switch(period) { case PERIOD_M1: return "M1"; case PERIOD_M5: return "M5"; case PERIOD_M15: return "M15"; case PERIOD_M30: return "M30"; case PERIOD_H1: return "H1"; case PERIOD_H4: return "H4"; case PERIOD_D1: return "D1"; case PERIOD_W1: return "W1"; case PERIOD_MN1: return "MN1"; default: return IntegerToString(period); } }
CleanupObjectsByPrefix() entfernt alle Objekte, die ein Präfix gemeinsam haben; es geht rückwärts durch die Objektliste, um Indexverschiebungsfehler beim Löschen zu vermeiden.
void CleanupObjectsByPrefix(const string prefix) { long total = ObjectsTotal(g_chart_id); for(int i=total-1; i>=0; i--) { string name = ObjectName(g_chart_id, i); if(StringLen(name) >= StringLen(prefix) && StringSubstr(name, 0, StringLen(prefix)) == prefix) ObjectDelete(g_chart_id, name); } }
Entfernen (PruneFractals)
PruneFractals() erhält die Leistung und Speicherstabilität, indem gespeicherte Fraktale, die älter als der Schwellenwert MaxFractalHistoryBars sind, entfernt werden. Die Routine komprimiert Arrays an Ort und Stelle mit einem Schreibzeiger und ändert dann ihre Größe, wodurch unnötige Zuweisungen vermieden werden. Beachten Sie, dass iBarShift() beim Pruning für jedes gespeicherte Fraktal aufgerufen wird, sodass massive Speicherbegrenzungen die Verarbeitungszeit erhöhen können. Durch die Wahl eines angemessenen Standardwerts (z. B. 2000 Balken) wird ein Gleichgewicht zwischen historischer Abdeckung und Laufzeiteffizienz hergestellt.
void PruneFractals(int keepBars) { if(keepBars <=0) return; // Prune bullish fractals int nB = ArraySize(bull_time); if(nB > 0) { int write = 0; for(int i=0; i<nB; i++) { int sh = iBarShift(_Symbol, _Period, bull_time[i], false); if(sh != -1 && sh <= keepBars) { bull_time[write] = bull_time[i]; bull_price[write] = bull_price[i]; bull_marked[write] = bull_marked[i]; write++; } } if(write != nB) { ArrayResize(bull_time, write); ArrayResize(bull_price, write); ArrayResize(bull_marked, write); } } // Prune bearish fractals int nS = ArraySize(bear_time); if(nS > 0) { int write = 0; for(int i=0; i<nS; i++) { int sh = iBarShift(_Symbol, _Period, bear_time[i], false); if(sh != -1 && sh <= keepBars) { bear_time[write] = bear_time[i]; bear_price[write] = bear_price[i]; bear_marked[write] = bear_marked[i]; write++; } } if(write != nS) { ArrayResize(bear_time, write); ArrayResize(bear_price, write); ArrayResize(bear_marked, write); } } }
Dieser EA ist ein modulares und professionelles System, das validierte Fraktale auf geschlossenen Balken identifiziert, sie effizient speichert, bestätigte Kreuzungen erkennt, um BOS/ChoCH-Signale zu generieren, klare Chart-Anmerkungen wiedergibt und Multi-Channel-Warnungen ausgibt. Jedes Modul ist auf Übersichtlichkeit, Reproduzierbarkeit und Wartbarkeit ausgelegt. In einer Produktions- oder Schulungsumgebung sollten die Leser in einer Demo-Umgebung testen und während der Validierungsschritte den DebugMode aktivieren.
Optionen für Warnungen, Protokollierung und Benachrichtigung
Der EA bietet drei konfigurierbare Ausgabekanäle, damit Sie kein Ereignis verpassen: Desktop-Popups, Mobile Push und Sound. Verwenden Sie sie zusammen für eine maximale Abdeckung oder einzeln, wenn Sie einen leiseren Betrieb wünschen.
Einstellungen:
- EnableAlerts – Desktop-Popups über Alert() (sofort, sichtbar, während das Terminal läuft). Nützlich für die aktive Überwachung.
- EnableNotifications – mobile Push-Nachricht über SendNotification() (erfordert die Eingabe ihrer Metaquotes-ID unter in Extras – Optionen – Benachrichtigungen und die Aktivierung von Benachrichtigungen in der Mobil-App von MetaTrader.
- EnableSound – einen lokalen Terminalsound über PlaySound(Dateiname) abspielen; die Datei muss sich im Ordner Sounds des Terminals befinden und die Lautstärke des Terminalsounds darf nicht stumm gestellt sein.
Empfohlenes Format der Warnmeldung (klar, analysierbar):
Bull BOS detected: EURUSD H1 at 2025.08.01 14:00 price=1.12345 fr_time=2025.08.01 12:00
Geben Sie Symbol, Zeitrahmen, Ereignistyp, Bestätigungszeit, Preis und optional einen Fraktal-Zeitstempel an, um den Abgleich zu erleichtern.
Um Benachrichtigungen zu aktivieren, stellen Sie Ihre MetaQuotes-ID in den Terminaloptionen ein und stellen Sie sicher, dass die Benachrichtigungen auf Ihrem mobilen Gerät aktiviert sind.

Kopieren Sie Ihre WAV- oder MP3-Dateien in den Sounds-Ordner des Terminals und überprüfen Sie, ob die PlaySound-Funktion über Optionen – Ereignisse korrekt funktioniert. Um Spamming zu verhindern, sollten Sie die *_marked[]-Arrays verwenden, um doppelte Alarme für dasselbe Fraktal zu vermeiden, und bei volatilen Marktbedingungen eine kurze Abkühlungsphase (z. B. 30-120 Sekunden) pro Symbol einführen. Außerdem können Sie zu Prüfungs- und Fehlerbehebungszwecken Ereignisse protokollieren, indem Sie sie an eine CSV-Datei anhängen – mit FILE_COMMON oder indem Sie die Datei im MQL5/Files-Verzeichnis des Terminals ablegen – während des Testens, und stellen Sie sicher, dass Sie die Datei nach dem Schreiben sofort schließen, um Dateisperren zu vermeiden.
Tests und Ergebnisse
In diesem Abschnitt stelle ich die Leistung des Systems vor – sowohl historische Backtests als auch Live-Ergebnisse – die meines Erachtens den Zielvorgaben entsprechen und sich wie erwartet verhalten. Ich zeige die Backtest-Konfiguration und -Metriken, die Kapitalkurve und Handelsbeispiele, die das Verhalten des Detektors in der Praxis veranschaulichen, sowie einen kurzen Überblick über die Live-Performance mit Echtzeit-Screenshots und den Abgleich mit dem Backtest. Abschließend fasse ich die Einschränkungen zusammen und schlage die nächsten Schritte zur weiteren Validierung vor.
Ich habe Backtests für EURUSD und Step Index durchgeführt, beide im H1-Zeitrahmen, und zeige die Ergebnisse unten. Die folgende GIF-Animation stellt die ChoCH- und BOS-Ereignisse klar und deutlich dar – die Anmerkungen sind präzise und machen die Abfolge von Warnung und Bestätigung leicht nachvollziehbar.
- Step Index H1: kommentierter fraktaler Pivot (Anker), ChoCH (gescheitertes HH) und Bull BOS (bestätigt auf Schlusskurs).

- EURUSD H1: kommentierter fraktaler Pivot (Anker), ChoCH (gescheitertes HH) und Bull BOS (bestätigt auf Schlusskurs).

Ich habe auch Live-Tests durchgeführt, um die Echtzeitleistung zu bestätigen, und das Tool hat sich genau wie erwartet verhalten. Die folgenden Screenshots zeigen, wie der EA die Ereignisse ChoCH und BOS in Echtzeit erkennt, kennzeichnet und protokolliert, wobei die Desktop-Warnungen und Chart-Objekte sofort nach dem bestätigten geschlossenen Balken erscheinen. Das Live-Verhalten stimmte mit den Backtest-Signalen überein, wobei in den Ausführungsprotokollen nur die üblichen Live-Market-Effekte (Spread und Slippage) sichtbar waren.
Live-Demo-Volatilität 75 (1s) Index M1: Echtzeit ChoCH-Warnungen und Bull BOS-Bestätigungen.
Dieser Live-Screenshot zeigt, wie das System fraktale Umkehrpunkte verwendet, um frühe ChoCH-Warnungen zu erkennen und dann Strukturverschiebungen mit nicht übermalenden BOS-Markern zu bestätigen – eine visuelle Bestätigung des Echtzeitverhaltens des Detektors.
Live-Demo-Step Index M1: ChoCH-Warnungen und Bull BOS-Bestätigungen in Echtzeit
Der EA markiert zunächst eine Abwärtsbewegung mit einem fallenden ChoCH und aufeinanderfolgenden fallenden BOS-Levels, markiert dann ein steigenden ChoCH, wenn die Abwärtsbewegung scheitert, und bestätigt eine Aufwärts-Umkehr mit mehreren Aufwärts-BOS-Signalen – ein Beweis für das Frühwarnsystem (ChoCH) und die nicht nachmalende Bestätigung (BOS) in Echtzeit.
Nachfolgend finden Sie die Protokolle auf der Registerkarte MetaTrader 5 experts.
2025.09.03 10 : 20 :58.856 Fractal Reaction System (Volatility 75 (1s) Index,M1) Alert: Bear BOS detected: Volatility 75 (1s) Index M1 at 2025.09.03 13:44 price=3446.40 2025.09.03 10 : 20 :58.856 Fractal Reaction System (Volatility 75 (1s) Index,M1) Bear BOS detected: Volatility 75 (1s) Index M1 at 2025.09.03 13:44 price=3446.73 2025.09.03 10 : 20 :58.856 Fractal Reaction System (Volatility 75 (1s) Index,M1) Alert: Bear BOS detected: Volatility 75 (1s) Index M1 at 2025.09.03 13:44 price=3446.73 2025.09.03 10 : 20 :58.894 Fractal Reaction System (Step Index,M1) Bear BOS detected: Step Index M1 at 2025.09.03 13:44 price=8233.6 2025.09.03 10 : 20 :58.894 Fractal Reaction System (Step Index,M1) Alert: Bear BOS detected: Step Index M1 at 2025.09.03 13:44 price=8233.6 2025.09.03 10 : 20 :58.896 Fractal Reaction System (Volatility 75 (1s) Index,M1) Alert: Bear BOS detected: Volatility 75 (1s) Index M1 at 2025.09.03 13:44 price=3447.86
Sowohl bei historischen Backtests (EURUSD und Step Index, H1) als auch bei Live-Tests verhielt sich der fraktalbasierte Detektor genau so, wie er konzipiert war: Fraktale Umkehrpunkte lieferten stabile, Ankerpunkte ohne Repainting. Die ChoCH-Warnungen zeigten frühzeitig den Verlust der Verzerrung an, und BOS-Bestätigungen signalisierten zuverlässig die strukturellen Verschiebungen. Backtests ergaben konsistente Handelsereignisse, die mit den kommentierten GIFs übereinstimmen (Warn- und Bestätigungssequenzen), und Live-Screenshots zeigen dasselbe Muster in Echtzeit, nur mit den erwarteten Live-Market-Effekten (Spread und Slippage).
Schlussfolgerung
Das Fractal Reaction System wandelt einfache fraktale Umkehrpunkte in zuverlässige, nicht nachmalende Marktstruktursignale um: ChoCH (Change of Character) als Frühwarnung und BOS (Break of Structure) als Bestätigung. Der hier vorgestellte EA ist speichersicher, wertet aus Gründen der Reproduzierbarkeit nur geschlossene Balken aus, zeichnet persistente Strukturobjekte auf dem Chart und protokolliert und warnt bei jedem bestätigten Ereignis – ein Verhalten, das sowohl in historischen Backtests als auch in Live-Tests konsistent ist. Seine Hauptstärken sind Transparenz (überprüfbare Ereignisse), Reproduzierbarkeit (geschlossene Logik) und Praktikabilität (Desktop-, mobile und akustische Benachrichtigungen sowie einfache Abstimmung über Protokolle).
Dieses Tool ist ein Signaldetektor, kein vollständiger Handelsmanager: Faktoren der Live-Ausführung wie Spread, Slippage und Fills beeinflussen die realisierte Leistung, und die ChoCH-Warnungen sind eher informativ als vorschreibend. Bevor Sie Kapital einsetzen, sollten Sie den EA mit Ihren eigenen Instrumenten und Broker-Einstellungen validieren, das mitgelieferte Einstellungen für Backtests und die Ereignisprotokolle prüfen und das Hinzufügen von Positionsgrößenregeln, Filtern für höhere Zeitrahmen oder eines Cooldown-Mechanismus in Betracht ziehen, um Ihrem Risikoprofil zu entsprechen.
Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/19365
Warnung: Alle Rechte sind von MetaQuotes Ltd. vorbehalten. Kopieren oder Vervielfältigen untersagt.
Dieser Artikel wurde von einem Nutzer der Website verfasst und gibt dessen persönliche Meinung wieder. MetaQuotes Ltd übernimmt keine Verantwortung für die Richtigkeit der dargestellten Informationen oder für Folgen, die sich aus der Anwendung der beschriebenen Lösungen, Strategien oder Empfehlungen ergeben.
Die Grenzen des maschinellen Lernens überwinden (Teil 3): Eine neue Perspektive auf irreduzible Fehler
Aufbau eines professionellen Handelssystems mit Heikin Ashi (Teil 1): Entwickeln eines nutzerdefinierten Indikators
Vereinfachung von Datenbanken in MQL5 (Teil 1): Einführung in Datenbanken und SQL
Automatisieren von Handelsstrategien in MQL5 (Teil 31): Erstellung eines Price Action 3 Drives Harmonic Pattern Systems
- Freie Handelsapplikationen
- Über 8.000 Signale zum Kopieren
- Wirtschaftsnachrichten für die Lage an den Finanzmärkte
Sie stimmen der Website-Richtlinie und den Nutzungsbedingungen zu.
Hallo,
Datei 'C:\Benutzer\Administrator\AppData\Roaming\MetaQuotes\Terminal\24F345EB9F291441AFE537834F9D8A19\MQL5\Include\stdlib_mq5.mqh' nicht gefunden Fractal_Reaction_System.mq5
Wo kann ich die Datei bekommen?
Chris