Larry Williams‘ Marktgeheimnisse (Teil 9): Mit Mustern zum Gewinn
Einführung
Die Märkte haben die Trader schon immer dazu eingeladen, nach der Bedeutung der Preise zu suchen. Im Laufe der Jahrzehnte wurden unzählige Chartmuster benannt, klassifiziert und gelehrt, als ob sie eine universelle Vorhersagekraft hätten. Schulter-Kopf-Schulter-Formationen, Dreiecke, Flaggen und unzählige Variationen haben die Art und Weise geprägt, wie viele Trader Charts interpretieren.
Larry Williams stellt diese Tradition infrage. Er lehnt Muster nicht völlig ab, aber er stellt ihren praktischen Wert infrage, wenn sie vage und subjektiv sind und sich über lange Zeiträume erstrecken. Klassische Formationen sind oft interpretationsbedürftig, hängen von Rückblicken ab und entwickeln sich über Zeiträume, in denen zu viele Variablen das Ergebnis beeinflussen können. Wenn ein langfristiges Muster abgeschlossen ist, kann sich das Marktumfeld bereits geändert haben.
Stattdessen konzentriert sich Larry Williams auf das kurzfristige Preisverhalten. Kurzfristige Muster bilden sich schnell, lösen sich schnell auf und können präzise gemessen werden. Sie sind aus einfachen Beziehungen zwischen Eröffnungen, Schließungen, Höchst- und Tiefstständen aufgebaut. Sie spiegeln eher unmittelbare Marktstimmung als langfristige Meinungen wider. Am wichtigsten ist, dass sie objektiv geprüft werden können. Diese Verlagerung von der visuellen Erzählung hin zu messbarem Verhalten ändert alles. Wenn die Muster klar definiert sind, können sie in Regeln umgewandelt werden. Wenn die Regeln präzise sind, können sie automatisiert werden. Wenn eine Automatisierung möglich ist, kann die Performance von Tausende Trades untersucht werden, anstatt sich nur an eine Handvoll Beispiele zu erinnern.
In diesem Artikel beginnen wir mit diesem Prozess. Wir nehmen einige der kurzfristigen Muster, die in seinem Buch Long-term Secrets to Short-term Trading vorgestellt werden, und verwandeln sie in einen forschungsbasierten Expert Advisor. Jedes Muster wird als unabhängige Strategie umgesetzt. Jede Strategie kann isoliert getestet werden. Jede Position wird nach einheitlichen Risiko- und Ausstiegsregeln verwaltet. Das Ziel besteht nicht darin, zu beweisen, dass ein einzelnes Muster perfekt ist, sondern darin zu verstehen, wie Struktur, Emotion, Zeit und Volatilität in realen Marktdaten zusammenwirken.
Dieser Artikel ist Teil einer fortlaufenden Serie. Gemeinsam entwickeln wir ein Framework, in dem Ideen nicht allein aufgrund ihres Rufs akzeptiert, sondern anhand von Beweisen bewertet werden, in dem Handelskonzepte nicht allein anhand von Charts bewundert, sondern anhand von Code gemessen werden und in dem Neugierde ganz natürlich zu Experimenten führt.
Von der Theorie zur Umsetzung, von den Mustern zum Gewinn: Beginnen wir nun mit dem Aufbau einer kurzfristigen Musterforschungsmaschine in MQL5.
Kurzfristige Muster für die Automatisierung ausgewählt
Warum diese Muster wichtig sind
Larry Williams hat nicht nach komplexen Formationen gesucht. Er suchte nach einfachen Ereignissen, die kurzfristige Emotionen und Erschöpfung widerspiegeln. Jedes hier beschriebene Muster bildet sich schnell, löst sich schnell auf und kann ohne Interpretation gemessen werden. Genau das macht sie für die Automatisierung geeignet.
In diesem Abschnitt beschreiben wir die Muster, die in unserem Expert Advisor implementiert werden. Jeder von ihnen vertritt eine andere Sichtweise des Marktverhaltens. Einige folgen der Dynamik. Einige handeln dagegen. Sie alle können objektiv geprüft werden. Wir wenden auch den Filter für den Handelstag der Woche auf jede Strategie an. So können wir untersuchen, wie sich das gleiche Muster an verschiedenen Wochentagen verhält.
Kaufen zum Eröffnungskurs
Nun, dies ist kein Muster, aber es existiert als Vergleichsbasis des Experiments. Zu Beginn eines jeden Handelstages erteilen wir einen Kaufauftrag ohne zusätzliche Bedingungen. Der Stop-Loss wird entweder auf dem Tief der vorherigen Bar oder in einem aus deren Handelsspanne abgeleiteten Abstand gesetzt. Das gewählte Ausstiegsmodell bestimmt den Take-Profit.
Mit diesem Muster wird nicht versucht, die Richtung vorherzusagen. Er misst lediglich, ob der Markt im Laufe der Zeit eine natürliche Aufwärtsneigung zeigt. Sie gibt uns einen neutralen Bezugspunkt, mit dem alle anderen Strategien verglichen werden können. Wenn selektivere Muster diese einfache Regel übertreffen, dann ist die zusätzliche Logik gerechtfertigt.
Kaufen nach einem gefallenen Schlusskurs
Mit diesem Muster wird der erste emotionale Filter eingeführt. Wir kaufen nur, wenn der Vortag tiefer schloss, als er eröffnete. Die Idee ist einfach. Ein einzelner Abwärtsschluss spiegelt häufig kurzfristigen Verkaufsdruck wider. In einem aufwärtsgerichteten Markt kann dies nicht mehr als eine kurze Pause bedeuten. Indem wir nach einem fallenden Schlusskurs kaufen, versuchen wir, während eines kleinen Rücksetzers einzusteigen und nicht bei Stärke. Ziel ist es, zu prüfen, ob eine leichte Schwäche die Einstiegsqualität verbessert.
Kaufen nach drei aufeinanderfolgenden tieferen Schlusskursen
Dieses Muster vertieft dieselbe Idee. Anstelle einer einzigen Abwärtsbar benötigen wir mehrere aufeinanderfolgende Abwärtsbars. Larry Williams stellte fest, dass kurze Verkaufsserien oft schnell erschöpft sind. Wenn drei oder mehr Abwärtsbewegungen hintereinander auftreten, steht der Markt möglicherweise bereits vor einem kurzfristigen Wendepunkt.
Hier kaufen wir erst nach einer bestimmten Anzahl von rückläufigen Bars. Dieses Muster prüft, ob wiederholte Schwäche eher eine Chance als eine Gefahr darstellt.
Kaufen nach einem objektiv gemessenen Rückschlag
Dies ist das am besten strukturierte Trendmuster. Zunächst bestätigen wir, dass der Kurs über dem Schlusskurs einer weit zurückliegenden Bar liegt. Damit ist ein Aufwärtstrend etabliert. Als Nächstes bestätigen wir, dass der Kurs unter dem Schlusskurs einer näheren Bar liegt. Damit wird ein kontrollierter Rücksetzer definiert. Wenn beide Bedingungen erfüllt sind, kaufen wir beim nächsten gültigen Eintrag.
Dieses Muster vermeidet eine visuelle Beurteilung. Trend und Rücksetzer werden anhand fester Referenzstäbe gemessen. Die Logik bleibt für alle Märkte und alle Tests gleich. Sie spiegelt eine der wichtigsten Ideen von Larry Williams wider. Starke Trends belohnen oft diejenigen, die eher Schwäche als Stärke kaufen.
Kaufen nach einer bärischen Outside Bar
Dies ist ein klassisches Beispiel für emotionale Erschöpfung. Eine Outside Bar dehnt sich über das Hoch und Tief des Vortages hinaus aus. Wenn eine solche Bar nahe ihrem Tief schließt, erscheint der Markt extrem schwach. Die Angst ist auf dem Chart sichtbar. Die meisten Trader interpretieren dies als ein Verkaufssignal.
Larry Williams hat das Gegenteil beobachtet. In vielen Fällen markiert dieses Muster eher das Ende als den Anfang einer Panik. Wenn der Verkauf ein Extrem erreicht, schreiten die Käufer oft schon am nächsten Tag ein. Hier kaufen wir nach einem rückläufigen Outside Bar mit einem Schlusskurs unter dem vorherigen Tief.
Mit diesem Muster wird direkt getestet, ob extreme Angst kurzfristige Chancen schafft.
Verblassen von drei aufeinanderfolgenden bullischen Schlusskursen
Dies ist die einzige Kurzstrategie in der Gruppe. Wir warten auf drei aufeinanderfolgende bullische Schlusskurse. Solche Sequenzen ziehen oft späte Käufer an. Stärke wird sichtbar. Der Optimismus nimmt zu. Anstatt uns der Bewegung anzuschließen, lassen wir sie hinter uns. Wenn der Kurs an Schwung verliert und unter unser Einstiegsniveau fällt, verkaufen wir.
Mit diesem Muster wird getestet, ob kurzfristige Stärke oft eher eine Erschöpfung als eine Fortsetzung bedeutet.
Jedes Muster kann an allen Tagen oder nur an ausgewählten Tagen gehandelt werden. Larry Williams zeigte, dass sich viele kurzfristige Muster im Wochenverlauf sehr unterschiedlich verhalten. Der Montag ist oft eine Reaktion auf die Nachrichten vom Wochenende. Der Freitag ist häufig durch Positionsabbau geprägt. Der Donnerstag steht häufig vor Wochenschluss unter Druck. Durch die Aktivierung des Handelstagsfilters können wir messen, wie jede Strategie an bestimmten Tagen abschneidet. So können wir nicht nur das Preisverhalten, sondern auch das Zeitverhalten untersuchen.
Diese Muster stehen für unterschiedliche Kräfte. Einige handeln Rücksetzer innerhalb bestehender Stärke. Einige handeln gegen emotionale Übertreibungen. Einige setzen auf Fortsetzung, andere auf Erschöpfung. Indem wir sie in einen einheitlichen Rahmen stellen und dasselbe Risikomodell und dieselben Ausstiegsregeln verwenden, können wir sie fair vergleichen.
Dabei geht es nicht darum, einen Gewinner zu ermitteln. Es geht darum, zu verstehen, welche Ideen sich im historischen Datenmaterial bewähren und welche nur auf dem Papier attraktiv bleiben. Nachdem wir die Muster definiert haben, beginnen wir nun mit dem technischen Aufbau des Expert Advisors, um sie mit Disziplin und Klarheit zu testen.
Grundlegende Handelsregeln und Systemarchitektur
Ein einziges System mit einer aktiven Strategie
Dieser Expert Advisor ist als einheitliches Handelssystem konzipiert, das in mehreren Modi arbeiten kann, aber immer nur in einem Modus. Beim Start wählen wir genau ein Muster aus den verfügbaren Strategien aus. Das ausgewählte Muster wird die einzige Signalquelle, bis der EA neu gestartet oder die Konfiguration geändert wird. Dieser Ansatz ermöglicht es uns, jede Idee unabhängig zu testen und verhindert Wechselwirkungen zwischen nicht miteinander verbundenen Logiken.
Ebenso wichtig ist, dass das System zu jedem Zeitpunkt nur eine offene Position hält: kein Pyramiding, kein Hedging, keine sich überschneidenden Trades. Jede neue Gelegenheit muss warten, bis die vorherige Position vollständig geschlossen ist. Diese Regel hält das Verhalten einfach, die Statistiken sauber und die Interpretation der Ergebnisse zuverlässig.
Einstiegslogik und volatilitätsabhängige Bestätigung
Mit Ausnahme der Basisstrategie basieren alle Muster auf einem volatilitätsbasierten Einstiegsmodell. Sobald ein gültiges Muster auf der geschlossenen Bar erkannt wird, steigen wir nicht sofort ein. Stattdessen ermitteln wir ein Einstiegsniveau mithilfe der einfachen Volatilitätsausbruchstechnik. Der Bereich der vorherigen Bar wird gemessen und mit einem nutzerdefinierten Faktor multipliziert. Dieser Abstand wird zum aktuellen Eröffnungskurs addiert oder davon subtrahiert, um den Einstiegskurs zu ermitteln.
Eine Position wird erst dann eröffnet, wenn der Kurs dieses projizierte Niveau in der erwarteten Richtung überschreitet. Diese Regel dient zwei Zwecken. Erstens wird der Einstieg mit dem kurzfristigen Momentum abgestimmt. Zweitens wird vermieden, dass Eintritte in Phasen geringer Aktivität oder Unentschlossenheit vermieden werden. Die einzige Ausnahme bildet die Basisstrategie. In diesem Modus steigen wir direkt bei der Eröffnung ein, ohne auf einen Ausbruch zu warten.
Konfigurierbare Stop-Loss-Platzierung
Ein Stop-Loss muss jeden Handel von dem Moment an schützen, in dem er eröffnet wird. Wir lassen zwei Platzierungsmodelle zu. Im ersten Modell wird der Stop-Loss als Prozentsatz der Spanne der vorherigen Bar vom Einstiegskurs festgelegt. So entsteht ein volatilitätsbereinigtes Schutzniveau, das sich mit der Marktaktivität ausdehnt und zusammenzieht.
Beim zweiten Modell wird der Stop-Loss am Extremwert der vorherigen Bar platziert. Bei Long-Trades ist dies das vorherige Tief. Bei Short-Trades ist dies das vorherige Hoch. Beide Methoden sind objektiv, einfach und vollständig konfigurierbar. Das gewählte Modell gilt einheitlich für alle Strategien.
Flexible Modelle für Take-Profit
Die Ausstiegslogik spielt in diesem System eine zentrale Rolle. Wir unterstützen drei Gewinnmitnahmemodi. Im ersten Modus wird die Position bei der ersten neu geöffneten Bar, die einen gleitenden Gewinn aufweist, geschlossen. Das schnelle Reaktionsverhalten dieses Modells spiegelt Larry Williams‘ Vorliebe für schnelle Ausstiege wider.
Im zweiten Modus wird die Position nach einer bestimmten Anzahl von abgeschlossenen Bars geschlossen. Auf diese Weise können wir untersuchen, wie lange jedes Muster tendenziell wirksam bleibt.
Im dritten Modus wird der Take-Profit anhand eines festen Risiko-Ertrags-Verhältnisses berechnet. Der Stop-Distanz bestimmt das Risiko. Die Ziel-Distanz wird als Vielfaches dieses Risikos berechnet.
Es ist jeweils nur ein Ausstiegsmodell aktiv. Das gewählte Modell gilt für jeden Handel, unabhängig von der Strategie.
Handelstag-Filterung
Jedes Signal kann nach Wochentag gefiltert werden. Wir können den Handel an allen Tagen zulassen oder die Ausführung nur auf ausgewählte Tage beschränken. Diese Regel gilt, bevor ein Trade eröffnet wird. Dieser Filter ermöglicht es uns, das Zeitverhalten unabhängig vom Preisverhalten zu untersuchen. Ein und dasselbe Muster kann an verschiedenen Tagen sehr unterschiedlich funktionieren. Indem wir diese Effekte isolieren, gewinnen wir Erkenntnisse darüber, wann ein Muster am stärksten ist und wann es vermieden werden sollte.
Positionsgrößenbestimmung und Risikokontrolle
Die Positionsgröße kann auf zwei Arten definiert werden. Im manuellen Modus wird für jeden Handel eine feste Losgröße verwendet. Im automatischen Modus wird die Positionsgröße als Prozentsatz des Kontosaldos auf der Grundlage des Stop-Loss-Abstands berechnet. Dadurch bleibt das Risiko proportional und stabil, wenn sich das Eigenkapital ändert.
Diese Regel stellt sicher, dass alle Strategien unter einheitlichen Risikobedingungen getestet werden. Die Leistungsunterschiede spiegeln dann eher die Qualität der Logik wider als Artefakte bei der Positionsbestimmung.
Ausführungsdisziplin und Sicherheitsregeln
Alle Kursniveaus werden nur bei der Eröffnung jeder neuen Bar neu berechnet. Es wird kein Handel eröffnet, wenn eine Position aus diesem EA bereits aktiv ist. Alle Aufträge werden mit einer eindeutigen Magic Number versehen, um Interferenzen mit anderen Systemen zu vermeiden. Diese Sicherheitsvorkehrungen verhindern übermäßiges Handeln, widersprüchliche Signale und unbeabsichtigtes Stapeln von Positionen.
Design-Philosophie
Die Architektur folgt einem einfachen Prinzip.
- Eine Idee nach der anderen.
- Ein Trade nach dem anderen.
- Ein Risikomodell nach dem anderen.
Jede Komponente ist isoliert, konfigurierbar und testbar.
Da die Regeln nun klar definiert sind, können wir zur technischen Konstruktion des Expert Advisors übergehen und diese Konzepte in funktionierenden MQL5-Code umsetzen.
Die Grundlagen für den Expert Advisor schaffen
In diesem Abschnitt wird das anfängliche Gerüst des Expert Advisors konstruiert, alle Konfigurationsstrukturen werden definiert und die Kernobjekte und Datencontainer werden vorbereitet, die später jede Entscheidung des Programms unterstützen werden. Diese Phase mag einfach erscheinen, aber sie ist eine der wichtigsten Phasen des gesamten Projekts. Eine klare, gut organisierte Grundlage macht es einfacher, die Strategie zu verstehen, zu erweitern und auf kontrollierte Weise zu testen.
Voraussetzungen für das Mitmachen
Um diesen Abschnitt in vollem Umfang nutzen zu können, sind einige Grundkenntnisse erforderlich. Zunächst ist die Vertrautheit mit der Sprache MQL5 unerlässlich. Grundlegende Konzepte wie Variablen, Funktionen, bedingte Anweisungen, Schleifen, Aufzählungen und die Verwendung von Standardbibliotheken sollten bereits vertraut sein. Wenn diese Themen noch neu sind, ist die offizielle MQL5-Referenz ein hervorragender Ausgangspunkt, bevor Sie fortfahren.
Zweitens werden Vorkenntnisse im Umgang mit der MetaTrader-Plattform vorausgesetzt. Die Navigation durch Charts, das Anhängen von Expert Advisors und die Verwendung des Strategy Testers sollten bereits Routineaufgaben sein.
Drittens ist eine regelmäßige Verwendung von MetaEditor erforderlich. Das Erstellen neuer Quelldateien, das Kompilieren von Code, das Lesen von Compiler-Meldungen und das Aufspüren einfacher Fehler sollten bereits Teil des normalen Arbeitsablaufs sein. Mit diesen Voraussetzungen können wir nun Schritt für Schritt mit dem Aufbau des Expert Advisors beginnen.
Zusammenarbeit mit der Referenzimplementierung
Programmieren lernt man am besten durch das Schreiben von Code, nicht nur durch Lesen. Aus diesem Grund ist die fertige Quelldatei für den Expert Advisor als lwPatternsToprofit.mq5 an diesen Artikel angehängt.
Wenn Sie diese Datei in einer separaten Registerkarte geöffnet lassen, während Sie dem Lernprogramm folgen, können Sie die einzelnen Konstruktionsschritte direkt mit dem fertigen System vergleichen. Diese Vorgehensweise hilft, das Verständnis zu bestätigen, und verhindert, dass sich kleine Fehler unbemerkt ansammeln.
Erstellen der ursprünglichen Quelldatei
Wir beginnen damit, den MetaEditor zu öffnen und eine neue leere Expert Advisor-Quelldatei zu erstellen. Die Datei kann einen beliebigen Namen erhalten. In diese Datei fügen wir den unten gezeigten Standardcode ein.
//+------------------------------------------------------------------+ //| lwPatternsToprofit.mq5 | //| Copyright 2026, MetaQuotes Ltd. Developer is Chacha Ian | //| https://www.mql5.com/en/users/chachaian | //+------------------------------------------------------------------+ #property copyright "Copyright 2026, MetaQuotes Ltd. Developer is Chacha Ian" #property link "https://www.mql5.com/en/users/chachaian" #property version "1.00" //+------------------------------------------------------------------+ //| Standard Libraries | //+------------------------------------------------------------------+ #include <Trade\Trade.mqh> //+------------------------------------------------------------------+ //| Custom Enumerations | //+------------------------------------------------------------------+ enum ENUM_LW_STRATEGY_MODE { LW_STRATEGY_BASELINE_BUY_OPEN, LW_STRATEGY_CONSECUTIVE_BEARISH_BARS, LW_STRATEGY_UPTREND_WITH_PULLBACK, LW_STRATEGY_OUTSIDE_DAY_DOWN_CLOSE, LW_STRATEGY_THIRD_BULLISH_DAY_FADE }; enum ENUM_TDW_MODE { TDW_ALL_DAYS, TDW_SELECTED_DAYS }; enum ENUM_BAR_CLOSE_STATE { BAR_CLOSE_UP, BAR_CLOSE_DOWN }; enum ENUM_STOP_LOSS_MODE { SL_BY_RANGE_PERCENT, SL_AT_PREVIOUS_BAR }; enum ENUM_TAKE_PROFIT_MODE { TP_FIRST_PROFITABLE_OPEN, TP_AFTER_N_CANDLES, TP_BY_RISK_REWARD }; enum ENUM_LOT_SIZE_INPUT_MODE { MODE_MANUAL, MODE_AUTO }; //+------------------------------------------------------------------+ //| User input variables | //+------------------------------------------------------------------+ input group "Information" input ulong magicNumber = 254700680002; input ENUM_TIMEFRAMES timeframe = PERIOD_CURRENT; input group "Strategy Configuration" input ENUM_LW_STRATEGY_MODE lwStrategyMode = LW_STRATEGY_BASELINE_BUY_OPEN; input int requiredConsecutiveBearishBars = 3; input int requiredConsecutiveBullishBars = 3; input int uptrendLookbackBars = 30; input int pullbackLookbackBars = 9; input group "Volatility Breakout Parameters" input double inpBuyRangeMultiplier = 0.50; input double inpSellRangeMultiplier = 0.50; input double inpStopRangeMultiplier = 0.50; input group "TDW filter" input ENUM_TDW_MODE tradeDayMode = TDW_SELECTED_DAYS; input bool tradeSunday = false; input bool tradeMonday = true; input bool tradeTuesday = false; input bool tradeWednesday = false; input bool tradeThursday = false; input bool tradeFriday = false; input bool tradeSaturday = false; input group "Trade and Risk Management" input ENUM_STOP_LOSS_MODE stopLossMode = SL_BY_RANGE_PERCENT; input ENUM_TAKE_PROFIT_MODE takeProfitMode = TP_BY_RISK_REWARD; input double riskRewardRatio = 3.0; input int exitAfterCandles = 3; input ENUM_LOT_SIZE_INPUT_MODE lotSizeMode = MODE_AUTO; input double riskPerTradePercent = 1.0; input double positionSize = 0.1; //+------------------------------------------------------------------+ //| Global Variables | //+------------------------------------------------------------------+ //--- Create a CTrade object to handle trading operations CTrade Trade; //--- To hep track current market prices for Buying (Ask) and Selling (Bid) double askPrice; double bidPrice; //--- To store current time datetime currentTime; //--- Holds all price levels derived from Larry Williams' volatility breakout calculations struct MqlLwVolatilityLevels { double yesterdayRange; double buyEntryPrice; double sellEntryPrice; double bullishStopLoss; double bearishStopLoss; double bullishTakeProfit; double bearishTakeProfit; double bullishStopDistance; double bearishStopDistance; }; MqlLwVolatilityLevels lwVolatilityLevels; //--- To store minutes data double closePriceMinutesData []; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit(){ //--- Assign a unique magic number to identify trades opened by this EA Trade.SetExpertMagicNumber(magicNumber); //--- Reset Larry Williams' volatility levels ZeroMemory(lwVolatilityLevels); //--- Treat the following arrays as timeseries (index 0 becomes the most recent bar) ArraySetAsSeries(closePriceMinutesData, true); 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); bidPrice = SymbolInfoDouble (_Symbol, SYMBOL_BID); currentTime = TimeCurrent(); //--- Get some minutes data if(CopyClose(_Symbol, PERIOD_M1, 0, 7, closePriceMinutesData) == -1){ Print("Error while copying minutes datas ", GetLastError()); return; } } //--- UTILITY FUNCTIONS //+------------------------------------------------------------------+
Dieser Code enthält noch keine Strategielogik. Stattdessen wird die Struktur definiert, die später alle Muster, Filter und Ausführungsregeln unterstützen wird. Jeder hier hinzugefügte Abschnitt hat eine spezifische Rolle in der Systemarchitektur.
Programmidentität und grundlegende Eigenschaften
//+------------------------------------------------------------------+ //| lwPatternsToprofit.mq5 | //| Copyright 2026, MetaQuotes Ltd. Developer is Chacha Ian | //| https://www.mql5.com/en/users/chachaian | //+------------------------------------------------------------------+ #property copyright "Copyright 2026, MetaQuotes Ltd. Developer is Chacha Ian" #property link "https://www.mql5.com/en/users/chachaian" #property version "1.00"
In den ersten Zeilen werden der Programmname, die Autorenangaben und die Versionsnummer angegeben. Diese Eigenschaften dokumentieren den Ursprung der Quelldatei und ermöglichen es der Plattform, den Expert Advisor während der Tests und der Ausführung zu identifizieren. In diesem Stadium wird keine Logik eingeführt. Wir sind lediglich dabei, eine formale Identität für das Programm zu schaffen.
Einschließlich der Handelsbibliothek
Die Handelsbibliothek ist als Nächstes enthalten.
//+------------------------------------------------------------------+ //| Standard Libraries | //+------------------------------------------------------------------+ #include <Trade\Trade.mqh>
Diese Bibliothek stellt die Klasse CTrade zur Verfügung, die eine zuverlässige und konsistente Schnittstelle für das Öffnen und Schließen von Positionen, das Ändern von Aufträgen und das Lesen von Ausführungsergebnissen bietet.
Definieren von Strategie- und Konfigurationsaufzählungen
Wir führen nun eine Reihe von Enumerationen ein, die alle auswählbaren Verhaltensweisen des Systems beschreiben.
//+------------------------------------------------------------------+ //| Custom Enumerations | //+------------------------------------------------------------------+ enum ENUM_LW_STRATEGY_MODE { LW_STRATEGY_BASELINE_BUY_OPEN, LW_STRATEGY_CONSECUTIVE_BEARISH_BARS, LW_STRATEGY_UPTREND_WITH_PULLBACK, LW_STRATEGY_OUTSIDE_DAY_DOWN_CLOSE, LW_STRATEGY_THIRD_BULLISH_DAY_FADE }; enum ENUM_TDW_MODE { TDW_ALL_DAYS, TDW_SELECTED_DAYS }; enum ENUM_BAR_CLOSE_STATE { BAR_CLOSE_UP, BAR_CLOSE_DOWN }; enum ENUM_STOP_LOSS_MODE { SL_BY_RANGE_PERCENT, SL_AT_PREVIOUS_BAR }; enum ENUM_TAKE_PROFIT_MODE { TP_FIRST_PROFITABLE_OPEN, TP_AFTER_N_CANDLES, TP_BY_RISK_REWARD }; enum ENUM_LOT_SIZE_INPUT_MODE { MODE_MANUAL, MODE_AUTO };
Die Enumeration der Strategie listet alle Handelsmuster auf, die getestet werden können. Es wird immer nur eine Strategie aktiv sein. Auf diese Weise kann jedes Muster unabhängig getestet und objektiv verglichen werden. Die Enumeration der Stop-Loss-Modi definiert, wie schützende Ausstiegsregeln angewendet werden. Die Enumeration der Take-Profit-Modi legt fest, wie die Gewinne realisiert werden. Die Enumeration für die Losgrößenmodi definiert, wie das Positionsvolumen berechnet wird. Die Enumeration der Zustände beim Schließen der Bars bietet eine saubere Möglichkeit zur Klassifizierung der Richtung. Die Enumeration der Handelstagemodi bereitet das System auf eine optionale tagesbasierte Filterung vor. Diese Enumerationen bilden das Rückgrat des Konfigurationsmodells. Sie verwandeln Handelsideen in strukturierte Zustände, die das Programm systematisch verarbeiten kann.
Nutzereingabeparameter und Strategiekontrollen
//+------------------------------------------------------------------+ //| User input variables | //+------------------------------------------------------------------+ input group "Information" input ulong magicNumber = 254700680002; input ENUM_TIMEFRAMES timeframe = PERIOD_CURRENT; input group "Strategy Configuration" input ENUM_LW_STRATEGY_MODE lwStrategyMode = LW_STRATEGY_BASELINE_BUY_OPEN; input int requiredConsecutiveBearishBars = 3; input int requiredConsecutiveBullishBars = 3; input int uptrendLookbackBars = 30; input int pullbackLookbackBars = 9; input group "Volatility Breakout Parameters" input double inpBuyRangeMultiplier = 0.50; input double inpSellRangeMultiplier = 0.50; input double inpStopRangeMultiplier = 0.50; input group "TDW filter" input ENUM_TDW_MODE tradeDayMode = TDW_SELECTED_DAYS; input bool tradeSunday = false; input bool tradeMonday = true; input bool tradeTuesday = false; input bool tradeWednesday = false; input bool tradeThursday = false; input bool tradeFriday = false; input bool tradeSaturday = false; input group "Trade and Risk Management" input ENUM_STOP_LOSS_MODE stopLossMode = SL_BY_RANGE_PERCENT; input ENUM_TAKE_PROFIT_MODE takeProfitMode = TP_BY_RISK_REWARD; input double riskRewardRatio = 3.0; input int exitAfterCandles = 3; input ENUM_LOT_SIZE_INPUT_MODE lotSizeMode = MODE_AUTO; input double riskPerTradePercent = 1.0; input double positionSize = 0.1;
Als Nächstes definieren wir alle vom Nutzer konfigurierbaren Eingabeparameter. Dieser Abschnitt stellt die öffentliche Schnittstelle des Expert Advisors dar. Jeder hier deklarierte Parameter wird in der Plattform sichtbar und kann ohne Änderung des Quellcodes angepasst werden. Die Gruppe für die Strategiekonfiguration ermöglicht die Auswahl des aktiven Musters und die Feinabstimmung der musterspezifischen Werte, wie z. B. die Anzahl der aufeinanderfolgenden Bars, das Trendreferenzfenster und das Rücksetzer-Referenzfenster. Die Volatilitäts-Breakout-Gruppe steuert, wie die Einstiegs- und Stoppabstände auf der Grundlage der Spanne der vorherigen Bar berechnet werden. Die Filtergruppe Handelstag legt fest, welche Wochentage für die Ausführung zulässig sind. Die Handels- und Risikomanagementgruppe definiert die Stop-Loss- und Gewinnmitnahme-Logik, die Regeln für die Positionsgröße und das Risikoengagement.
In diesem Stadium wenden wir noch keine Logik an. Wir beschreiben hier nur die vollständige Steuerungsoberfläche des Systems, die später jede Handelsentscheidung lenken wird.
Erstellen von Kernhandelsobjekten und Preiscontainern
Wir erstellen nun das CTrade-Objekt, das alle Handelsoperationen verwalten wird.
//+------------------------------------------------------------------+ //| Global Variables | //+------------------------------------------------------------------+ //--- Create a CTrade object to handle trading operations CTrade Trade;
Dieses Objekt wird zum einzigen Ausführungskanal für das Öffnen und Schließen von Positionen. Jeder Auftrag, der vom Programm gesendet wird, durchläuft diese Schnittstelle.
Anschließend deklarieren wir Variablen, um den aktuellen Briefkurs, den Geldkurs und die aktuelle Uhrzeit zu speichern.
//--- To hep track current market prices for Buying (Ask) and Selling (Bid) double askPrice; double bidPrice; //--- To store current time datetime currentTime;
Diese Werte werden bei jedem Tick aktualisiert und dienen später als Grundlage für alle preisbasierten Berechnungen.
Vorbereiten der Volatilitätslevelstruktur
Ein zentrales Konzept in diesem System ist die Projektion von Einstiegs-, Stopp- und Zielniveaus aus den jüngsten Marktspannen. Um diese Werte sauber zu verwalten, definieren wir eine Struktur, die alle von der Volatilität abgeleiteten Preisniveaus enthält.
//--- Holds all price levels derived from Larry Williams' volatility breakout calculations struct MqlLwVolatilityLevels { double yesterdayRange; double buyEntryPrice; double sellEntryPrice; double bullishStopLoss; double bearishStopLoss; double bullishTakeProfit; double bearishTakeProfit; double bullishStopDistance; double bearishStopDistance; }; MqlLwVolatilityLevels lwVolatilityLevels;
Diese Struktur speichert den Bereich der vorherigen Bar, die projizierten Einstiegskurse, die projizierten Stoppkurse, die projizierten Take-Profit-Kurse und die gemessenen Stoppabstände.
Durch die Gruppierung dieser Werte in einer einzigen Struktur bleiben die zugehörigen Daten übersichtlich und leicht zu pflegen. Spätere Berechnungen können diese Struktur einmal pro Bar aktualisieren, während die Ein- und Ausstiegslogik aus ihr lesen kann.
Vorbereitung der Intraday-Kursverfolgung
Wir deklarieren auch ein Array, um die letzten einminütigen Schlusskurse zu speichern.
//--- To store minutes data double closePriceMinutesData [];
Dieses Array wird später eine präzise Erkennung von Crossover- und Crossunder-Ereignissen im Verhältnis zu den prognostizierten Einstiegsniveaus ermöglichen. Ohne dieses Array würden die Einstiegssignale bis zum Schließen der Bar verzögert werden, wodurch die beabsichtigte Zeitpräzision verloren ginge.
Bei der Initialisierung wird das Array als Zeitreihe konfiguriert, sodass sich der Index Null immer auf den jüngsten Kurs bezieht.
Initialisierung des Expert Advisors
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit(){ //--- Assign a unique magic number to identify trades opened by this EA Trade.SetExpertMagicNumber(magicNumber); //--- Reset Larry Williams' volatility levels ZeroMemory(lwVolatilityLevels); //--- Treat the following arrays as timeseries (index 0 becomes the most recent bar) ArraySetAsSeries(closePriceMinutesData, true); return(INIT_SUCCEEDED); }
Innerhalb der Initialisierungsfunktion führen wir drei wesentliche Aktionen durch. Zunächst weisen wir dem Handelsobjekt eine eindeutige Magic Number zu. Dadurch kann das Programm später erkennen, welche Positionen zu dieser speziellen Expert Advisor-Instanz gehören. Zweitens setzen wir die Struktur des Volatilitätsniveaus auf Null zurück. Dies garantiert, dass beim Eintreffen der ersten Bars keine nicht initialisierten Werte verwendet werden. Drittens konfigurieren wir das Preisfeld als Zeitreihe. Dadurch wird sichergestellt, dass die Preisindexierung während des gesamten Programms konsistent bleibt.
Zu diesem Zeitpunkt ist der Expert Advisor vollständig initialisiert, enthält jedoch keine Handelsintelligenz. Er ist nun bereit, Ticks zu empfangen und Marktdaten sicher zu speichern.
Deinitialisierung und Programmabschaltung
//+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason){ //--- Notify why the program stopped running Print("Program terminated! Reason code: ", reason); }
Die Deinitialisierungsfunktion führt eine einfache Berichtsaufgabe aus. Wenn das Programm aufhört zu laufen, wird der von der Plattform angegebene Beendigungsgrund gedruckt. Diese Informationen sind bei Tests nützlich, um unerwartete Abschaltungen oder manuelle Entfernungen zu diagnostizieren.
Der initiale Tick-Handler
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick(){ //--- Retrieve current market prices for trade execution askPrice = SymbolInfoDouble (_Symbol, SYMBOL_ASK); bidPrice = SymbolInfoDouble (_Symbol, SYMBOL_BID); currentTime = TimeCurrent(); //--- Get some minutes data if(CopyClose(_Symbol, PERIOD_M1, 0, 7, closePriceMinutesData) == -1){ Print("Error while copying minutes datas ", GetLastError()); return; } }
Die erste Version der Tick-Funktion führt nur zwei Aufgaben aus. Sie aktualisiert den aktuellen Geldkurs, den Briefkurs und die aktuelle Uhrzeit. Er kopiert die letzten einminütigen Schlusskurse in das Intraday-Array. Es wurden noch keine Handelsentscheidungen getroffen. Es werden keine Signale ausgewertet. Es werden keine Aufträge verschickt. Diese Einfachheit ist beabsichtigt. Bevor wir die Strategielogik einführen, stellen wir zunächst sicher, dass die Kursdaten korrekt aktualisiert werden, die Zeit korrekt synchronisiert wird und die Intraday-Arrays fehlerfrei gefüllt werden. Erst wenn diese Grundlage stabil ist, beginnen wir mit der Schichtung von Mustererkennung, Filtern und Ausführungsregeln.
In diesem Stadium enthält der Expert Advisor noch keine Handelslogik, aber er drückt bereits die gesamte Architektur des Systems aus. Wir haben alle Strategien, Exit-Frameworks, Filter, Risikokontrollen und Kerndatencontainer definiert. Jede künftige Funktion wird sich nun ganz natürlich an eine bestehende Struktur anschließen.
Im nächsten Abschnitt fügen wir die ersten logischen Bausteine hinzu, beginnend mit der Barerkennung und der Klassifizierung grundlegender Muster, und verwandeln dieses leere Gerüst schrittweise in ein voll funktionsfähiges Multi-Strategie-Handelssystem.
Erkennen von Mustern und Projektion von Volatilitätsniveaus
Nachdem das Fundament gelegt ist, können wir nun die eigentliche Intelligenz des Expert Advisors herausarbeiten. In dieser Phase werden die Mustererkennung, die Volatilitätsprognose, das Handels-Timing und die internen Tools eingeführt, die es dem System ermöglichen, sich wie ein disziplinierter kurzfristiger Trader zu verhalten.
In dieser Phase geht es noch nicht um die Eröffnung von Trades, sondern um den Aufbau eines zuverlässigen Umfelds, in dem gültige Gelegenheiten erkannt, gemessen, gefiltert und später mit Präzision ausgeführt werden können.
Warum die Erkennung neuer Bars wichtig ist
Fast jede wichtige Entscheidung in diesem System beginnt, wenn sich ein neuer Bar bildet. Zu diesem Zeitpunkt wollen wir drei Ziele erreichen. Zunächst müssen wir neue, auf der Volatilität basierende Einstiegs- und Ausstiegsniveaus aus dem vorherigen Bar hochrechnen. Zweitens müssen wir die internen Zähler aktualisieren, die von zeitbasierten Ausgängen verwendet werden. Drittens müssen wir die Ausstiegsbedingungen bewerten, bevor wir neue Signale in Betracht ziehen.
Aus diesem Grund benötigt das Programm eine zuverlässige Methode, um zu erkennen, wann eine neue Bar im ausgewählten Zeitrahmen geöffnet wird. Wir führen eine kleine Nutzenfunktion ein, deren einzige Aufgabe es ist, dieses Ereignis zu erkennen.
//+------------------------------------------------------------------+ //| 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 Öffnungszeit der aktuellen Bar mit der gespeicherten Öffnungszeit der vorherigen Bar. Wenn die beiden Werte voneinander abweichen, hat sich eine neue Bar gebildet. Der dritte Parameter wird als Referenz übergeben, damit die Funktion die gespeicherte Zeit intern aktualisieren kann. Dadurch wird eine doppelte Erkennung vermieden und sichergestellt, dass die Logik nur einmal pro Bar ausgeführt wird.
Um diesen Mechanismus zu unterstützen, definieren wir eine globale Variable, die die letzte Öffnungszeit der Bar speichert.
//--- To help track new bar open datetime lastBarOpenTime;
Bei der Initialisierung wird er auf Null gesetzt, damit die allererste Bar richtig erkannt wird.
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit(){ ... //--- Initialize global variables lastBarOpenTime = 0; return(INIT_SUCCEEDED); }
Dieses einzige Werkzeug ist das Herzstück des gesamten Systems. Jede Volatilitätsprognose, jede Aktualisierung des Barzählers und jede tägliche Rückstellung beruht auf diesem Moment.
Aufeinanderfolgende Schlusskurse der Bars verfolgen
Zwei der Strategien hängen von aufeinanderfolgenden Schlusskursen in dieselbe Richtung ab. Man wartet auf mehrere rückläufige Schlusskurse, bevor man kauft. Die andere Variante wartet auf mehrere bullische Schlusskurse, bevor sie die Bewegung ausblendet. Anstatt diese Logik an mehreren Stellen zu duplizieren, definieren wir eine allgemeine Funktion, die überprüft, ob die letzten N abgeschlossenen Bars alle in dieselbe Richtung 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; }
Die Funktion beginnt bei Barindex eins, also dem letzten vollständig geschlossenen Bar. Für jede Bar wird der Schlusskurs mit dem Eröffnungskurs verglichen. Wenn ein Bar die erforderliche Richtung nicht einhält, schlägt das Muster sofort fehl. Wenn alle Bars die Bedingung erfüllen, bestätigt die Funktion die Reihenfolge. Diese Funktion wandelt die Idee eines visuellen Charts in eine objektive Regel um, die das Programm unzweideutig testen kann.
Erkennen eines Aufwärtstrends mit einem gemessenen Rücksetzer
Larry Williams hat eine einfache und wirkungsvolle Methode vorgeschlagen, um einen Rücksetzer ohne subjektive Beurteilung zu definieren. Es werden zwei historische Bezugspunkte verwendet. Ein Bar bestätigt den allgemeinen Trend, indem sie den heutigen Eröffnungskurs mit einem weit zurückliegenden Schlusskurs vergleicht. Ein weiterer Bar bestätigt den Rücksetzer, indem er den heutigen Eröffnungskurs mit einem jüngeren Schlusskurs vergleicht. Wenn der heutige Eröffnungskurs höher ist als der weiter zurückliegende Schlusskurs, besteht ein Aufwärtstrend. Wenn der heutige Eröffnungskurs niedriger ist als der nächste Schlusskurs, hat ein Rücksetzer stattgefunden.
Die Funktion, die diese Logik implementiert, erhält die Rückblickabstände als Parameter.
//+------------------------------------------------------------------+ //| Detects an uptrend with a small pullback based on Larry Williams'| //| logic using historical reference bars | //+------------------------------------------------------------------+ bool IsUptrendWithPullback(string symbol, ENUM_TIMEFRAMES tf, int index, int trendLookback, int pullbackLookback) { //--- Today's open double todayOpen = iOpen(symbol, tf, index); //--- Reference closes double closeTrendBar = iClose(symbol, tf, index + trendLookback); double closePullbackBar = iClose(symbol, tf, index + pullbackLookback); //--- Validate data if(todayOpen == 0.0 || closeTrendBar == 0.0 || closePullbackBar == 0.0) return false; //--- Condition 1: Uptrend confirmation bool isInUptrend = (todayOpen > closeTrendBar); if(!isInUptrend) return false; //--- Condition 2: Pullback confirmation bool isPullback = (todayOpen < closePullbackBar); if(!isPullback) return false; return true; }
Dadurch ist das Muster vollständig konfigurierbar und eignet sich für Tests unter verschiedenen Marktbedingungen. Mit diesem Ansatz werden Meinungen aus dem Prozess herausgenommen. Trend und Rücksetzer sind keine visuellen Vermutungen mehr, sondern strikte Preisbeziehungen.
Erkennen emotional getriebener Verkäufe anhand von Outside Bars
Ein weiteres wichtiges Muster, das von Larry Williams beschrieben wurde, ist der bärische Outside Bar mit einem Schlusskurs nach unten. Diese Formation fängt einen Moment der Panik ein, in dem der Preis in beide Richtungen expandiert, aber in der Nähe des Tiefs schließt. Die Erkennungsfunktion setzt drei Bedingungen voraus.
//+-----------------------------------------------------------------------+ //| Detects an outside bar with a bearish close relative to the prior bar | //+-----------------------------------------------------------------------+ bool IsOutsideBarWithDownClose(string symbol, ENUM_TIMEFRAMES tf, int index){ //--- Current bar data double open0 = iOpen (symbol, tf, index); double high0 = iHigh (symbol, tf, index); double low0 = iLow (symbol, tf, index); double close0 = iClose(symbol, tf, index); //--- Previous bar data double high1 = iHigh(symbol, tf, index + 1); double low1 = iLow (symbol, tf, index + 1); //--- Condition 1: Outside bar range bool isOutsideBar = (high0 > high1) && (low0 < low1); if(!isOutsideBar) return false; //--- Condition 2: Bearish close below previous low bool isDownClose = (close0 < low1); if(!isDownClose) return false; //--- Condition 3: Bearish bar bool isBearishBar = (open0 > close0); if(!isBearishBar) return false; return true; }
Erstens muss die aktuelle Bar die vorhergehende Bar sowohl im Hoch als auch im Tief übertreffen. Zweitens muss der Schlusskurs unter das vorherige Tief fallen. Drittens muss die Bar unter ihrer Eröffnung schließen, um den Abwärtsdruck zu bestätigen. Nur wenn alle drei Bedingungen erfüllt sind, bestätigt die Funktion das Muster. Dadurch wird eine visuell komplexe Stabformation in eine präzise mechanische Regel umgewandelt.
Messung der Volatilität anhand der vorherigen Bar
Jede Volatilitätsprognose in diesem System beginnt mit einer einfachen Messung. Das Hoch minus dem Tief der vorhergehenden Bar definiert den Arbeitsbereich. Eine Hilfsfunktion ruft diesen Bereich ab und normalisiert ihn auf die richtige Symbolgenauigkeit.
//+------------------------------------------------------------------+ //| Returns the price range (high - low) of a bar at the given index | //+------------------------------------------------------------------+ double GetBarRange(const string symbol, ENUM_TIMEFRAMES tf, int index){ double high = iHigh(symbol, tf, index); double low = iLow (symbol, tf, index); if(high == 0.0 || low == 0.0){ return 0.0; } return NormalizeDouble(high - low, Digits()); }
Dieser einzelne Wert dient als Grundlage für die Projektion von Einstiegs-, Stopp- und späteren Take-Profit-Levels. Ohne diese Messung kann das Breakout-Modell nicht funktionieren.
Hochrechnung der Eintrittspreise
Sobald die Preisspanne bekannt ist, wird die Prognose der Einstiegspreise einfach. Bei einem bullischen Setup ist der Einstiegskurs der aktuelle Eröffnungskurs plus ein Bruchteil der vorherigen Spanne.
//+--------------------------------------------------------------------------------------+ //| Calculates the bullish breakout entry price using today's open and yesterday's range | //+--------------------------------------------------------------------------------------+ double CalculateBuyEntryPrice(double todayOpen, double yesterdayRange, double buyMultiplier){ return todayOpen + (yesterdayRange * buyMultiplier); }
Bei einem bärischen Setup ist der Einstiegskurs der aktuelle Eröffnungskurs abzüglich eines Bruchteils der vorherigen Spanne.
//+--------------------------------------------------------------------------------------+ //| Calculates the bearish breakout entry price using today's open and yesterday's range | //+--------------------------------------------------------------------------------------+ double CalculateSellEntryPrice(double todayOpen, double yesterdayRange, double sellMultiplier){ return todayOpen - (yesterdayRange * sellMultiplier); }
Die Multiplikatoren kann der Nutzer konfigurieren, sodass getestet werden kann, wie aggressiv oder konservativ der Schwellenwert für den Ausbruch sein sollte. Diese Prognosen legen fest, wo sich die Dynamik beweisen muss, bevor ein Handel zulässig ist.
Projektion von Stop-Loss-Levels
Schutzausgänge können auf zwei verschiedene Arten gebaut werden. Im volatilitätsbasierten Modus wird der Stop-Loss ausgehend vom Einstiegskurs unter Verwendung eines Bruchteils der gleichen vorherigen Spanne projiziert. Bei bullischen Trades wird der Stopp unter dem Einstiegswert platziert.
//+--------------------------------------------------------------------------------------------------+ //| Calculates the stop-loss price for a bullish position based on entry price and yesterday's range | //+--------------------------------------------------------------------------------------------------+ double CalculateBullishStopLoss(double entryPrice, double yesterdayRange, double stopMultiplier){ return entryPrice - (yesterdayRange * stopMultiplier); }
Bei bärischen Trades wird der Stopp über dem Einstiegswert platziert.
//+--------------------------------------------------------------------------------------------------+ //| Calculates the stop-loss price for a bearish position based on entry price and yesterday's range | //+--------------------------------------------------------------------------------------------------+ double CalculateBearishStopLoss(double entryPrice, double yesterdayRange, double stopMultiplier){ return entryPrice + (yesterdayRange * stopMultiplier); }
Dadurch bleibt das Risiko proportional zur jüngsten Volatilität und passt sich auf natürliche Weise über Märkte und Zeitrahmen hinweg an.
Projektion von Gewinnmitnahmen unter Verwendung des Risiko-/Ertragsverhältnisses
Wenn der Take-Profit-Modus auf Risk-to-Reward eingestellt ist, werden die Ziele direkt von der gemessenen Stopp-Distanz abgeleitet. Der Abstand zwischen Einstieg und Stopp wird zur Risikoeinheit. Dieser Abstand wird mit dem gewählten Belohnungsverhältnis multipliziert.
Das Ergebnis wird zum Einstiegskurs addiert oder von diesem subtrahiert, um den Zielkurs zu erhalten.
//+--------------------------------------------------------------------------+ //| Calculates take-profit level for a bullish trade using risk-reward logic | //+--------------------------------------------------------------------------+ double CalculateBullishTakeProfit(double entryPrice, double stopLossPrice, double rewardValue){ double stopDistance = entryPrice - stopLossPrice; double rewardDistance = stopDistance * rewardValue; return NormalizeDouble(entryPrice + rewardDistance, Digits()); } //+--------------------------------------------------------------------------+ //| Calculates take-profit level for a bearish trade using risk-reward logic | //+--------------------------------------------------------------------------+ double CalculateBearishTakeProfit(double entryPrice, double stopLossPrice, double rewardValue){ double stopDistance = stopLossPrice - entryPrice; double rewardDistance = stopDistance * rewardValue; return NormalizeDouble(entryPrice - rewardDistance, Digits()); }
Dieser Ansatz garantiert eine einheitliche Risiko- und Ertragsgeometrie bei allen Trades.
Erkennung von Kurskreuzungen in Echtzeit
Die Signale in diesem System werden nicht durch das Schließen von Bars ausgelöst. Sie werden ausgelöst, wenn der Kurs das prognostizierte Einstiegsniveau tatsächlich überschreitet. Zu diesem Zweck speichern wir die letzten einminütigen Schlusskurse und vergleichen zwei aufeinanderfolgende Werte. Ein Crossover tritt auf, wenn sich der Kurs von einem Niveau unterhalb zu einem Niveau oberhalb bewegt.
//+------------------------------------------------------------------+ //| To detect a crossover at a given price level | //+------------------------------------------------------------------+ bool IsCrossOver(const double price, const double &closePriceMinsData[]){ if(closePriceMinsData[1] <= price && closePriceMinsData[0] > price){ return true; } return false; }
Eine Unterschreitung wird festgestellt, wenn sich der Kurs von einem Niveau oberhalb zu einem Niveau unterhalb bewegt.
//+------------------------------------------------------------------+ //| To detect a crossunder at a given price level | //+------------------------------------------------------------------+ bool IsCrossUnder(const double price, const double &closePriceMinsData[]){ if(closePriceMinsData[1] >= price && closePriceMinsData[0] < price){ return true; } return false; }
Dadurch kann das System sofort reagieren, wenn der Schwung die Einstellung bestätigt.
Filtern von Trades nach Wochentag
Die Trade-Day-Filterung wird mit zwei kleinen Hilfsfunktionen implementiert. Die erste wandelt die aktuelle Zeit in einen Tagesindex um.
//+------------------------------------------------------------------------------------+ //| Returns the day of the week (0 = Sunday, 6 = Saturday) for the given datetime value| //+------------------------------------------------------------------------------------+ int TimeDayOfWeek(datetime time){ MqlDateTime timeStruct = {}; if(!TimeToStruct(time, timeStruct)){ Print("TimeDayOfWeek: TimeToStruct failed"); return -1; } return timeStruct.day_of_week; }
Der zweite prüft, ob dieser Tag in der Konfiguration aktiviert ist.
//+-----------------------------------------------------------------------------------------------------+ //| Determines whether trading is permitted for the given datetime based on the selected trade-day mode | //+-----------------------------------------------------------------------------------------------------+ bool IsTradingDayAllowed(datetime time) { // Baseline mode: no filtering if(tradeDayMode == TDW_ALL_DAYS){ return true; } int day = TimeDayOfWeek(time); switch(day) { case 0: return tradeSunday; case 1: return tradeMonday; case 2: return tradeTuesday; case 3: return tradeWednesday; case 4: return tradeThursday; case 5: return tradeFriday; case 6: return tradeSaturday; } return false; }
Dieser Mechanismus ermöglicht das selektive Testen von Mustern an bestimmten Tagen, was für das Konzept des Handelstages der Woche von zentraler Bedeutung ist. Der Filter ist optional. Wenn diese Funktion deaktiviert ist, werden alle Tage akzeptiert.
Projektion von Volatilitätsniveaus bei der Bildung neuer Bars
Wenn alle Werkzeuge bereit sind, aktualisieren wir die Tick-Funktion, sodass die Volatilitätswerte nur dann projiziert werden, wenn sich ein neuer Bar bildet. Innerhalb des neuen Blocks führen wir eine strenge Abfolge von Aktionen durch.
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick(){ ... //--- Run this block only when a new bar is detected on the selected timeframe if(IsNewBar(_Symbol, timeframe, lastBarOpenTime)){ //--- Increment the number of completed bars since the position was opened if(barsSinceEntry > 0){ barsSinceEntry = barsSinceEntry + 1; } //--- Handle exit conditions for the currently active position based on the configured take-profit mode if(takeProfitMode == TP_FIRST_PROFITABLE_OPEN){ ManageOpenPositionExits(); } lwVolatilityLevels.yesterdayRange = GetBarRange(_Symbol, timeframe, 1); if(lwStrategyMode == LW_STRATEGY_BASELINE_BUY_OPEN){ lwVolatilityLevels.buyEntryPrice = askPrice; lwVolatilityLevels.sellEntryPrice = bidPrice; }else{ lwVolatilityLevels.buyEntryPrice = CalculateBuyEntryPrice (askPrice, lwVolatilityLevels.yesterdayRange, inpBuyRangeMultiplier ); lwVolatilityLevels.sellEntryPrice = CalculateSellEntryPrice(bidPrice, lwVolatilityLevels.yesterdayRange, inpSellRangeMultiplier); } if(stopLossMode == SL_BY_RANGE_PERCENT){ lwVolatilityLevels.bullishStopLoss = CalculateBullishStopLoss(lwVolatilityLevels.buyEntryPrice, lwVolatilityLevels.yesterdayRange, inpStopRangeMultiplier); lwVolatilityLevels.bearishStopLoss = CalculateBearishStopLoss(lwVolatilityLevels.sellEntryPrice, lwVolatilityLevels.yesterdayRange, inpStopRangeMultiplier); } if(stopLossMode == SL_AT_PREVIOUS_BAR){ lwVolatilityLevels.bullishStopLoss = iLow (_Symbol, timeframe, 1); lwVolatilityLevels.bearishStopLoss = iHigh(_Symbol, timeframe, 1); } if(takeProfitMode == TP_BY_RISK_REWARD){ lwVolatilityLevels.bullishTakeProfit = CalculateBullishTakeProfit(lwVolatilityLevels.buyEntryPrice, lwVolatilityLevels.bullishStopLoss, riskRewardRatio); lwVolatilityLevels.bearishTakeProfit = CalculateBearishTakeProfit(lwVolatilityLevels.sellEntryPrice, lwVolatilityLevels.bearishStopLoss, riskRewardRatio); } lwVolatilityLevels.bullishStopDistance = lwVolatilityLevels.buyEntryPrice - lwVolatilityLevels.bullishStopLoss; lwVolatilityLevels.bearishStopDistance = lwVolatilityLevels.bearishStopLoss - lwVolatilityLevels.sellEntryPrice; } }
Zunächst wird der Barzähler aktualisiert, wenn eine Position aktiv ist. Zweitens werden die Ausstiegsbedingungen für den ersten rentablen offenen Modus bewertet. Drittens wird die Spanne der vorherigen Bar gemessen. Viertens werden die Einstiegspreise je nach gewählter Strategie hochgerechnet. Fünftens werden die Stop-Loss-Kurse anhand des gewählten Stop-Modells hochgerechnet. Sechstens werden die voraussichtlichen Take-Profit-Preise angezeigt, wenn der Risk-to-Reward-Modus aktiv ist. Schließlich werden die Stoppabstände für die spätere Positionsberechnung berechnet.
Dieser Block setzt den gesamten Handelsrahmen zu Beginn jeder neuen Bars zurück. Wenn ein prognostiziertes Niveau während der Bar nicht durchbrochen wird, wird es verworfen und bei der nächsten Bar ersetzt. Es sind keine wiederkehrenden Signale erlaubt.
Sicherstellen, dass nur eine Position zu einem bestimmten Zeitpunkt vorhanden ist
Eine der wichtigsten Gestaltungsregeln ist, dass es zu jedem Zeitpunkt nur eine einzige Position geben kann. Zwei Hilfsfunktionen prüfen alle offenen Positionen und bestätigen, ob für diese Expert Advisor-Instanz bereits eine Kauf- oder Verkaufsposition besteht.
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+ //| To verify whether this EA currently has an active sell position. | | //+------------------------------------------------------------------+ bool IsThereAnActiveSellPosition(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_SELL){ return true; } } } return false; }
Diese Kontrollen werden vor jeder Handelsausführung durchgeführt. Wenn eine Position gefunden wird, ist kein neuer Handel erlaubt. Diese Regel sorgt für ein einfaches System und verhindert unbeabsichtigte Positionsstapelungen.
Positionen programmatisch schließen
Wenn die Gewinnmitnahme-Modi kein festes Ziel verwenden, muss das System die Trades auf der Grundlage der Logik und nicht des Preises schließen. Eine allgemeine Schließungsfunktion überprüft alle offenen Positionen, die zu diesem Expert Advisor gehören, und schließt sie sofort.
//+------------------------------------------------------------------+ //| 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); } } } } }
Diese Funktion wird später sowohl für zeitabhängige Ausstiege als auch für Ausstiege nach der ersten gewinnbringenden Öffnung wiederverwendet.
Berechnung der automatischen Positionsgröße
Wenn der Losgrößenmodus automatisch ist, wird das Positionsvolumen vom Kontorisiko abgeleitet.
//+----------------------------------------------------------------------------------+ //| Calculates position size based on a fixed percentage risk of the account balance | //+----------------------------------------------------------------------------------+ double CalculatePositionSizeByRisk(double stopDistance){ double amountAtRisk = (riskPerTradePercent / 100.0) * AccountInfoDouble(ACCOUNT_BALANCE); double contractSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_CONTRACT_SIZE); double volume = amountAtRisk / (contractSize * stopDistance); return NormalizeDouble(volume, 2); }
Die Funktion berechnet den Geldbetrag, der auf der Grundlage des gewählten Prozentsatzes riskiert werden kann. Anschließend wird dieser Betrag durch das Produkt aus Kontraktgröße und Haltestellenabstand geteilt. Das Ergebnis ist ein Positionsvolumen, das genau den gewünschten Anteil des Kontos riskiert. Dadurch bleibt das Risiko über verschiedene Instrumente und Volatilitätsregime hinweg stabil.
Ausführen von Kauf- und Verkaufsaufträgen
Zwei Ausführungsfunktionen sorgen für die Platzierung von Trades.
//+------------------------------------------------------------------+ //| Function to open a market buy position | //+------------------------------------------------------------------+ bool OpenBuy(double stopLoss, double takeProfit, double lotSize){ if(lotSizeMode == MODE_AUTO){ lotSize = CalculatePositionSizeByRisk(lwVolatilityLevels.bullishStopDistance); } if(!Trade.Buy(lotSize, _Symbol, askPrice, lwVolatilityLevels.bullishStopLoss)){ Print("Error while executing a market buy order: ", GetLastError()); Print(Trade.ResultRetcode()); Print(Trade.ResultComment()); return false; } if(takeProfitMode == TP_AFTER_N_CANDLES){ barsSinceEntry = 1; } return true; } //+------------------------------------------------------------------+ //| Function to open a market sell position | //+------------------------------------------------------------------+ bool OpenSel(double stopLoss, double takeProfit, double lotSize){ if(lotSizeMode == MODE_AUTO){ lotSize = CalculatePositionSizeByRisk(lwVolatilityLevels.bearishStopDistance); } if(!Trade.Sell(lotSize, _Symbol, bidPrice, lwVolatilityLevels.bearishStopLoss)){ Print("Error while executing a market buy order: ", GetLastError()); Print(Trade.ResultRetcode()); Print(Trade.ResultComment()); return false; } if(takeProfitMode == TP_AFTER_N_CANDLES){ barsSinceEntry = 1; } return true; }
Vor dem Absenden eines Auftrags wird die Losgröße neu berechnet, wenn die automatische Größenbestimmung aktiviert ist. Die Aufträge werden mit dem voraussichtlichen Stop-Loss versehen. Wenn der Ausstiegsmodus auf der Anzahl der Bars basiert, wird der Barzähler unmittelbar nach dem Einstieg initialisiert.
Jede Funktion meldet Ausführungsfehler, sodass Fehler während der Prüfung diagnostiziert werden können.
Ausstiegsmanagement ohne feste Zielvorgaben
Wenn der Take-Profit nicht durch einen festen Preis definiert ist, müssen die Ausstiege manuell verwaltet werden.
//+-------------------------------------------------------------------------------------------+ //| Manages exit logic for the currently open position based on the selected take-profit mode | //+-------------------------------------------------------------------------------------------+ void ManageOpenPositionExits(){ if(takeProfitMode == TP_FIRST_PROFITABLE_OPEN){ for(int i = PositionsTotal() - 1; i >= 0; i--){ ulong ticket = PositionGetTicket(i); if(ticket == 0){ Print("Error while fetching position ticket ", GetLastError()); continue; }else{ if(PositionGetDouble(POSITION_PROFIT) > 0 ){ ClosePositionsByMagic(magicNumber); } } } } if(takeProfitMode == TP_AFTER_N_CANDLES){ if(barsSinceEntry > exitAfterCandles){ ClosePositionsByMagic(magicNumber); barsSinceEntry = 0; } } }
Es werden zwei Ausstiegsmodelle unterstützt. Im ersten profitablen Eröffnungsmodus prüft das System offene Positionen und schließt sie, sobald der gleitende Gewinn positiv wird. Im Modus auf Basis der Bars schließt das System die Position, wenn der Barzähler den konfigurierten Grenzwert überschreitet, und setzt dann den Zähler zurück. Diese Funktion wird zu bestimmten Zeitpunkten im Tick-Zyklus aufgerufen, um sicherzustellen, dass die Ausgänge nicht verpasst werden.
Verfolgung der seit dem Einstieg verstrichenen Bar
Einige Ausstiegsmodelle hängen eher von der Zeit als vom Preis ab. Aus diesem Grund muss das System verfolgen, wie viele abgeschlossene Bars seit der Eröffnung einer Position vergangen sind.
Innerhalb der Ausstiegslogik beziehen wir uns auf eine Variable namens barsSinceEntry. Diese Variable zählt die Anzahl der Bars, die seit der Platzierung des aktuellen Trades verstrichen sind, und wird verwendet, um zu entscheiden, wann eine Position geschlossen werden sollte. Wir definieren sie zunächst auf globaler Ebene.
//--- Tracks the number of completed bars elapsed since the current trade was opened int barsSinceEntry;
Bevor es sicher verwendet werden kann, muss es beim Starten des Experten initialisiert werden.
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit(){ ... //--- Initialize bar counter for time-based trade exit tracking barsSinceEntry = 0; return(INIT_SUCCEEDED); }
Dieser Zähler wird bei jeder neuen Bar erhöht, nachdem ein Handel eröffnet wurde, und zurückgesetzt, sobald die Position geschlossen ist. Sie wird später im System zur Referenz für alle zeitbasierten Ausstiegsentscheidungen.
Zusammenstellung der Logik der Strategieumsetzung
Nachdem alle Bausteine vorhanden sind, setzen wir nun die Handelslogik in der Tick-Funktion zusammen. Jede Strategie ist in einem eigenen bedingten Block isoliert. Die Basisstrategie wird nur bei neuen Barformationen ausgeführt, da sie sofort bei der Eröffnung eintritt.
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick(){ ... //--- Run this block only when a new bar is detected on the selected timeframe if(IsNewBar(_Symbol, timeframe, lastBarOpenTime)){ ... //--- if(lwStrategyMode == LW_STRATEGY_BASELINE_BUY_OPEN){ if(IsThereAnActiveBuyPosition(magicNumber) || IsThereAnActiveSellPosition(magicNumber)){ ClosePositionsByMagic(magicNumber); Sleep(50); } if(tradeDayMode == TDW_SELECTED_DAYS){ if(IsTradingDayAllowed(currentTime)){ if(!IsThereAnActiveBuyPosition(magicNumber) && !IsThereAnActiveSellPosition(magicNumber)){ OpenBuy(lwVolatilityLevels.bullishStopLoss, lwVolatilityLevels.bullishTakeProfit, positionSize); } } }else{ if(!IsThereAnActiveBuyPosition(magicNumber) && !IsThereAnActiveSellPosition(magicNumber)){ OpenBuy(lwVolatilityLevels.bullishStopLoss, lwVolatilityLevels.bullishTakeProfit, positionSize); } } } } }
Alle anderen Strategien laufen während des Bars kontinuierlich ab, da ihre Signale davon abhängen, dass der Preis die prognostizierten Niveaus überschreitet.
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick(){ ... //--- if(lwStrategyMode == LW_STRATEGY_CONSECUTIVE_BEARISH_BARS){ if(!IsThereAnActiveBuyPosition(magicNumber) && !IsThereAnActiveSellPosition(magicNumber)){ if(IsConsecutiveBarCloseState(_Symbol, timeframe, requiredConsecutiveBearishBars, BAR_CLOSE_DOWN)){ if(IsCrossOver(lwVolatilityLevels.buyEntryPrice, closePriceMinutesData)){ if(tradeDayMode == TDW_SELECTED_DAYS){ if(IsTradingDayAllowed(currentTime)){ OpenBuy(lwVolatilityLevels.bullishStopLoss, lwVolatilityLevels.bullishTakeProfit, positionSize); } }else{ OpenBuy(lwVolatilityLevels.bullishStopLoss, lwVolatilityLevels.bullishTakeProfit, positionSize); } } } } } //--- if(lwStrategyMode == LW_STRATEGY_UPTREND_WITH_PULLBACK){ if(!IsThereAnActiveBuyPosition(magicNumber) && !IsThereAnActiveSellPosition(magicNumber)){ if(IsUptrendWithPullback(_Symbol, timeframe, 1, uptrendLookbackBars, pullbackLookbackBars)){ if(IsCrossOver(lwVolatilityLevels.buyEntryPrice, closePriceMinutesData)){ if(tradeDayMode == TDW_SELECTED_DAYS){ if(IsTradingDayAllowed(currentTime)){ OpenBuy(lwVolatilityLevels.bullishStopLoss, lwVolatilityLevels.bullishTakeProfit, positionSize); } }else{ OpenBuy(lwVolatilityLevels.bullishStopLoss, lwVolatilityLevels.bullishTakeProfit, positionSize); } } } } } //--- if(lwStrategyMode == LW_STRATEGY_OUTSIDE_DAY_DOWN_CLOSE){ if(!IsThereAnActiveBuyPosition(magicNumber) && !IsThereAnActiveSellPosition(magicNumber)){ if(IsOutsideBarWithDownClose(_Symbol, timeframe, 1)){ if(IsCrossOver(lwVolatilityLevels.buyEntryPrice, closePriceMinutesData)){ if(tradeDayMode == TDW_SELECTED_DAYS){ if(IsTradingDayAllowed(currentTime)){ OpenBuy(lwVolatilityLevels.bullishStopLoss, lwVolatilityLevels.bullishTakeProfit, positionSize); } }else{ OpenBuy(lwVolatilityLevels.bullishStopLoss, lwVolatilityLevels.bullishTakeProfit, positionSize); } } } } } //--- if(lwStrategyMode == LW_STRATEGY_THIRD_BULLISH_DAY_FADE){ if(!IsThereAnActiveBuyPosition(magicNumber) && !IsThereAnActiveSellPosition(magicNumber)){ if(IsConsecutiveBarCloseState(_Symbol, timeframe, requiredConsecutiveBullishBars, BAR_CLOSE_UP)){ if(IsCrossUnder(lwVolatilityLevels.sellEntryPrice, closePriceMinutesData)){ if(tradeDayMode == TDW_SELECTED_DAYS){ if(IsTradingDayAllowed(currentTime)){ OpenSel(lwVolatilityLevels.bearishStopLoss, lwVolatilityLevels.bearishTakeProfit, positionSize); } }else{ OpenSel(lwVolatilityLevels.bearishStopLoss, lwVolatilityLevels.bearishTakeProfit, positionSize); } } } } } }
Bevor ein Handel ausgeführt wird, müssen immer drei Bedingungen erfüllt sein. Es gibt keine aktive Position. Das ausgewählte Muster ist gültig. Der Filter des Handelstages ermöglicht die Ausführung. Nur wenn alle Bedingungen übereinstimmen, wird ein Auftrag erteilt. Diese Struktur ermöglicht es jeder Strategie, unabhängig zu operieren, während sie sich dieselbe Ausführungsmaschine teilen.
Platzierung der Ausstiegslogik zum richtigen Zeitpunkt
Die Ausstiegslogik wird an zwei Stellen ausgewertet. Bei einer neuen Barbildung werden die Ausgänge für den ersten profitablen offenen Modus überprüft. Bei jedem Tick werden die Ausstiegsbedingungen des kerzenbasierten Modus überprüft.
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick(){ ... //--- Run this block only when a new bar is detected on the selected timeframe if(IsNewBar(_Symbol, timeframe, lastBarOpenTime)){ //--- Increment the number of completed bars since the position was opened if(barsSinceEntry > 0){ barsSinceEntry = barsSinceEntry + 1; } //--- Handle exit conditions for the currently active position based on the configured take-profit mode if(takeProfitMode == TP_FIRST_PROFITABLE_OPEN){ ManageOpenPositionExits(); } ... } //--- Handle exit conditions for the currently active position based on the configured take-profit mode if(takeProfitMode == TP_AFTER_N_CANDLES){ ManageOpenPositionExits(); } ... }
Durch diese Trennung wird sichergestellt, dass jedes Ausstiegsmodell bei der richtigen Frequenz ausgewertet wird, ohne dass eine unnötige Verarbeitung stattfindet.
Konfigurieren einer sauberen Chart-Umgebung
Schließlich bereiten wir den visuellen Arbeitsbereich vor.
//+------------------------------------------------------------------+ //| 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; }
Die Funktion für das Erscheinungsbild des Charts entfernt das Gitter, wählt den Kerzenmodus aus, legt klare Hintergrund- und Vordergrundfarben fest und weist konsistente Kerzenfarben zu. Dadurch wird eine saubere Umgebung geschaffen, in der die Kursentwicklung und die ausgeführten Trades ohne Ablenkung beobachtet werden können. Die Funktion wird während der Initialisierung aufgerufen, sodass der Chart sofort beim Start des Expert Advisors konfiguriert wird.
//+------------------------------------------------------------------+ //| 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); }
An diesem Punkt ist der Expert Advisor vollständig. Jedes Muster kann objektiv erkannt werden. Jedes Volatilitätsniveau wird konsistent projiziert. Jeder Handel wird nach Regeln gefiltert, bemessen, ausgeführt und beendet. Jede Strategie läuft unabhängig innerhalb desselben Rahmens. Das System ist nun bereit für Tests.
Testen der Muster mit Gold
Testumgebung und Konfiguration
Bevor auf die einzelnen Strategien eingegangen wird, ist es wichtig, das Umfeld zu beschreiben, in dem alle Versuche durchgeführt wurden. Jede Strategie wurde unter denselben kontrollierten Bedingungen getestet, um vergleichbare Ergebnisse zu gewährleisten. Alle Tests wurden mit den folgenden Einstellungen durchgeführt:
- Instrument: Gold XAUUSD
- Startguthaben: 10.000 Dollar
- Testzeitraum: 1. Januar 2025 bis 30. Dezember 2025
- Zeitrahmen: Täglich
- Stop-Loss: Prozentsatz der Spanne des Vortages
- Take-Profit: Risiko-Ertrags-Modell
- Handelstage: Alle Tage erlaubt
- Größe der Position: Prozentsatz des Kontosaldos
Dieses Setup ist eng an die ursprünglichen Studien von Larry Williams angelehnt und bietet eine realistische Handelsumgebung.
Grundlegende Strategie: Kauf der Eröffnung
Dieser erste Test stellt das einfachste mögliche Modell dar. Eine Position wird zu Beginn eines jeden Tages eröffnet und am Ende dieses Tages geschlossen.
Ergebnisse:
- Reingewinn: 8.187,44 Dollar
- Gewinnrate: 50,40 Prozent
Einsicht

Dieses Ergebnis bestätigt einen wichtigen Punkt des Buches. Wenn sich ein Markt stark entwickelt, kann selbst eine naive Strategie Gewinn bringen. Gold war im Jahr 2025 auf breiter Front im Aufwärtstrend. Durch Käufe bei jeder Eröffnung konnte das System von dieser Richtungsvorgabe profitieren. Die Gewinnquote bleibt jedoch nahezu zufällig. Der Gewinn ergibt sich hier hauptsächlich aus der Marktrichtung und nicht aus der Qualität der Muster. Mit dieser Strategie wird ein nützliches Referenzniveau für alle anderen festgelegt.
Kaufen nach einem niedrigeren Schlusskurs
Ergebnisse:
- Reingewinn: 662,40 Dollar
- Gewinnrate: 36,36 Prozent
Einsicht

Diese Variante schneidet schlecht ab. Ein einziger rückläufiger Tag gibt nur wenig Aufschluss über eine Erschöpfung oder Umkehrung. Die niedrige Gewinnquote zeigt, dass die meisten Rücksetzer eher anhalten als umkehren. Dies bestätigt, dass nicht alle Muster das gleiche Vertrauen verdienen.
Kaufen nach drei aufeinanderfolgenden tieferen Schlusskursen
Ergebnisse:
- Gesamtnettogewinn: 1.309,55 Dollar
- Gewinnrate: 71,43 Prozent
Einsicht

Hier ändert sich das Bild dramatisch. Drei aufeinanderfolgende rückläufige Schlusskurse bedeuten emotionalen Druck und kurzfristige Erschöpfung. Die Gewinnrate steigt stark an, und die Rentabilität verbessert sich. Dies unterstützt eine der Schlüsselideen von Larry Williams. Muster, die extremes Verhalten darstellen, bieten einen echten Vorteil. Selektivität verbessert eindeutig die Leistung.
Aufwärtstrend mit Rücksetzern
Ergebnisse:
- Reingewinn: 1.343,43 Dollar
- Gewinnrate: 100,00 Prozent
Einsicht

Dies ist der stärkste Wert in der gesamten Studie. Das Muster kombiniert zwei Kräfte:
- Langfristige Trendbestätigung
- Einstieg bei einem kurzfristigen Rücksetzer
Die Trades werden nach einer vorübergehenden Schwäche in Trendrichtung eröffnet. Auch wenn die Stichprobengröße gering ist, zeigt das Ergebnis, warum dieses Modell zu den Favoriten von Larry Williams gehört. Ein Trend mit Rücksetzern führt zu hochwertigen Gelegenheiten. Dies bestätigt, dass die Struktur wichtiger ist als die reine Frequenz.
Outside Bar mit niedrigerem Schlusskurs
Ergebnisse:
- Reingewinn: 0,00 Dollar
- Gewinnrate: 0,00 Prozent
Einsicht

Während des gesamten Jahres wurden keine Trades ausgelöst. Dieses Ergebnis ist ebenso wichtig wie jedes andere gewinnbringende Ergebnis. Larry Williams warnte bereits, dass dieses Muster nur selten auftritt. Die Tests bestätigen, dass die Knappheit real ist. Seltene Muster mögen leistungsfähig sein, aber sie sind als eigenständige Systeme ungeeignet. Sie eignen sich am besten als Filter oder gelegentliche Gelegenheitsmodelle.
Drei aufeinanderfolgende bullische Kerzen mit abnehmender Stärke
Ergebnisse:
- Nettogewinn insgesamt: minus -1.043,81 Dollar
- Gewinnrate: 0,00 Prozent
Einsicht

Diese Strategie schneidet am schlechtesten von allen ab. Der Versuch, in einem Jahr der Hausse auf die Stärke zu verzichten, führt zu wiederholten Misserfolgen. Die Dynamik hält an und kehrt sich nicht um. Dies bestätigt ein zentrales Prinzip. Gegenläufige Tendenzen sind gefährlich, wenn sie nicht durch einen starken Kontext unterstützt werden. Ohne Erschöpfungs- oder Volatilitätsfilter wird das Fading zu einem Verlustgeschäft. Dieser Test macht deutlich, warum Larry Williams lieber mit dem vorherrschenden Marktdruck und nicht gegen ihn handelt.
Strategieübergreifende Beobachtungen
Aus diesen Ergebnissen ergeben sich mehrere wichtige Schlussfolgerungen.
Die Richtung des Marktes ist wichtig
Die Basisstrategie schnitt nur deshalb gut ab, weil der Goldpreis stark anstieg. In einem seitwärts tendierenden oder rückläufigen Jahr würde das gleiche Modell wahrscheinlich scheitern. Die Muster müssen immer im Kontext des Marktes bewertet werden.
Selektivität verbessert die Qualität
Wenn die Muster selektiver werden, verbessert sich die Leistung.
- Ein niedrigerer Schlusskurs schneidet schlecht ab
- Drei niedrigere Schlusskurse zeigen eine gute Performance
- Trend plus Rücksetzer schneidet am besten ab
Die Filterung verringert die Handelshäufigkeit, erhöht aber die Handelsqualität. Dies spricht dafür, dass weniger und besser strukturierte Trades zu besseren Ergebnissen führen.
Gefühlsbasierte Muster funktionieren besser
Die stärksten Muster stellen alle emotionales Verhalten dar.
- Mehrere niedrigere Schlusskurse
- Rücksetzer in Trends
- Erschöpfung nach Druck
Diese Muster erfassen eher kurzfristige Ungleichgewichte als die visuelle Form. Dies bestätigt die Ansicht von Larry Williams, dass Emotionen kurzfristige Chancen bestimmen.
Nachlassendes Momentum ist gefährlich
Die Verliererstrategie zeigt das Risiko, dass eine starke Dynamik nachlässt. Ohne Anzeichen für eine Erschöpfung kämpfen Gegenbewegungen gegen den Marktdruck an. Dies erklärt, warum viele Umkehrsysteme in Trendumfeldern versagen.
Was uns diese Tests lehren
Diese Studie bestätigt mehrere der zentralen Ideen von Larry Williams.
- Kurzfristig sind die Märkte nicht zufällig
- Manche Tage und Muster bieten einen echten Vorteil
- Selektivität schafft Stabilität
- Trendanpassung verbessert Überleben
Vor allem aber zeigt es, dass Muster getestet und nicht angenommen werden müssen. Manche Ideen sehen in der Theorie gut aus, verschwinden aber in der Realität. Andere erzeugen in aller Stille einen beständigen Vorsprung.
Warum das wichtig ist
Der Zweck dieser Testphase ist nicht, ein perfektes System zu finden. Es geht darum, eine disziplinierte Methode zur Untersuchung des Marktverhaltens aufzuzeigen. Jedes Muster in diesem Artikel kann jetzt sein:
- Erneut auf anderen Märkten testen
- Mit Filtern kombinieren
- Mit Volatilitätsbedingungen erweitern
- Zeitübergreifend studieren
Dies bildet eine Grundlage für systematische Forschung und nicht für subjektive Überzeugungen. Und das ist letztlich die eigentliche Lektion von Muster zum Gewinn.
Schlussfolgerung
In diesem Artikel haben wir mehr getan, als Ideen aus einem Buch in Code umzusetzen. Wir haben ein kleines Forschungssystem entwickelt, mit dem Muster auf disziplinierte Weise getestet, verglichen und gemessen werden können. Schritt für Schritt haben wir die kurzfristigen Beobachtungen von Larry Williams in einen funktionierenden Expert Advisor umgewandelt, der ausgeführt, wiederholt und anhand historischer Daten überprüft werden kann.
Die Ergebnisse zeigen eine wichtige Lektion. Einige Muster bieten einen klaren Vorteil, andere funktionieren nur unter bestimmten Bedingungen, und einige versagen, wenn sie dem realen Marktverhalten ausgesetzt sind. Dies bestätigt eines der Hauptthemen des Buches. Die Märkte sind auf kurze Sicht nicht zufällig, aber nicht jedes Muster eignet sich für unser Kapital. Nur diejenigen, die eine objektive Prüfung überstehen, sind der Aufmerksamkeit wert.
Der wahre Wert dieser Arbeit liegt nicht in den Zahlen allein. Das liegt an dem geschaffenen Rahmen. Wir haben jetzt ein flexibles System, das viele Ideen aufnehmen, konsistente Risikoregeln anwenden, Trades nach Zeit filtern und die Performance ohne Emotionen bewerten kann. Dieser Ansatz ermöglicht es der Handelsforschung, vom Glauben zum Beweis zu gelangen.
Von hier aus ist der Weg frei. Neue Muster können hinzugefügt werden. Bestehende Regeln können verfeinert werden. Es können verschiedene Märkte, Zeitrahmen und Risikomodelle getestet werden. Jede Änderung wird zu einem kontrollierten Experiment und nicht zu einer Vermutung.
Letztendlich geht es beim profitablen Handel nicht darum, ein einziges perfektes Muster zu finden. Es geht darum, ein Verfahren zu entwickeln, das herausfindet, was wirklich funktioniert, das Kapital schützt, wenn es nicht funktioniert, und das durch disziplinierte Forschung ständig verbessert wird.
Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/21063
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.
Integration externer Anwendungen mit MQL5 Community OAuth
MQL5-Handelswerkzeuge (Teil 14): Pixelgenaues, scrollbares Textpanel mit Anti-Aliasing und abgerundeter Scrollleiste
Eine alternative Log-datei mit der Verwendung der HTML und CSS
Der MQL5 Standard Library Explorer (Teil 6): Optimierung eines generierten Expert Advisors
- 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.