Larry Williams Marktgeheimnisse (Teil 3): Nachweis eines nicht zufälligen Marktverhaltens mit MQL5
Einführung
Sind die Finanzmärkte wirklich zufällig, oder weisen sie Muster auf, die gemessen und getestet werden können? Diese Frage steht im Mittelpunkt eines jeden Handelssystems, das jemals entwickelt wurde. Wenn die Preisbewegung vollkommen zufällig ist, ist jeder Versuch, eine Strategie, einen Indikator oder ein automatisches Handelssystem zu entwickeln, letztlich sinnlos. Es gäbe keinen Vorteil zu entdecken, nur den Zufall.
In diesem Artikel werden diese Ideen aus einer modernen, praktischen Perspektive wieder aufgegriffen (für eine ausführlichere Diskussion der zugrunde liegenden statistischen Konzepte siehe den vorherigen Artikel). Anstatt die Schlussfolgerungen für bare Münze zu nehmen, werden wir MQL5 verwenden, um die Experimente von Williams mithilfe von Code nachzustellen und zu erweitern. Indem wir einen nutzerdefinierten Expert Advisor schreiben, testen wir, ob bestimmte Kursverhaltensweisen häufiger auftreten, als zufällig zu erwarten wäre. Das Ziel besteht nicht darin, die Märkte mit Sicherheit vorherzusagen, sondern darin festzustellen, ob kleine, messbare Verzerrungen bestehen.
Dies ist wichtig, weil selbst eine leichte statistische Verzerrung die Grundlage für einen Handelsvorteil bilden kann. Wenn das Preisverhalten der Vergangenheit keinen Bezug zu zukünftigen Ergebnissen hat, kann der systematische Handel nicht funktionieren. Wenn Märkte jedoch auch nur teilweise nicht zufällig sind, werden Struktur, Logik und Wahrscheinlichkeit zu sinnvollen Instrumenten für Händler.
Wie wir nicht zufälliges Marktverhalten testen
In der Einleitung haben wir eine einfache, aber wesentliche Idee dargelegt. Wenn die Märkte wirklich zufällig wären, würde das Preisverhalten einem Münzwurf ähneln. Jede Kerze wäre unabhängig von der vorangegangenen, und die Wahrscheinlichkeit, dass sie nach oben oder unten schließt, bliebe bei fünfzig Prozent, unabhängig davon, was vorher geschehen ist.
Larry Williams stellte diese Annahme infrage, indem er eine praktische Frage stellte. Was geschieht, nachdem der Kurs nach oben oder unten geschlossen hat? Wenn die Märkte kein Gedächtnis haben, dann sollte sich das Ergebnis der nächsten Kerze nicht ändern. Seine Untersuchungen haben gezeigt, dass dies nicht der Fall ist.
Um dieses Konzept praktisch und messbar zu überprüfen, werden wir einen automatisierten Ansatz wählen. Anstatt uns auf Annahmen oder selektive Beispiele zu verlassen, lassen wir den Code historische Kursdaten scannen und zählen, wie oft bestimmte Bedingungen auftreten. Jeder Test ist darauf ausgerichtet, eine bestimmte Wahrscheinlichkeitsfrage zum Preisverhalten zu beantworten.
Die Experimente, die wir durchführen werden, lassen sich in drei große Kategorien einteilen. Zunächst testen wir, ob es innerhalb einer einzelnen Kerze eine allgemeine Tendenz zur Richtungsänderung gibt. Dies beantwortet die grundlegende Frage, ob der Kurs häufiger höher schließt als er eröffnet, als es rein zufällig zu erwarten wäre.
Zweitens: Wir testen die bedingte Wahrscheinlichkeit. Hier geht es um die Frage, ob sich der Markt nach einer oder mehreren aufeinanderfolgenden Auf- oder Abwärtskerzen unterschiedlich verhält. Wären das Marktverhalten zufällig, bliebe die Wahrscheinlichkeit, dass die nächste Kerze auf- oder abwärts ist, unverändert. Wenn sich die Wahrscheinlichkeiten verschieben, reagiert der Preis eindeutig auf seine jüngste Vergangenheit.
Drittens testen wir eine einfache Form der Marktstruktur. Larry Williams beschrieb ein kurzfristiges Tief als ein Muster aus drei Balken, bei dem der Kurs einen Tiefpunkt erreicht und dann nicht weiter sinkt. Wenn dieses Muster einen Vorhersagewert hat, dann sollte die darauf folgende Kerze häufiger höher schließen, als es der Zufall vermuten ließe.
Um diese Ideen abzudecken, automatisieren wir die folgenden acht Testfälle:
Das Bias von Eröffnungs- und Schlusskurs
Wir messen, wie oft eine Kerze aufwärts schließt, d.h. der Schlusskurs ist höher als der Eröffnungskurs. In einem wirklich zufälligen Markt sollten gestiegene und gefallene Schlusskurse jeweils etwa fünfzig Prozent der Zeit auftreten.
Aufwärtsreaktion nach einem tieferen Schlusskurs
Wir testen, wie oft die nächste Kerze nach einer einzelnen Abwärtskerze aufwärts schließt. Auf diese Weise kann festgestellt werden, ob ein kurzfristiger Rücksetzer eher einen Kaufdruck erzeugt.
Aufwärtsreaktion nach zwei aufeinanderfolgenden tieferen Schlusskursen
Hier prüfen wir, ob die Wahrscheinlichkeit einer Abwärtskerze nach zwei aufeinanderfolgenden Abwärtskerzen steigt. Dabei wird untersucht, ob kurzfristige Rückgänge tendenziell zu einem Aufschwung führen.
Aufwärtsreaktion nach drei aufeinanderfolgenden tieferen Schlusskursen
Dieser Test geht noch einen Schritt weiter, indem er das Preisverhalten nach einem tieferen kurzfristigen Rückgang untersucht.
Aufwärtsreaktion nach einem höheren Schlusskurs
Wir testen, ob das Aufwärtsmomentum nach einer einzigen Aufwärtskerze anhält oder nachlässt.
Aufwärtsgerichtete Reaktion nach zwei aufeinanderfolgenden höheren Schlusskursen
Bei diesem Experiment wird getestet, ob der Stärke eine weitere Stärke folgt oder ob der Preis nach einer kurzen Rallye zum Stillstand kommt.
Aufwärtsgerichtete Reaktion nach drei aufeinanderfolgenden höheren Schlusskursen
Dieser Test prüft, ob ein längerer kurzfristiger Kaufdruck zu einer Fortsetzung oder Erschöpfung führt.
Aufwärtsgerichtete Reaktion nach einem kurzfristigen Markttief
In Anlehnung an die Definition von Larry Williams erkennen wir ein kurzfristiges Tief anhand eines Kursmusters aus drei Balken und messen, wie oft die folgende Kerze eine Aufwärtskerze ist.
Jeder dieser Tests beantwortet eine einfache Frage. Beeinflusst das Ergebnis früherer Kerzen die Wahrscheinlichkeit der nächsten Kerze? Wenn die Antwort immer fünfzig Prozent wäre, dann würden sich die Märkte wie ein Münzwurf verhalten. Jede konstante Abweichung von diesem Wert deutet auf ein nicht zufälliges Verhalten hin.
Da diese Testfälle klar definiert sind, können wir nun zum technischen Teil übergehen. Im nächsten Abschnitt werden wir einen MQL5-Expertenberater implementieren, der es uns ermöglicht, jedes Experiment einzeln auf verschiedenen Märkten durchzuführen und die Ergebnisse direkt anhand historischer Daten zu beobachten.
Implementierung der Experimente in MQL5
Um zu prüfen, ob dieses Marktverhalten wirklich nicht zufällig ist, benötigen wir mehr als Statistiken. Wir wollen die realen Handelsbedingungen simulieren. Das bedeutet, dass eine Position bei Kerzenöffnung eröffnet und bei Kerzenschluss geschlossen wird, und dass aufgezeichnet wird, was tatsächlich passiert. Dieser Ansatz ermöglicht es uns, Wahrscheinlichkeiten zu bewerten und auch eine praktischere Frage zu beantworten. Können diese Verhaltensweisen später in handelbare Ideen umgewandelt werden?
Um zu beginnen, öffnen Sie den MetaEditor 5, erstellen einen neuen Expert Advisor, wählen eine leere Vorlage und benennen die Datei larryWilliamsNonRandomMarketBehaviorTester.mq5. Fügen Sie nach der Erstellung den mitgelieferten Quellcode in die Datei ein und speichern Sie sie.
//+------------------------------------------------------------------+ //| larryWilliamsNonRandomMarketBehaviorTester.mq5 | //| Copyright 2025, MetaQuotes Ltd. Developer is Chacha Ian | //| https://www.mql5.com/en/users/chachaian | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, MetaQuotes Ltd. Developer is Chacha Ian" #property link "https://www.mql5.com/en/users/chachaian" #property version "1.00" #property description "This Expert Advisor is designed to run one statistical experiment at a time." #property description "The test to be executed is selected through a dropdown input when attaching the EA to the chart." //+------------------------------------------------------------------+ //| Standard Libraries | //+------------------------------------------------------------------+ #include <Trade\Trade.mqh> //+------------------------------------------------------------------+ //| Global Variables | //+------------------------------------------------------------------+ //--- Create a CTrade object to handle trading operations CTrade Trade; //--- Bid and Ask double askPrice; //+------------------------------------------------------------------+ //| User input variables | //+------------------------------------------------------------------+ input group "Information" input ulong magicNumber = 254700680002; input ENUM_TIMEFRAMES timeframe = PERIOD_CURRENT; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit(){ //--- Assign a unique magic number to identify trades opened by this EA Trade.SetExpertMagicNumber(magicNumber); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason){ //--- Notify why the program stopped running Print("Program terminated! Reason code: ", reason); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick(){ //--- Retrieve current market prices for trade execution askPrice = SymbolInfoDouble (_Symbol, SYMBOL_ASK); } //+------------------------------------------------------------------+ //| TradeTransaction function | //+------------------------------------------------------------------+ void OnTradeTransaction(const MqlTradeTransaction& trans, const MqlTradeRequest& request, const MqlTradeResult& result) { } //+------------------------------------------------------------------+
Dieser erste Code ist die Grundlage, auf der wir aufbauen werden. Er führt noch keine Experimente durch, aber es richtet alles ein, was wir dafür brauchen, und zwar auf eine saubere, kontrollierte Weise. Um Ihnen die Arbeit zu erleichtern, haben wir den endgültigen Quellcode beigefügt: larryWilliamsNonRandomMarketBehaviorTester.mq5. Laden Sie die Datei herunter und vergleichen Sie sie mit Ihrer Version, um sicherzustellen, dass alles richtig eingestellt ist.
Datei-Header und die Direktiven der Eigenschaften
//+------------------------------------------------------------------+ //| larryWilliamsNonRandomMarketBehaviorTester.mq5 | //| Copyright 2025, MetaQuotes Ltd. Developer is Chacha Ian | //| https://www.mql5.com/en/users/chachaian | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, MetaQuotes Ltd. Developer is Chacha Ian" #property link "https://www.mql5.com/en/users/chachaian" #property version "1.00" #property description "This Expert Advisor is designed to run one statistical experiment at a time." #property description "The test to be executed is selected through a dropdown input when attaching the EA to the chart."
Der erste Abschnitt enthält den Dateinamen, Angaben zum Autor, die Version und die Beschreibung. Diese Eigenschaften sind wichtig, weil sie den Zweck des Expert Advisors beschreiben, wenn er an ein Chart angehängt ist. In diesem Fall besagt die Beschreibung eindeutig, dass der EA jeweils ein statistisches Experiment durchführt und das Experiment über ein Eingabe-Dropdown ausgewählt wird. Dies steht in direktem Zusammenhang mit den Zielen des Artikels.
Einbeziehung der Standardbibliothek
//+------------------------------------------------------------------+ //| Standard Libraries | //+------------------------------------------------------------------+ #include <Trade\Trade.mqh>
Als Nächstes wird die Standard-Handelsbibliothek eingebunden. Diese Bibliothek ermöglicht den Zugriff auf die Klasse CTrade, die die Ausführung von Aufträgen auf sichere und strukturierte Weise abwickelt. Die Verwendung dieser Klasse wird empfohlen, da sie die Verwaltung der Handelsgeschäfte vereinfacht und das Risiko von Ausführungsfehlern verringert.
Einstellungen für Nutzereingaben//+------------------------------------------------------------------+ //| User input variables | //+------------------------------------------------------------------+ input group "Information" input ulong magicNumber = 254700680002; input ENUM_TIMEFRAMES timeframe = PERIOD_CURRENT;
Im Eingabebereich werden die Parameter definiert, die der Nutzer steuern kann, wenn er den EA an einen Chart anhängt. Wir geben eine magische Zahl an, um die von diesem EA eröffneten Handelsgeschäfte eindeutig zu identifizieren und zu verhindern, dass sie mit anderen Strategien in Konflikt geraten. Der Nutzer kann auch den Zeitrahmen für das Experiment auswählen. Dies macht den EA flexibel und wiederverwendbar für verschiedene Chart-Setups.
Globale Handelsobjekte und Variablen
//+------------------------------------------------------------------+ //| Global Variables | //+------------------------------------------------------------------+ //--- Create a CTrade object to handle trading operations CTrade Trade; //--- Bid and Ask double askPrice;
Wir erstellen dann ein globales CTrade-Objekt. Dieses Objekt wird im gesamten EA zum Öffnen und Schließen von Positionen verwendet. Wir deklarieren auch Variablen für Marktpreise. In diesem Stadium rufen wir nur den Ask-Preis ab, der für die Eröffnung von Kaufgeschäften während unserer Experimente ausreichend ist.
Logik der Initialisierung
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit(){ //--- Assign a unique magic number to identify trades opened by this EA Trade.SetExpertMagicNumber(magicNumber); return(INIT_SUCCEEDED); }
Die Initialisierungsfunktion wird einmal ausgeführt, wenn der EA mit einem Chart verbunden wird. Hier weisen wir dem CTrade-Objekt die magische Zahl zu. Dieser Schritt ist unerlässlich, da er sicherstellt, dass jeder vom EA eröffnete Handel später korrekt nachverfolgt und verwaltet werden kann.
Logik der Deinitialisierung
//+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason){ //--- Notify why the program stopped running Print("Program terminated! Reason code: ", reason); }
Wenn der EA entfernt oder beendet wird, wird die Deinitialisierungsfunktion aufgerufen. In unserem Fall wird einfach eine Meldung ausgegeben, die angibt, warum das Programm angehalten wurde. Dies ist nützlich für die Fehlersuche und um zu verstehen, wann und wie der EA-Lebenszyklus endet.
Handhabung der Ticks
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick(){ //--- Retrieve current market prices for trade execution askPrice = SymbolInfoDouble (_Symbol, SYMBOL_ASK); }
Die Tick-Funktion wird kontinuierlich ausgeführt, sobald neue Marktdaten eintreffen. Momentan wird nur der aktuelle Ask-Preis abgerufen. Später wird diese Funktion als Kontrollzentrum für unsere Experimente dienen. Sie erkennt neue Kerzen, bewertet die Testbedingungen, eröffnet Handelsgeschäfte und schließt sie zum richtigen Zeitpunkt.
Behandlung der Handelstransaktionen
//+------------------------------------------------------------------+ //| TradeTransaction function | //+------------------------------------------------------------------+ void OnTradeTransaction(const MqlTradeTransaction& trans, const MqlTradeRequest& request, const MqlTradeResult& result) { }
Schließlich definieren wir die Funktion der Handelstransaktion. Diese Funktion wird immer dann ausgelöst, wenn eine Handelsaktion stattfindet, z. B. die Eröffnung oder Schließung einer Position. Sie ist derzeit noch leer, wird aber später dazu beitragen, die Ergebnisse des Handels zu verfolgen und die Ergebnisse von Experimenten zu sammeln.
Zu diesem Zeitpunkt zeigt der Expert Advisor keine sichtbaren Aktivitäten auf dem Chart. Das ist beabsichtigt. Wir haben ein sauberes und zuverlässiges Grundgerüst erstellt, das bewährten Verfahren folgt. Im nächsten Abschnitt werden wir damit beginnen, eine Logik zur Erkennung neuer Kerzen hinzuzufügen, unsere nicht zufälligen Testfälle anzuwenden und Handelsgeschäfte auszuführen, um das reale Marktverhalten in Aktion zu beobachten.
Nach der Erstellung des EA-Skeletts besteht die nächste Phase darin, unserem Programm beizubringen, wie es das Marktverhalten erkennen kann. In diesem Stadium sind wir noch nicht im Handel. Unser Ziel ist einfach und entscheidend. Wir wollen, dass der EA jedes Muster, das einen unserer nicht zufälligen Testfälle darstellt, korrekt erkennt.
Bevor ein EA etwas testen kann, muss er genau wissen, welches Experiment er durchführt. Deshalb ist der erste Schritt, einen Mechanismus zu definieren, der einen sauberen und kontrollierten Wechsel zwischen den Testszenarien ermöglicht.
Festlegung der Prüfmodi
Direkt unterhalb der Standardbibliothekseinbindung führen wir eine nutzerdefinierte Enumeration ein, die alle nicht zufälligen Experimente auflistet, die wir durchführen wollen. Jeder Wert steht für ein bestimmtes Marktverhalten, das wir untersuchen wollen.
//--- CUSTOM ENUMERATIONS //+------------------------------------------------------------------+ //| Non-random market behavior test modes | //+------------------------------------------------------------------+ enum ENUM_NON_RANDOM_TEST_MODE { TEST_OPEN_TO_CLOSE_BIAS, TEST_AFTER_ONE_DOWN_CLOSE, TEST_AFTER_TWO_DOWN_CLOSES, TEST_AFTER_THREE_DOWN_CLOSES, TEST_AFTER_ONE_UP_CLOSE, TEST_AFTER_TWO_UP_CLOSES, TEST_AFTER_THREE_UP_CLOSES, TEST_AFTER_SHORT_TERM_LOW };
Diese Enumeration ermöglicht es dem EA, jeweils in einem Testmodus zu arbeiten. Wenn der EA an ein Chart angehängt wird, wählt der Nutzer aus einem als Eingabeparameter definierten Dropdown-Menü aus, welches Experiment ausgeführt werden soll. Dadurch wird die Logik einfach gehalten und die Vermischung von Ergebnissen aus verschiedenen Tests vermieden.
Zu diesem Zeitpunkt testen wir noch keine Wahrscheinlichkeiten. Wir definieren nur die Struktur. Diese Struktur dient als Leitfaden für die Erkennung von Mustern und die darauf folgende Entscheidungsfindung.
Erkennen, ob der Balken geöffnet ist
Alle unsere Experimente werden einmal ausgewertet, wenn eine neue Kerze geöffnet wird. Dies ist von entscheidender Bedeutung. Wenn wir auf jeden Tick reagieren, führen wir Rauschen und doppelte Signale ein. Um dies zu vermeiden, erkennen wir Muster nur, wenn sich ein neuer Balken bildet.
Um dies zu erreichen, definieren wir eine kleine, aber leistungsstarke Funktion, die prüft, ob ein neuer Balken im ausgewählten Zeitrahmen geöffnet wurde.
//--- UTILITY FUNCTIONS //+------------------------------------------------------------------+ //| Function to check if there's a new bar on a given chart timeframe| //+------------------------------------------------------------------+ bool IsNewBar(string symbol, ENUM_TIMEFRAMES tf, datetime &lastTm) { datetime currentTm = iTime(symbol, tf, 0); if(currentTm != lastTm){ lastTm = currentTm; return true; } return false; }
Die Funktion vergleicht die Eröffnungszeit des aktuellen Balkens mit der Zeit des letzten aufgezeichneten Balkens. Wenn sich die Zeit geändert hat, hat sich ein neuer Balken gebildet.
Um diese Logik zu unterstützen, deklarieren wir auch eine globale Variable, die die Öffnungszeit des letzten Balkens speichert.
//+------------------------------------------------------------------+ //| Global Variables | //+------------------------------------------------------------------+ ... //--- To help track new bar open datetime lastBarOpenTime;
Diese Variable wird in der Initialisierungsfunktion auf Null initialisiert, damit der erste Balken immer korrekt erkannt wird.
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit(){ ... //--- Initialize global variables lastBarOpenTime = 0; return(INIT_SUCCEEDED); }
Von diesem Zeitpunkt an wird jede Musterprüfung im EA nur dann durchgeführt, wenn ein neuer Balken bestätigt wird. Dies gewährleistet die Konsistenz aller Experimente.
Eine universelle Funktion für das Verhalten von aufeinanderfolgenden Balken
Die meisten unserer Testfälle beruhen auf einer einfachen Frage. Was geschieht nach einer bestimmten Anzahl von Auf- und Abwärtsbalken als Nächstes?
Anstatt für jeden Fall eine eigene Logik zu schreiben, entwickeln wir eine einzige, flexible Funktion, die alle Fälle abdeckt. Diese Funktion prüft, ob die letzten N abgeschlossenen Kerzen alle in der gleichen Richtung relativ zu ihrer Öffnung geschlossen wurden.
//+------------------------------------------------------------------+ //| Checks whether the last N completed bars all closed | //| either up or down relative to their open | //+------------------------------------------------------------------+ bool IsConsecutiveBarCloseState(string symbol, ENUM_TIMEFRAMES tf, int barsToCheck, ENUM_BAR_CLOSE_STATE closeState) { // Start from bar index 1 (last fully closed bar) for(int i = 1; i <= barsToCheck; i++){ double openPrice = iOpen (symbol, timeframe, i); double closePrice = iClose(symbol, timeframe, i); // Safety check (in case of missing data) if(openPrice == 0.0 || closePrice == 0.0){ return false; } // Validate close direction if(closeState == BAR_CLOSE_UP && closePrice <= openPrice){ return false; } if(closeState == BAR_CLOSE_DOWN && closePrice >= openPrice){ return false; } } return true; }
Um die Funktion wiederverwendbar und übersichtlich zu gestalten, übergeben wir vier Eingänge. Das zu testende Symbol, der Zeitrahmen, die Anzahl der zu bewertenden aufeinanderfolgenden Balken und die Richtung dieser Balken. Um die Richtung des Balkens klar zu beschreiben, führen wir eine kleine Enumeration für gestiegene und gefallene Schlusskurse ein.
//--- CUSTOM ENUMERATIONS //+------------------------------------------------------------------+ //| Non-random market behavior test modes | //+------------------------------------------------------------------+ ... //+------------------------------------------------------------------+ //| Direction of candle close relative to open | //+------------------------------------------------------------------+ enum ENUM_BAR_CLOSE_STATE { BAR_CLOSE_UP, BAR_CLOSE_DOWN };
Die Funktion durchläuft nur abgeschlossene Kerzen. Sie beginnt mit dem zuletzt geschlossenen Balken und bewegt sich rückwärts. Für jeden Balken werden die Eröffnungs- und Schlusskurse verglichen und überprüft, ob sie mit der erwarteten Richtung übereinstimmen. Wenn ein Balken die Bedingung nicht erfüllt, gibt die Funktion sofort false zurück. Wenn alle Balken übereinstimmen, gibt die Funktion true zurück.
Diese einzige Funktion unterstützt sechs unserer acht Experimente. Sie ist der Kernbaustein unserer nicht zufälligen Tests und wird später den realen Handel bestimmen.
Erkennen eines kurzfristigen Tiefs nach Larry Williams
Das letzte Muster, das wir erkennen müssen, ist eher strukturell. Larry Williams definiert ein kurzfristiges Tief als eine Formation aus drei Balken, bei der der mittlere Balken einen tiefen Umkehrpunkt bildet, mit höheren Tiefs auf beiden Seiten.
Er fügt jedoch auch zwei wichtige Filter hinzu. Der Umkehr-Balken darf kein „Outside-Balken“ sein und der letzte Balken kein „Inside-Balken“. Diese Bedingungen helfen, schwache oder irreführende Signale zu vermeiden.
Um dieses Muster zu erkennen, entwickeln wir eine spezielle Funktion, die die letzten drei abgeschlossenen Balken analysiert.
//+------------------------------------------------------------------+ //| Detects a Larry Williams short-term low on the last three bars | //| Bar index 2 must be a swing low with higher lows on both sides | //| Bar 2 must NOT be an outside bar | //| Bar 1 must NOT be an inside bar | //+------------------------------------------------------------------+ bool IsLarryWilliamsShortTermLow(string symbol, ENUM_TIMEFRAMES tf){ //--- Price data for the three bars double high1 = iHigh(symbol, tf, 1); double low1 = iLow (symbol, tf, 1); double high2 = iHigh(symbol, tf, 2); double low2 = iLow (symbol, tf, 2); double high3 = iHigh(symbol, tf, 3); double low3 = iLow (symbol, tf, 3); //--- Condition 1: Bar 2 must be a swing low bool isSwingLow = (low2 < low1) && (low2 < low3); if(!isSwingLow){ return false; } //--- Condition 2: Bar 2 must NOT be an outside bar relative to bar 3 bool isOutsideBar = (high2 > high3) && (low2 < low3); if(isOutsideBar){ return false; } //--- Condition 3: Bar 1 must NOT be an inside bar relative to bar 2 bool isInsideBar = (high1 < high2) && (low1 > low2); if(isInsideBar){ return false; } //--- All conditions satisfied return true; }
Zunächst wird geprüft, ob der mittlere Balken das tiefste Tief besitzt. Dann wird überprüft, ob der mittlere Balken nicht ein „Outside-Balken“ im Verhältnis zum vorherigen ist. Schließlich wird bestätigt, dass der jüngste Balken kein „Inside-Balken“ im Verhältnis zum Umkehr-Balken ist.
Nur wenn alle Bedingungen erfüllt sind, gibt die Funktion true zurück.
Auswahl des aktiven Experiments
Um das Testen zu vereinfachen, führen wir eine Nutzereingabe ein, mit der der Händler auswählen kann, welcher nicht zufällige Testmodus ausgeführt werden soll. Diese Eingabe verwendet die zuvor definierte Enumeration und erscheint als Dropdown, wenn der EA mit einem Chart verbunden ist.
//+------------------------------------------------------------------+ //| User input variables | //+------------------------------------------------------------------+ ... input group "Trade And Risk Management" input ENUM_NON_RANDOM_TEST_MODE nonRandomTestMode = TEST_OPEN_TO_CLOSE_BIAS;
Dieser Ansatz ermöglicht es uns, denselben EA für alle Experimente wiederzuverwenden, ohne den Code jedes Mal ändern zu müssen.
Überprüfung der Mustererkennung in OnTick
Nachdem alle Erkennungslogik vorhanden ist, verbinden wir nun alles innerhalb der Tick-Funktion.
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick(){ ... //--- Run this block only when a new bar is detected on the selected timeframe if(IsNewBar(_Symbol, timeframe, lastBarOpenTime)){ //--- Execute a buy trade when the Open-to-Close bias test mode is selected if(nonRandomTestMode == TEST_OPEN_TO_CLOSE_BIAS ){ Print("EA should open a long position"); } //--- Enter a buy position to test bullish bias following a single down close if(nonRandomTestMode == TEST_AFTER_ONE_DOWN_CLOSE ){ if(IsConsecutiveBarCloseState(_Symbol, timeframe, 1, BAR_CLOSE_DOWN)){ Print("Previous bearish bar"); } } //--- Enter a buy position to test bullish bias following two consecutive down closes if(nonRandomTestMode == TEST_AFTER_TWO_DOWN_CLOSES ){ if(IsConsecutiveBarCloseState(_Symbol, timeframe, 2, BAR_CLOSE_DOWN)){ Print("Two consecutive bearish bars"); } } //--- Enter a buy position to test bullish bias following three consecutive down closes if(nonRandomTestMode == TEST_AFTER_THREE_DOWN_CLOSES){ if(IsConsecutiveBarCloseState(_Symbol, timeframe, 3, BAR_CLOSE_DOWN)){ Print("Three consecutive bearish bars"); } } //--- Enter a buy position to test bullish bias following a single up close if(nonRandomTestMode == TEST_AFTER_ONE_UP_CLOSE ){ if(IsConsecutiveBarCloseState(_Symbol, timeframe, 1, BAR_CLOSE_UP)){ Print("Previous bullish bar"); } } //--- Enter a buy position to test bullish bias following two consecutive up closes if(nonRandomTestMode == TEST_AFTER_TWO_UP_CLOSES ){ if(IsConsecutiveBarCloseState(_Symbol, timeframe, 2, BAR_CLOSE_UP)){ Print("Two consecutive bullish bars"); } } //--- Enter a buy position to test bullish bias following three consecutive up closes if(nonRandomTestMode == TEST_AFTER_THREE_UP_CLOSES ){ if(IsConsecutiveBarCloseState(_Symbol, timeframe, 3, BAR_CLOSE_UP)){ Print("Three consecutive bullish bars"); } } //--- Enter a buy position when a Larry Williams-defined short-term low pattern is detected if(nonRandomTestMode == TEST_AFTER_SHORT_TERM_LOW ){ if(IsLarryWilliamsShortTermLow(_Symbol, timeframe)){ Print("Short-term low"); } } } }
Der EA prüft zunächst, ob sich ein neuer Balken gebildet hat. Wenn nicht, passiert nichts. Wenn ein neuer Balken erkannt wird, wertet der EA nur den ausgewählten Testmodus aus. Für jeden Fall ruft es die entsprechende Erkennungsfunktion auf und gibt eine Meldung aus, wenn ein Muster gefunden wird.
In diesem Stadium werden keine Handelsgeschäfte getätigt. Die gedruckten Meldungen dienen als Bestätigungssignal. Sie ermöglichen es uns, visuell zu überprüfen, ob die Muster zum richtigen Zeitpunkt und unter den richtigen Bedingungen erkannt werden.
Dieser Schritt ist entscheidend. Bevor wir eine echte Handelslogik hinzufügen, müssen wir sicherstellen, dass unsere Mustererkennung genau ist. Ein Aufbau auf einer falschen Logik würde alle nachfolgenden Ergebnisse ungültig machen.
Im nächsten Abschnitt werden wir diese Print-Anweisungen durch die tatsächliche Logik der Handelsausführung ersetzen. An diesem Punkt wird unser EA von der Beobachtung zum Experiment übergehen, sodass wir testen können, ob sich diese nicht zufälligen Verhaltensweisen in realen Marktergebnissen niederschlagen können.
In diesem Stadium weiß unser EA bereits, wie er alle Muster erkennen kann, die für die nicht zufälligen Markttests erforderlich sind. Der nächste Schritt besteht darin, diese Signale in reale Marktaktionen umzusetzen. Hier wird das Beobachten zum Experimentieren.
Um mit der ursprünglichen Logik von Larry Williams übereinzustimmen, wird unser EA nur Käufe tätigen. Jeder Handel wird zu Beginn eines neuen Balkens eröffnet und am Ende dieses Balkens wieder geschlossen. Es gibt keine Stop-Loss- oder Take-Profit-Niveaus. Das Ziel ist nicht die Verwaltung des Handels, sondern das Messen. Wir wollen beobachten, wie sich der Preis unmittelbar nach Eintritt einer bestimmten Bedingung verhält.
Aufgrund dieses Konzepts muss der EA zuverlässig drei Dinge machen können. Zunächst muss er wissen, ob er bereits eine offene Position hat. Zweitens muss er in der Lage sein, diese Position sauber zu schließen. Drittens darf er nur dann eine neue Position eröffnen, wenn die ausgewählte Testbedingung erfüllt ist.
Prüfen auf eine aktive Position
Bevor der EA ein neues Handelsgeschäft eröffnet, muss er überprüfen, ob er bereits eine aktive Kaufposition hat. Da mehrere EAs oder manuelle Trades auf demselben Konto existieren können, verwenden wir eine magische Zahl, um die von diesem EA eröffneten Handelsgeschäfte eindeutig zu identifizieren.
Zu diesem Zweck definieren wir eine Funktion, die alle offenen Positionen durchsucht und prüft, ob sie eine passende magische Zahl haben. Wenn eine solche Position existiert, gibt die Funktion true zurück. Andernfalls wird false zurückgegeben.
//+------------------------------------------------------------------+ //| To verify whether this EA currently has an active buy position. | | //+------------------------------------------------------------------+ bool IsThereAnActiveBuyPosition(ulong magic){ for(int i = PositionsTotal() - 1; i >= 0; i--){ ulong ticket = PositionGetTicket(i); if(ticket == 0){ Print("Error while fetching position ticket ", _LastError); continue; }else{ if(PositionGetInteger(POSITION_MAGIC) == magic && PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY){ return true; } } } return false; }
Diese Kontrolle ermöglicht es dem EA, diszipliniert zu bleiben. Zu einem bestimmten Zeitpunkt kann es nur eine aktive Position pro Test geben. So bleiben die Ergebnisse sauber und überlappende Handelsgeschäfte, die die Statistik verzerren würden, werden vermieden.
Schließen einer bestehenden Position
Sobald sich ein neuer Balken bildet, ist der vorherige Balken offiziell geschlossen. Dieser Moment stellt das Ende unseres Testfensters für das vorherige Handelsgeschäft dar. Wenn eine Position noch offen ist, muss sie sofort geschlossen werden.
Um dies zu erreichen, definieren wir eine Funktion, die alle offenen Positionen durchläuft und die zu diesem EA gehörenden Positionen auf der Grundlage der magischen Zahl schließt. Die Funktion versucht nicht, Gewinne oder Verluste zu verwalten. Sie steigt einfach zum Marktpreis aus dem Handel aus.
//+------------------------------------------------------------------+ //| To close all position with a specified magic number | //+------------------------------------------------------------------+ void ClosePositionsByMagic(ulong magic) { for (int i = PositionsTotal() - 1; i >= 0; i--) { ulong ticket = PositionGetTicket(i); if (PositionSelectByTicket(ticket)) { if (PositionGetInteger(POSITION_MAGIC) == magic) { ulong positionType = PositionGetInteger(POSITION_TYPE); double volume = PositionGetDouble(POSITION_VOLUME); if (positionType == POSITION_TYPE_BUY) { Trade.PositionClose(ticket); } else if (positionType == POSITION_TYPE_SELL) { Trade.PositionClose(ticket); } } } } }
Auf diese Weise wird sichergestellt, dass jeder Test den gleichen Regeln folgt. Ein Balken rein, ein Balken raus. Mehr nicht.
Eröffnen einer neuen Kaufposition
Wenn die Positionskontrolle eingerichtet ist, definieren wir eine einfache Funktion zur Eröffnung eines Marktkaufauftrags. Die Funktion benötigt zwei Eingaben. Der erste ist der Einstiegskurs, d. h. der aktuelle Briefkurs, den der EA bereits verfolgt. Die zweite ist die Positionsgröße.
//+------------------------------------------------------------------+ //| Function to open a market buy position | //+------------------------------------------------------------------+ bool OpenBuy(double entryPrice, double lotSize){ if(!Trade.Buy(NormalizeDouble(lotSize, 2), _Symbol, entryPrice)){ Print("Error while executing a market buy order: ", GetLastError()); Print(Trade.ResultRetcode()); Print(Trade.ResultComment()); return false; } return true; }
Wenn der Handel aus irgendeinem Grund fehlschlägt, druckt die Funktion Diagnoseinformationen aus. Andernfalls wird true zurückgegeben, um zu bestätigen, dass die Bestellung erfolgreich aufgegeben wurde.
Um dies flexibel zu gestalten, erlauben wir dem Nutzer, seine bevorzugte Losgröße über einen Eingabeparameter zu definieren. Dadurch bleibt der EA über verschiedene Kontogrößen hinweg verwendbar, während die Logik des Experiments erhalten bleibt.
//+------------------------------------------------------------------+ //| User input variables | //+------------------------------------------------------------------+ ... input double positionSize = 0.01;
Ausführen der Logik innerhalb von OnTick
Jetzt kommt alles zusammen.
Jedes Mal, wenn ein neuer Balken erkannt wird, führt der EA die folgenden Schritte der Reihe nach durch. Zunächst schließt er jede bestehende Position, die von diesem EA eröffnet wurde. Dies markiert das Ende des vorherigen Prüfzyklus. Es wird eine kurze Pause eingefügt, um sicherzustellen, dass die Plattform den Abschluss sauber verarbeitet.
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick(){ ... //--- Run this block only when a new bar is detected on the selected timeframe if(IsNewBar(_Symbol, timeframe, lastBarOpenTime)){ //--- Close any existing buy positions for this EA before opening a new one if(IsThereAnActiveBuyPosition(magicNumber)){ ClosePositionsByMagic(magicNumber); Sleep(100); } ... } }
Als Nächstes prüft der EA, welcher Testmodus gerade ausgewählt ist. Auf der Grundlage dieser Auswahl wird die entsprechende Bedingung ausgewertet. Wenn die Bedingung erfüllt ist, wird sofort zu Beginn des neuen Balkens eine neue Kaufposition eröffnet.
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick(){ ... //--- Run this block only when a new bar is detected on the selected timeframe if(IsNewBar(_Symbol, timeframe, lastBarOpenTime)){ ... //--- Execute a buy trade when the Open-to-Close bias test mode is selected if(nonRandomTestMode == TEST_OPEN_TO_CLOSE_BIAS ){ OpenBuy(askPrice, positionSize); } //--- Enter a buy position to test bullish bias following a single down close if(nonRandomTestMode == TEST_AFTER_ONE_DOWN_CLOSE ){ if(IsConsecutiveBarCloseState(_Symbol, timeframe, 1, BAR_CLOSE_DOWN)){ OpenBuy(askPrice, positionSize); } } //--- Enter a buy position to test bullish bias following two consecutive down closes if(nonRandomTestMode == TEST_AFTER_TWO_DOWN_CLOSES ){ if(IsConsecutiveBarCloseState(_Symbol, timeframe, 2, BAR_CLOSE_DOWN)){ OpenBuy(askPrice, positionSize); } } //--- Enter a buy position to test bullish bias following three consecutive down closes if(nonRandomTestMode == TEST_AFTER_THREE_DOWN_CLOSES){ if(IsConsecutiveBarCloseState(_Symbol, timeframe, 3, BAR_CLOSE_DOWN)){ OpenBuy(askPrice, positionSize); } } //--- Enter a buy position to test bullish bias following a single up close if(nonRandomTestMode == TEST_AFTER_ONE_UP_CLOSE ){ if(IsConsecutiveBarCloseState(_Symbol, timeframe, 1, BAR_CLOSE_UP)){ OpenBuy(askPrice, positionSize); } } //--- Enter a buy position to test bullish bias following two consecutive up closes if(nonRandomTestMode == TEST_AFTER_TWO_UP_CLOSES ){ if(IsConsecutiveBarCloseState(_Symbol, timeframe, 2, BAR_CLOSE_UP)){ OpenBuy(askPrice, positionSize); } } //--- Enter a buy position to test bullish bias following three consecutive up closes if(nonRandomTestMode == TEST_AFTER_THREE_UP_CLOSES ){ if(IsConsecutiveBarCloseState(_Symbol, timeframe, 3, BAR_CLOSE_UP)){ OpenBuy(askPrice, positionSize); } } //--- Enter a buy position when a Larry Williams-defined short-term low pattern is detected if(nonRandomTestMode == TEST_AFTER_SHORT_TERM_LOW ){ if(IsLarryWilliamsShortTermLow(_Symbol, timeframe)){ OpenBuy(askPrice, positionSize); } } } }
Die einzige Änderung gegenüber unserer früheren Testphase ist einfach, aber wichtig. Wir haben alle Print-Anweisungen durch tatsächliche Aufforderungen zur Handelsausführung ersetzt. Die Erkennungslogik bleibt unverändert. Dies bestätigt, dass unsere früheren Validierungsarbeiten korrekt waren.
Zu diesem Zeitpunkt ist der EA voll einsatzfähig. Es erkennt Muster, eröffnet Handelsgeschäfte, schließt sie nach einem Balken und wiederholt den Prozess konsistent für alle Testfälle.
Konfigurieren des Charts für eindeutige visuelle Tests
Da dieser Expert Advisor für visuelle und statistische Experimente konzipiert ist, ist es wichtig, dass wir klar sehen können, was auf dem Chart passiert, während die Tests laufen. Wir wollen Auf- und Abwärtsbalken sofort erkennen, Handelsein- und -ausstiege identifizieren und unnötige visuelle Unordnung vermeiden, die von der Beobachtung ablenken könnte.
Aus diesem Grund führen wir eine kleine Hilfsfunktion ein, die das Erscheinungsbild des Charts konfiguriert, sobald der EA angehängt wird.
//+------------------------------------------------------------------+ //| This function configures the chart's appearance. | //+------------------------------------------------------------------+ bool ConfigureChartAppearance() { if(!ChartSetInteger(0, CHART_COLOR_BACKGROUND, clrWhite)){ Print("Error while setting chart background, ", GetLastError()); return false; } if(!ChartSetInteger(0, CHART_SHOW_GRID, false)){ Print("Error while setting chart grid, ", GetLastError()); return false; } if(!ChartSetInteger(0, CHART_MODE, CHART_CANDLES)){ Print("Error while setting chart mode, ", GetLastError()); return false; } if(!ChartSetInteger(0, CHART_COLOR_FOREGROUND, clrBlack)){ Print("Error while setting chart foreground, ", GetLastError()); return false; } if(!ChartSetInteger(0, CHART_COLOR_CANDLE_BULL, clrSeaGreen)){ Print("Error while setting bullish candles color, ", GetLastError()); return false; } if(!ChartSetInteger(0, CHART_COLOR_CANDLE_BEAR, clrBlack)){ Print("Error while setting bearish candles color, ", GetLastError()); return false; } if(!ChartSetInteger(0, CHART_COLOR_CHART_UP, clrSeaGreen)){ Print("Error while setting bearish candles color, ", GetLastError()); return false; } if(!ChartSetInteger(0, CHART_COLOR_CHART_DOWN, clrBlack)){ Print("Error while setting bearish candles color, ", GetLastError()); return false; } return true; }
Beim Experimentieren spielt das visuelle Feedback eine wichtige Rolle. Auch wenn unsere Schlussfolgerungen letztlich auf Daten und Statistiken beruhen, hilft es, das Preisverhalten unter den einzelnen Testbedingungen zu beobachten, um Annahmen zu überprüfen und Anomalien frühzeitig zu erkennen.
Mit der von dieser Funktion angewandten Konfiguration werden drei Hauptziele erreicht:
- Klarheit – Ein weißer Hintergrund mit schwarzen Vordergrundelementen sorgt für maximalen Kontrast.
- Richtungsbetonung – Aufwärtsbalken werden durch moderne, intuitive Farben klar von Abwärtsbalken unterschieden.
- Fokus – Die Gitternetzlinien werden entfernt, um die Aufmerksamkeit auf die Preisbewegung und die ausgeführten Handelsgeschäfte zu lenken.
Die Chartkonfigurationsfunktion wird innerhalb der Funktion OnInit aufgerufen.
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit(){ ... //--- To configure the chart's appearance if(!ConfigureChartAppearance()){ Print("Error while configuring chart appearance", GetLastError()); return INIT_FAILED; } return(INIT_SUCCEEDED); }
Dadurch wird sichergestellt, dass der Chart sofort angepasst wird, wenn der EA läuft. Wenn die Plattform aus irgendeinem Grund eine Einstellung nicht übernehmen kann, meldet der EA das Problem und bricht die Initialisierung ab.
Mit diesem Zusatz ist die Entwicklung unseres Test-EAs abgeschlossen. Im nächsten Abschnitt werden wir damit beginnen, diese Experimente auf verschiedenen Märkten durchzuführen. Wir werden die Ergebnisse beobachten, die Ergebnisse verschiedener Anlageklassen vergleichen und schließlich die Frage beantworten, mit der diese Reise begann. Sind Märkte wirklich zufällig, oder hinterlassen sie messbare Spuren?
Testdurchführung und Beobachtungen des Marktverhaltens
Bis jetzt haben wir uns auf die Ideen und die Vorbereitung konzentriert. Wir haben Larry Williams' Argument, dass das Preisverhalten nicht komplett zufällig ist, untersucht, diese Ideen in klar definierte Testfälle umgesetzt und einen Expert Advisor entwickelt, der in der Lage ist, diese Tests konsistent auszuführen.
Der EA ist absichtlich einfach gehalten. Er ist nicht optimiert, hat weder Stop-Loss noch Take-Profit und verwaltet die Handelsgeschäfte nicht dynamisch. Jede Position wird zu Beginn eines neuen Balkens eröffnet und am Ende desselben Balkens wieder geschlossen. Es kann jeweils nur ein Handel geöffnet sein, und jeder Test läuft unabhängig von den anderen.
Unser Ziel ist es nicht, ein profitables Handelssystem zu entwickeln, und auch nicht, die Ein- und Ausstiege oder das Risikomanagement zu optimieren. Stattdessen wollen wir eine viel einfachere und grundsätzlichere Frage beantworten:
Treten bestimmte Preisverhaltensweisen häufiger auf, als es der Zufall vermuten ließe, und können diese Verhaltensweisen auf verschiedenen Märkten konsistent beobachtet werden?
Testumgebung
Um sicherzustellen, dass die Ergebnisse unserer Versuche konsistent, vergleichbar und reproduzierbar sind, werden alle Tests unter denselben festen Bedingungen durchgeführt. Es werden keine Parameter von einem Markt zum anderen angepasst, und es findet in keiner Phase eine Optimierung statt.
Alle Experimente werden mit dem Zeitrahmen Daily (D1) durchgeführt. Die Verwendung eines größeren Zeitrahmens trägt dazu bei, das Rauschen der Marktmikrostruktur zu reduzieren, und stellt sicher, dass jeder Handel eine vollständige Handelssitzung und nicht nur kurzfristige Kursschwankungen widerspiegelt.
Der Testzeitraum erstreckt sich vom 01.01.2025 bis zum 30.11.2025 für jedes Finanzinstrument. Dieser identische Datumsbereich ermöglicht es uns, das Verhalten verschiedener Märkte zu vergleichen, ohne eine zeitliche Verzerrung zu verursachen.
Bei allen Versuchen wird eine feste Losgröße (0,1) verwendet. Der EA eröffnet jeweils nur ein Handelsgeschäft, und jedes wird für genau einen Balken gehalten. Es gibt keine Pyramidierung, Skalierung oder Überschneidung von Positionen. Dadurch wird sichergestellt, dass jedes Ergebnis unabhängig ist und direkt auf die zu bewertende Testbedingung zurückzuführen ist.
Alle Tests beginnen mit dem gleichen Startguthaben von 10.000 USD und verwenden einen Hebel von 1:500. Die Handelsregeln, die Ausführungslogik und das Exit-Verhalten bleiben über alle Märkte und Testfälle hinweg unverändert. Dieser einheitliche Aufbau ist entscheidend für die Isolierung des Marktverhaltens und nicht für die Strategieabstimmung.
Um die Replikation zu vereinfachen, wird eine Konfigurationsdatei (configurations.ini) bereitgestellt. Wenn Sie den EA in dem Strategie-Tester des MetaTrader 5 laufen lassen, kann diese Datei über die Registerkarte „Eingaben“ geladen werden, indem Sie auf „Laden“ klicken und die bereitgestellte .ini-Datei auswählen. Dabei werden die gleichen Versuchsparameter wie bei diesen Experimenten verwendet, sodass die Leser die gleichen Bedingungen vorfinden.
Finanztitel im Test
Um zu beurteilen, wie das nicht zufällige Marktverhalten in den verschiedenen Markttypen aussieht, führen wir alle Tests mit den folgenden Finanzinstrumenten durch:
- XAUUSD – Ein Rohstoff (Gold)
- BTCUSD – Eine Kryptowährung (Bitcoin)
- GBPUSD – Ein Währungspaar (Britisches Pfund vs. US Dollar)
- NAS100 – Ein Aktienindex (Nasdaq 100)
Diese Instrumente wurden bewusst aus verschiedenen, weitgehend unverbundenen Anlageklassen ausgewählt. Jeder Markt funktioniert unter anderen Bedingungen hinsichtlich Liquidität, Teilnehmerverhalten, Handelszeiten und Empfindlichkeit gegenüber Nachrichten und Stimmungen.
Indem wir dieselbe Testlogik auf Rohstoffe, Kryptowährungen, Währungen und Aktienindizes anwenden, können wir feststellen, ob das festgestellte nicht zufällige Verhalten spezifisch für bestimmte Märkte ist oder durchgängig in allen Anlageklassen auftritt.
Dieser Vergleich ist wichtig, um Marktstruktureffekte von instrumentenspezifischen Merkmalen zu trennen. Dies bildet die Grundlage für die folgenden Testergebnisse, bei denen jedes Instrument unabhängig voneinander nach den gleichen Regeln bewertet wird.
Experimente durchführen und Ergebnisse aufzeichnen
Um die Datenintegrität zu wahren und eine gegenseitige Beeinflussung der Ergebnisse zu verhindern, wird jeder Testfall unabhängig durchgeführt. Die Ergebnisse jedes Versuchs werden in einer eigenen Tabelle dokumentiert, um ein Höchstmaß an Übersichtlichkeit zu gewährleisten. Für jedes Experiment werden wir die folgenden Felder in jeder Tabelle erfassen:- Instrument – Der zu testende Finanzmarkt (XAUUSD, BTCUSD, GBPUSD, NAS100).
- Trade Count – Die Gesamtzahl der von diesem Szenario während des Testzeitraums ausgelösten Handelsgeschäfte.
- Win Rate (%) – Der Prozentsatz der Handelsgeschäfte, die mit Gewinn abgeschlossen wurden.
- Net Result – Eine Markierung, die anzeigt, ob das Gesamtergebnis des Handels positiv (mit Gewinn) oder negativ (mit Verlust) war.
Dieser strukturierte Ansatz ermöglicht es uns, die Ergebnisse über Märkte und Szenarien hinweg zu vergleichen und gleichzeitig die Daten sauber zu halten und leicht zu analysieren.
Versuch 1: Bias von „Open-to-Close“
Der Bias-Test von „Open-to-Close“ prüft, ob Kerzen dazu neigen, höher oder niedriger als ihr Eröffnungskurs zu schließen. Dieser Test ist das grundlegendste Szenario und gibt uns einen Einblick in die grundlegenden Richtungstendenzen eines jeden Marktes während des gewählten Zeitrahmens.
| Trade Count | Win Rate (%) | Net Results | |
|---|---|---|---|
| XAUUSD | 236 | 56.36 | Gewinn |
| BTCUSD | 331 | 51.06 | Verlust |
| GBPUSD | 238 | 45.38 | Verlust |
| NASDAQ | 236 | 55.93 | Gewinn |
Versuch 2: Aufwärts-Bias nach einem tieferen Schlusskurs
Mit diesem Test wird untersucht, ob eine Aufwärtskerze mit größerer Wahrscheinlichkeit auf eine einzelne Abwärtskerze folgen wird. Es hilft uns zu verstehen, ob der Markt dazu neigt, nach einem einzelnen Abwärtsbalken eine Umkehr zu vollziehen. Der EA prüft, ob eine Aufwärtskerze auf eine Abwärtskerze folgt.
| Trade Count | Win Rate (%) | Net Results | |
|---|---|---|---|
| XAUUSD | 101 | 62.38 | Gewinn |
| BTCUSD | 161 | 53.42 | Gewinn |
| GBPUSD | 106 | 46.23 | Verlust |
| NASDAQ | 106 | 62.26 | Gewinn |
Experiment 3: Aufwärts-Bias nach zwei aufeinanderfolgenden niedrigeren Schlusskursen
Mit diesem Test wird untersucht, ob zwei aufeinanderfolgende Abwärtskerzen die Wahrscheinlichkeit einer Aufwärtsumkehr bei der nächsten Kerze erhöhen.
| Trade Count | Win Rate (%) | Net Results | |
|---|---|---|---|
| XAUUSD | 35 | 57.14 | Gewinn |
| BTCUSD | 73 | 50.68 | Gewinn |
| GBPUSD | 47 | 44.68 | Verlust |
| NASDAQ | 40 | 57.50 | Verlust |
Versuch 4: Aufwärts-Bias nach drei aufeinanderfolgenden niedrigeren Schlusskursen
Dieser Test erweitert den vorherigen auf drei aufeinanderfolgende Abwärtskerzen und prüft, ob ein stärkerer Abwärtstrend die Wahrscheinlichkeit einer Umkehr nach oben erhöht.
| Trade Count | Win Rate (%) | Net Results | |
|---|---|---|---|
| XAUUSD | 14 | 71.43 | Gewinn |
| BTCUSD | 35 | 60.00 | Gewinn |
| GBPUSD | 20 | 35.00 | Verlust |
| NASDAQ | 19 | 52.63 | Gewinn |
Versuch 5: Aufwärts-Bias nach einem höheren Schlusskurs
Bei diesem Test wird geprüft, ob auf eine Aufwärtskerze eine weitere Aufwärtskerze folgt, wodurch kurzfristige Tendenzen des Momentums aufgedeckt werden können.
| Trade Count | Win Rate (%) | Net Results | |
|---|---|---|---|
| XAUUSD | 135 | 51.85 | Gewinn |
| BTCUSD | 170 | 48.82 | Verlust |
| GBPUSD | 131 | 45.04 | Verlust |
| NASDAQ | 130 | 50.77 | Verlust |
Experiment 6: Aufwärts-Bias nach zwei aufeinanderfolgenden höheren Schlusskursen
Bei diesem Test wird untersucht, ob sich das Momentum nach zwei aufeinanderfolgenden Aufwärtskerzen verstärkt und ob eine dritte Kerze wahrscheinlich folgen wird.
| Trade Count | Win Rate (%) | Net Results | |
|---|---|---|---|
| XAUUSD | 69 | 53.62 | Gewinn |
| BTCUSD | 82 | 43.90 | Verlust |
| GBPUSD | 72 | 41.67 | Verlust |
| NASDAQ | 64 | 54.69 | Verlust |
Experiment 7: Aufwärts-Bias nach drei aufeinanderfolgenden höheren Schlusskursen
Mit diesem Test wird untersucht, ob drei aufeinanderfolgende Aufwärtskerzen die Wahrscheinlichkeit einer vierten Kerze erhöhen, die ein stärkeres Momentum erkennen lässt.
| Trade Count | Win Rate (%) | Net Results | |
|---|---|---|---|
| XAUUSD | 37 | 37.84 | Verlust |
| BTCUSD | 34 | 52.94 | Gewinn |
| GBPUSD | 37 | 40.54 | Verlust |
| NASDAQ | 34 | 50.00 | Verlust |
Versuch 8: Aufwärts-Bias nach einem Larry-Williams-Kurzzeittief
Dieser Test prüft, ob eine Aufwärtskerze wahrscheinlich auf einen kurzfristigen, tiefen Umkehrpunkt folgt, wie er von Larry Williams definiert wurde. Er kombiniert Preisaktionen und Analysen der Umkehrpunkte für ein potenziell prognostizierbares Verhalten.
| Trade Count | Win Rate (%) | Net Results | |
|---|---|---|---|
| XAUUSD | 21 | 61.90 | Gewinn |
| BTCUSD | 26 | 53.85 | Gewinn |
| GBPUSD | 23 | 65.22 | Verlust |
| NASDAQ | 29 | 62.07 | Gewinn |
Beweise für nicht zufälliges Marktverhalten
Dieser Abschnitt fasst alle acht Experimente zusammen und konzentriert sich auf das, was wirklich zählt – die beobachteten Gewinnquoten. Wären die Märkte rein zufällig, würden sich steigende und fallende Schlusskurse gleichmäßig um 50 % verteilen, unabhängig von den vorherigen Bedingungen. Was wir hier beobachten, ist anders.
Bei mehreren Instrumenten und mehreren Testfällen ändert sich das Preisverhalten, je nachdem, was vorher geschehen ist. Dies allein stellt die Annahme in Frage, dass jede Kerze ein unabhängiges Ereignis ohne Erinnerung ist.
1. Das Bias von „Open-to-Close“ ist nicht einheitlich
Der Test von „Open-to-Close“ dient der Festlegung einer Basislinie. Wären die Märkte zufällig, müssten sich die Ergebnisse für alle Instrumente eng um die fünfzig Prozent anordnen. Stattdessen sehen wir Unterschiede. Gold und Nasdaq weisen Gewinnquoten von über fünfundfünfzig Prozent auf, beide enden mit einem Gewinn. Bitcoin bleibt in der Nähe des Zufalls und wird unrentabel. GBPUSD fällt deutlich unter fünfzig Prozent und schneidet schlecht ab. Dies sagt uns etwas Wichtiges. Selbst das grundlegendste Preisverhalten ist nicht auf allen Märkten identisch: Struktur und Teilnehmerverhalten spielen eine Rolle.
2. Tiefere Schlusskurse zeigen ein stärkeres Vorhersageverhalten
Die wichtigsten Beweise gegen den Zufall treten nach Abwärtskerzen auf. Nach einer Abwärtskerze weisen Gold und Nasdaq Gewinnquoten von über sechzig Prozent auf. Bitcoin bewegt sich über fünfzig Prozent und erzielt einen Gewinn. GBPUSD´bleibt schwach.
Nach zwei aufeinanderfolgenden Abwärtsbewegungen liegen die Gewinnquoten von Gold und Bitcoin immer noch über fünfzig Prozent. Auch wenn die Nasdaq an Rentabilität verliert, bleibt die Gewinnquote hoch.
Nach drei aufeinanderfolgenden Schließungen nach unten wird der Effekt deutlicher. Gold erreicht über siebzig Prozent. Bitcoin bleibt stark. Nasdaq bleibt über fünfzig Prozent. Nur der GBPUSD scheitert weiterhin.
Diese Progression ist wichtig. In einem zufälligen System sollte eine Erhöhung der Anzahl der vorangegangenen Abwärtskerzen die Wahrscheinlichkeit einer Hausse nicht erhöhen. Hier ist das der Fall, insbesondere bei Gold und Bitcoin.
Das ist kein Rauschen. Dies ist ein bedingtes Verhalten.
3. Aufwärtskerzen verhalten sich sehr unterschiedlich
Die gleiche Logik, angewandt auf Aufwärtssequenzen, führt zu schwächeren, weniger konsistenten Ergebnissen.
Die meisten Instrumente fallen nach einer Aufwärtskerze unter fünfzig Prozent. Nach zwei Aufwärtskerzen hat nur noch Gold einen leichten Vorsprung. Nach drei Aufwärtskorrekturen verschlechtern sich die Ergebnisse weiter.
Diese Asymmetrie ist entscheidend. Die Märkte reagieren auf Schwäche anders als auf Stärke. Die mittlere Umkehrung scheint nach Rückgängen stärker zu sein als die Fortsetzung nach Anstiegen.
Zufällige Systeme weisen keine Asymmetrie auf.
4. Kurzfristige Tiefs bestätigen strukturelles Verhalten
Der Test auf kurzzeitige Tiefs liefert einige der beständigsten Ergebnisse.
Gold, Bitcoin und der Nasdaq weisen allesamt Gewinnquoten von über 60 % auf und erzielten einen Gewinn. Sogar GBPUSD hat eine hohe Gewinnrate, obwohl es ihm nicht gelingt, diese Gewinne in Profit umzuwandeln.
Dies bestätigt, dass einfache strukturelle Muster wichtig sind. Ein gut definiertes kurzfristiges Tief verändert die Wahrscheinlichkeit der nächsten Kerze.
Dies untermauert direkt das Argument von Larry Williams, dass sich der Preis nicht blindlings von Balken zu Balken bewegt.
5. Die Unterschiede zwischen den Anlageklassen stärken die Argumente
Die Ergebnisse variieren je nach Instrument, aber die Variation selbst ist aussagekräftig.
Gold zeigt bei mehreren Tests durchweg die deutlichste Aufwärtstendenz. Bitcoin zeigt ein bedingtes Verhalten, das sich nach Rückgängen verstärkt. Der Nasdaq reagiert gut auf kurzfristige Schwäche und Struktur. GBPUSD bleibt nach diesen Regeln weitgehend ineffizient.
Wären die Märkte zufällig, würden die Anlageklassen keine Rolle spielen. Das ist aber eindeutig nicht so.
Letzte Einsicht
Diese Experimente erheben keinen Anspruch auf Gewissheit. Sie versprechen keine Vorhersage. Was sie zeigen, ist eine Wahrscheinlichkeitsverzerrung.
Unter bestimmten Bedingungen liegen die Chancen nicht bei fünfzig Prozent. Das allein widerlegt den reinen Zufall.
Die Märkte sind nicht vollständig vorhersehbar. Aber auch sie sind nicht gedächtnislos. Der Preis reagiert in messbarer Weise auf früheres Verhalten. Das ist die Grundlage des Vorteils.
Dies ist genau das, was Larry Williams vorgeschlagen hat. Außerdem können wir uns durch Automatisierung, Daten und kontrollierte Tests jetzt selbst davon überzeugen.
Schlussfolgerung
Dieser Artikel soll eine einfache, aber wichtige Frage beantworten. Bewegen sich die Märkte rein zufällig, oder beeinflusst die vergangene Kursentwicklung das, was als Nächstes kommt?
Durch kontrollierte Tests, Automatisierung und reale Marktdaten haben wir gezeigt, dass das Preisverhalten nicht gleichmäßig verteilt ist. Bestimmte Bedingungen verschieben die Wahrscheinlichkeiten immer weiter von 50 % weg. Das allein reicht schon aus, um die Idee eines gänzlich zufälligen Marktes zu verwerfen.
Die Ergebnisse lassen nicht auf Gewissheit oder perfekte Vorhersage schließen. Stattdessen enthüllten sie etwas Praktischeres. Die Märkte reagieren nach einer Schwäche anders als nach einer Stärke. Einige Strukturen wiederholen sich häufiger, als es der Zufall zulässt. Einige Instrumente zeigen diese Tendenzen deutlicher als andere. Dies sind keine Zufälle. Es sind messbare Verhaltensweisen.
Genauso wichtig wie die Ergebnisse ist das Verfahren, mit dem sie erzielt werden. Sie wurden Schritt für Schritt durch den Entwurf und die Entwicklung eines Expert Advisors für Tests geführt. Der EA war absichtlich einfach, transparent und modular aufgebaut. Es wurde gebaut, um Ideen zu testen, nicht um Gewinne zu optimieren. Dieser Ansatz ist bei der Suche nach echten Erkenntnissen unerlässlich.
Mit dieser Grundlage haben Sie nun einen funktionierenden Rahmen. Sie können die Logik ändern, neue Bedingungen hinzufügen, verschiedene Zeitrahmen testen oder andere Preismuster untersuchen. Dieselben Techniken können wiederverwendet werden, um Forschungsinstrumente, Handelssysteme oder Strategiekomponenten zu entwickeln, die auf eindeutigen Beweisen und nicht auf Annahmen beruhen.
Diese Schlussfolgerung unterstützt die Kernidee von Larry Williams. Die Märkte sind nicht gänzlich vorhersehbar, aber sie sind auch nicht gedächtnislos. Wenn dies verstanden wird, wird die Systementwicklung sinnvoll.
In den folgenden Teilen dieser Serie werden wir auf diesen Erkenntnissen aufbauen und mit der Konstruktion von Handelssystemen beginnen, die darauf abzielen, diese nicht zufälligen Verhaltensweisen auf strukturierte, wiederholbare Weise zu erfassen.
Um Ihnen die Arbeit zu erleichtern, sind die folgenden Dateien angehängt, damit Sie die Experimente genau nachvollziehen oder reproduzieren können.
| Dateiname | Beschreibung | |
|---|---|---|
| 1. | larryWilliamsNonRandomMarketBehaviorTester.mq5 | Der vollständige Quellcode des in diesem Artikel entwickelten Expert Advisors. |
| 2. | configurations.ini | Die Konfigurationsdatei, die für alle Strategieprüfungsläufe verwendet wird. |
Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/20510
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.
Datenwissenschaft und ML (Teil 47): Marktprognosen mithilfe des DeepAR-Modells in Python
Erstellen von nutzerdefinierten Indikatoren in MQL5 (Teil 1): Erstellen eines Pivot-basierten Trendindikators mit Canvas-Gradient
Eine alternative Log-datei mit der Verwendung der HTML und CSS
Klassische Strategien neu interpretiert (Teil 20): Moderne stochastische Oszillatoren
- 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.