Larry Williams’ Geheimnisse des Marktes (Teil 6): Messung von Volatilitätsausbrüchen anhand der Swings des Marktes
Einführung
Die Volatilität ist das Lebenselixier des Breakout-Tradings, wird aber oft als eindimensionales Konzept behandelt. Im letzten Artikel dieser Serie haben wir untersucht, wie Larry Williams die Volatilität anhand der Spanne der letzten Handelsperiode gemessen hat, und gezeigt, wie diese Idee in einen vollautomatischen Expert Advisor in MQL5 umgesetzt werden kann. Dieser Ansatz ist zwar effektiv, stellt aber nur eine Möglichkeit dar, die Marktexpansion zu betrachten.
In diesem Artikel richten wir unsere Aufmerksamkeit auf eine andere Perspektive – die Messung der Volatilität durch Marktschwankungen. Larry Williams vertritt die Ansicht, dass Preisschwankungen und nicht die Spannen einzelner Perioden Aufschluss darüber geben können, wie sich Käufer und Verkäufer unter der Oberfläche des Marktes positionieren. Indem wir untersuchen, wie weit sich der Kurs in den letzten Tagen zwischen den wichtigsten Swing-Punkten bewegt hat, erhalten wir einen Einblick in potenzielle Volatilitätsausweitungen, bevor sie auftreten.
Das Ziel dieses Artikels ist nicht die Optimierung, Kurvenanpassung oder das Filtern von Trades. Stattdessen liegt der Schwerpunkt auf dem Verständnis und der Automatisierung des beschriebenen Rohkonzepts, wobei die Implementierung flexibel und geräteunabhängig bleibt. Wir werden die Swing-basierte Volatilitätsberechnung Schritt für Schritt aufschlüsseln, sie in präzise Handelsregeln umsetzen und als wiederverwendbaren, gut strukturierten MQL5 Expert Advisor implementieren.
Verstehen der Swing-basierten Volatilitätsmessung
Larry Williams schlägt eine alternative Methode zur Schätzung der kurzfristigen Volatilität vor, indem er die jüngsten Kursschwankungen misst, anstatt sich auf Indikatoren wie ATR oder Standardabweichung zu verlassen. Der Grundgedanke ist einfach: Die jüngsten Kursbewegungen liefern eine praktische Einschätzung, wie weit sich der Markt in der nächsten Sitzung bewegen kann.
Anstatt einen einzigen Swing zu messen, werden zwei verschiedene Swing-Distanzen anhand der Kursdaten der vorhergehenden abgeschlossenen Bar berechnet. Diese Swings werden in dem Moment ausgewertet, in dem eine neue Bar geöffnet wird, bevor eine Handelsentscheidung getroffen wird.
Larry definiert zwei explizite Swing-Messungen. Der erste Swing misst den Abstand zwischen dem Höchststand, der vor drei Handelstagen verzeichnet wurde, und dem Tiefststand des letzten abgeschlossenen Tages.

Damit wird das Ausmaß der Preisverschiebung nach unten während des letzten Handelsfensters erfasst.
Der zweite Swing misst den Abstand zwischen dem Höchststand von vor einem Handelstag (der Bar, der dem jüngsten unmittelbar vorausgeht) und dem Tiefststand von vor drei Handelstagen.

Damit wird die gegenläufige Bewegung innerhalb der gleichen Drei-Tage-Struktur erfasst.
Es gibt keine weiteren Hochs oder Tiefs. Insbesondere wird die Höhe der letzten abgeschlossenen Bar nicht in die zweite Berechnung einbezogen.
Beide Swing-Werte werden streng als Spannen und nicht als Richtungsänderungen behandelt. Deswegen werden ihre absoluten Werte verwendet, um sicherzustellen, dass das Ergebnis nur die Größenordnung widerspiegelt, unabhängig davon, ob der Markt gestiegen oder gefallen ist.
Nach der Berechnung der beiden Swing-Distanzen wird nur der größere Wert beibehalten. Larry Williams betrachtet diesen Wert als das aktuelle Maß für die Volatilität, da er die stärkste Preisbewegung darstellt, die in der jüngsten Marktstruktur beobachtet wurde, unabhängig von der Richtung.
Dieser ausgewählte Swing-Wert wird dann für die Projektion von Ausbruchsniveaus ab der Eröffnung der neuen Handelsperiode verwendet.

Die Kauf- und Verkaufsschwellen werden berechnet, indem konfigurierbare Prozentsätze dieser Swing-Range zum Eröffnungskurs addiert oder subtrahiert werden. Der Handel wird nur dann ausgelöst, wenn der Kurs über diese prognostizierten Werte hinausgeht, was die Volatilität eher bestätigt als vorhersagt.
Umsetzung des Konzepts in Handelsregeln
Bei der Eröffnung von jeder neuen Bar des ausgewählten Zeitrahmens berechnet der Expert Advisor die Swing-Range mithilfe der bereits erläuterten Swing-Messmethode von Larry Williams. Diese Swing-Range bildet die Grundlage für alle Entscheidungen, die während des laufenden Handelszeitraums getroffen werden. Ausgehend von diesem Wert prognostiziert der Expert Advisor zwei wichtige Kursniveaus. Das Eröffnungsniveau für den Kauf wird berechnet, indem ein nutzerdefinierter Prozentsatz der Swing-Range zum aktuellen Kurs addiert wird. Das Eröffnungsniveau eines Verkaufs wird berechnet, indem ein nutzerdefinierter Prozentsatz der gleichen Swing-Range vom aktuellen Preis abgezogen wird. Diese projizierten Werte werden gespeichert und bleiben unverändert, bis eine neue Bar geöffnet wird.
Während der aktiven Handelsperiode wird das Kursgeschehen Tick für Tick überwacht. Wenn der Kurs das Eröffnungsniveau überschreitet, kauft der Expert Advisor mit einer Marktorder. Wenn der Kurs unter die Verkaufseingangsschwelle fällt, wird eine Marktorder für einen Verkauf erteilt. Damit Sie es wissen: Es werden keine schwebenden Aufträge verwendet. Alle Trades werden am Markt direkt ausgeführt, wenn ein gültiger Ausbruch erfolgt.
Das Risikomanagement steht in direktem Zusammenhang mit der gemessenen Swing-Range. Stop-Loss für jeden Handel wird als nutzerdefinierter Prozentsatz der gleichen Swing-Range berechnet, die auch für die Eröffnung verwendet wird. Take-Profit wird dann anhand eines Risiko-Ertrags-Ansatzes bestimmt. Der Abstand zwischen dem Eröffnungskurs und dem Stop-Loss definiert das Risiko. Diese Risikodistanz wird mit einem konfigurierbaren Belohnungsfaktor multipliziert, um das Take-Profit-Niveau zu ermitteln. Dadurch wird sichergestellt, dass jeder Handel einer einheitlichen und kontrollierten Risikostruktur folgt.
Zu jedem Zeitpunkt ist nur eine Position erlaubt. Sobald eine Position eröffnet ist, können keine weiteren ausgelöst werden, bis die aktuelle Position geschlossen ist. Erreicht der Kurs während des gesamten Handelszeitraums weder die Kauf- noch die Verkaufseingangsmarke, wird kein Handel getätigt. Wenn sich eine neue Bar öffnet, werden alle zuvor berechneten Niveaus verworfen und neue für den nächsten Zeitraum berechnet.
Der Expert Advisor ermöglicht es dem Händler auch, die zulässige Handelsrichtung zu kontrollieren. Der Nutzer kann den Handel auf Long- oder Short-Positionen beschränken oder beide Richtungen zulassen. Diese Funktion ist praktisch für Händler, die eine diskretionäre Trendanalyse durchführen und es vorziehen, nur in der vorherrschenden Marktrichtung zu handeln.
Die Positionsgrößenbestimmung ist durch zwei Modi zur Berechnung der Losgröße flexibel. Im manuellen Modus verwendet der Expert Advisor eine feste, vom Nutzer festgelegte Losgröße. Im automatischen Modus wird die Positionsgröße auf der Grundlage eines festen Prozentsatzes des Kontosaldos berechnet. Der automatische Modus passt die Losgrößen dynamisch an, um den vordefinierten Risikoprozentsatz bei allen Trades beizubehalten, unabhängig von der Preisvolatilität oder dem Kontowachstum.
Aufbau des Expert Advisors Schritt für Schritt
Dieser Abschnitt markiert den Beginn des Aufbaus des Expert Advisors. Von hier an verlagert sich der Schwerpunkt von der Theorie zur Umsetzung. Um bequem folgen zu können, sollte der Leser bereits über grundlegende Arbeitserfahrungen mit MQL5 verfügen. Dazu gehören die Verwendung der MetaTrader 5-Plattform, das Anhängen von Expert Advisors an Charts und die Durchführung von Tests im Strategietester. Der Leser sollte auch mit MetaEditor 5 vertraut sein und in der Lage sein, Code zu schreiben, ihn zu kompilieren, Fehler zu überprüfen und gegebenenfalls zu debuggen. Programmieren lernt man, indem man es tut, und nicht, indem man es nur liest. Deshalb ist dieser Abschnitt so konzipiert, dass man ihn aktiv verfolgt.
Deswegen ist die vollständige und endgültige Quelldatei, die in diesem Artikel entwickelt wurde, als lwVolatilitySwingBreakoutExpert.mq5 beigefügt. Wenn Sie bei der schrittweisen Erstellung des Expert Advisors auf Probleme stoßen, können Sie Ihre Arbeit jederzeit mit der angehängten Datei vergleichen, um den Überblick zu behalten. Es wird dringend empfohlen, diese herunterzuladen, bevor Sie fortfahren.
Sie können damit beginnen, MetaEditor 5 zu öffnen und eine neue Expert Advisor-Datei zu erstellen. Sie können ihm jeden Namen geben, den Sie bevorzugen. Könnten Sie nach der Erstellung der Datei den mitgelieferten Standardcode in die Datei einfügen?
//+------------------------------------------------------------------+ //| lwVolatilitySwingBreakoutExpert.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_TRADE_DIRECTION { ONLY_LONG, ONLY_SHORT, TRADE_BOTH }; 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 "Volatility Breakout Parameters" input double inpBuyRangeMultiplier = 0.50; input double inpSellRangeMultiplier = 0.50; input double inpStopRangeMultiplier = 0.50; input double inpRewardValue = 4.0; input group "Trade and Risk Management" input ENUM_TRADE_DIRECTION direction = ONLY_LONG; 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; //--- Bid and Ask double askPrice; double bidPrice; //+------------------------------------------------------------------+ //| 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); bidPrice = SymbolInfoDouble (_Symbol, SYMBOL_BID); } //+------------------------------------------------------------------+ //| TradeTransaction function | //+------------------------------------------------------------------+ void OnTradeTransaction(const MqlTradeTransaction& trans, const MqlTradeRequest& request, const MqlTradeResult& result) { } //--- UTILITY FUNCTIONS //+------------------------------------------------------------------+
Dieser erste Code bildet die Struktur, auf der wir aufbauen werden.
Verstehen der Baustein-Struktur
Die Code-Bausteine sind in klar definierte Abschnitte unterteilt, die jeweils einem bestimmten Zweck dienen. Der Header-Abschnitt definiert Eigentumsinformationen und die Versionierung. Dies hat keinen Einfluss auf die Handelslogik, hilft aber, die Datei und ihren Urheber zu identifizieren.
//+------------------------------------------------------------------+ //| lwVolatilitySwingBreakoutExpert.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"
Die Aufnahme in die Standardbibliothek bringt die Klasse CTrade mit sich. Diese Klasse vereinfacht die Auftragsausführung und das Handelsmanagement und wird später bei der Platzierung von Trades verwendet.
//+------------------------------------------------------------------+ //| Standard Libraries | //+------------------------------------------------------------------+ #include <Trade\Trade.mqh>
Die nutzerdefinierten Enumerationen werden als Nächstes definiert.
//--- CUSTOM ENUMERATIONS enum ENUM_TRADE_DIRECTION { ONLY_LONG, ONLY_SHORT, TRADE_BOTH }; enum ENUM_LOT_SIZE_INPUT_MODE { MODE_MANUAL, MODE_AUTO };
Diese ermöglichen es dem Nutzer, die Handelsrichtung und das Positionsgrößenverhalten mit lesbaren Optionen statt mit numerischen Werten zu steuern. Dies erhöht sowohl die Übersichtlichkeit als auch die Sicherheit bei der Konfiguration des Expert Advisors.
Im Bereich der Eingabevariablen werden alle konfigurierbaren Parameter für den Nutzer sichtbar.
//+------------------------------------------------------------------+ //| User input variables | //+------------------------------------------------------------------+ input group "Information" input ulong magicNumber = 254700680002; input ENUM_TIMEFRAMES timeframe = PERIOD_CURRENT; input group "Volatility Breakout Parameters" input double inpBuyRangeMultiplier = 0.50; input double inpSellRangeMultiplier = 0.50; input double inpStopRangeMultiplier = 0.50; input double inpRewardValue = 4.0; input group "Trade and Risk Management" input ENUM_TRADE_DIRECTION direction = ONLY_LONG; input ENUM_LOT_SIZE_INPUT_MODE lotSizeMode = MODE_AUTO; input double riskPerTradePercent = 1.0; input double positionSize = 0.1;
Diese Eingaben steuern Handelsrichtung, Volatilitätsmultiplikatoren, Stop-Loss-Verhalten, Renditeerwartungen und Positionsgrößenlogik. Jeder dieser Inputs wirkt sich direkt darauf aus, wie das Volatilitätskonzept von Larry Williams in ausführbare Regeln umgesetzt wird.
Es folgen globale Variablen.
//+------------------------------------------------------------------+ //| Global Variables | //+------------------------------------------------------------------+ //--- Create a CTrade object to handle trading operations CTrade Trade; //--- Bid and Ask double askPrice; double bidPrice;
Hier erstellen wir ein CTrade-Objekt zur Ausführung von Trades und definieren Variablen zum Speichern der aktuellen Geld- und Briefkurse. Diese Preise werden bei jedem Tick aktualisiert und für genaue Handelsberechnungen verwendet.
Die Funktion OnInit wird einmal beim Start des Expert Advisors ausgeführt.
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit(){ //--- Assign a unique magic number to identify trades opened by this EA Trade.SetExpertMagicNumber(magicNumber); return(INIT_SUCCEEDED); }
Seine Rolle ist hier wesentlich und einfach. Es weist dem CTrade-Objekt eine eindeutige magische Nummer zu, sodass alle von diesem Expert Advisor eröffneten Trades zuverlässig identifiziert werden können. In Zukunft werden wir diese Funktion verwenden, um globale Variablen zu initialisieren, die mit bekannten Werten beginnen müssen.
Die Funktion OnDeinit wird ausgeführt, wenn der Expert Advisor entfernt oder angehalten wird.
//+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason){ //--- Notify why the program stopped running Print("Program terminated! Reason code: ", reason); }
Sie meldet lediglich den Beendigungsgrund und hat keinen Einfluss auf die Handelslogik.
Die Funktion OnTick wird bei jedem Markttick aufgerufen.
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick(){ //--- Retrieve current market prices for trade execution askPrice = SymbolInfoDouble (_Symbol, SYMBOL_ASK); bidPrice = SymbolInfoDouble (_Symbol, SYMBOL_BID); }
In diesem Stadium werden nur die Geld- und Briefkurse aktualisiert. Diese Funktion wird später der zentrale Kontrollpunkt für die Ausführung unserer Strategielogik sein.
Der Abschnitt über die Nutzfunktionen ist zunächst absichtlich leer gelassen.
//--- UTILITY FUNCTIONS //+------------------------------------------------------------------+
Hier werden wir alle nutzerdefinierten Hilfsfunktionen platzieren, die die Hauptlogik des Handels unterstützen.
Erkennen einer neuen Bar
Unsere Strategie erfordert eine Neuberechnung der Niveaus nur einmal pro Handelsperiode. Um dies zu erreichen, müssen wir erkennen, wann eine neue Bar im ausgewählten Zeitrahmen geöffnet wird.
Zu diesem Zweck wird eine nutzerdefinierte Funktion in den Utility-Bereich aufgenommen.
//--- 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 Öffnungszeit der aktuellen Bar mit der zuvor aufgezeichneten Bar-Zeit. Wenn die Zeiten voneinander abweichen, hat sich eine neue Bar gebildet.
Die Funktion nimmt drei Parameter entgegen. Das Symbol und der Zeitrahmen geben an, welches Chart wir beobachten. Der dritte Parameter wird als Referenz übergeben und speichert die Eröffnungszeit der zuletzt verarbeiteten Bar. Wenn eine neue Bar erkannt wird, wird dieser Wert automatisch aktualisiert.
Um diese Logik zu unterstützen, wird eine globale Variable vom Typ datetime deklariert.
//--- To help track new bar open datetime lastBarOpenTime;
Diese Variable erfasst die Öffnungszeit der zuletzt bearbeiteten Bar. In der Funktion OnInit wird diese Variable auf Null initialisiert, um einen sauberen Ausgangszustand zu gewährleisten.
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit(){ ... //--- Initialize global variables lastBarOpenTime = 0; return(INIT_SUCCEEDED); }
Speichern der täglichen Volatilitätswerte
Jede Handelsperiode erfordert einen festen Satz von Kursniveaus, die bis zur Eröffnung der nächsten Bar gültig bleiben. Dazu gehören die Swing-Range, Eröffnungskurse, Stop-Loss- und Take-Profit-Niveaus für beide Handelsrichtungen.
Um diese Werte sauber zu speichern, wird eine nutzerdefinierte Struktur im globalen Bereich definiert.
//--- Holds all price levels derived from Larry Williams' volatility breakout calculations struct MqlLwVolatilityLevels { double dominantSwingRange; double buyEntryPrice; double sellEntryPrice; double bullishStopLoss; double bearishStopLoss; double bullishTakeProfit; double bearishTakeProfit; }; MqlLwVolatilityLevels lwVolatilityLevels;
Diese Struktur fasst alle zusammenhängenden Preisstufen zu einer einzigen logischen Einheit zusammen. Eine Instanz dieser Struktur wird unmittelbar nach ihrer Definition erstellt.
Innerhalb der Funktion OnInit wird die Strukturinstanz mit der Funktion ZeroMemory zurückgesetzt.
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit(){ ... //--- Reset Larry Williams' volatility levels ZeroMemory(lwVolatilityLevels); return(INIT_SUCCEEDED); }
Dadurch wird sichergestellt, dass alle Felder mit bekannten Werten beginnen und unbeabsichtigtes Verhalten durch nicht initialisierte Daten verhindert wird.
Berechnung der Swing-basierten Volatilitätsspanne
Die erste nutzerdefinierte Berechnungsfunktion bestimmt die von Larry Williams beschriebene swingbasierte Volatilitätsspanne.
//+------------------------------------------------------------------+ //| Calculates Larry Williams swing-based volatility range | //+------------------------------------------------------------------+ double CalculateLwSwingVolatilityRange(const string symbol, ENUM_TIMEFRAMES tf){ //--- Retrieve required highs and lows double high_3_days_ago = iHigh(symbol, tf, 4); double low_yesterday = iLow (symbol, tf, 1); double high_1_day_ago = iHigh(symbol, tf, 2); double low_3_days_ago = iLow (symbol, tf, 4); //--- Validate data if(high_3_days_ago == 0.0 || low_yesterday == 0.0 || high_1_day_ago == 0.0 || low_3_days_ago == 0.0) { return 0.0; } //--- Calculate swing distances using absolute values double swingRangeA = MathAbs(high_3_days_ago - low_yesterday); double swingRangeB = MathAbs(high_1_day_ago - low_3_days_ago); //--- Select the dominant swing double usableRange = MathMax(swingRangeA, swingRangeB); //--- Normalize for symbol precision return NormalizeDouble(usableRange, (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS)); }
Diese Funktion ruft bestimmte Höchst- und Tiefstwerte aus historischen Bars des ausgewählten Zeitrahmens ab. Die zwei Swing-Distanzen werden als absolute Werte berechnet, um die Korrektheit unabhängig von der Preisreihenfolge zu gewährleisten. Die größere der beiden Swing-Weiten wird als dominante Swing-Range gewählt.
Dieser Wert steht für die stärkste Preissteigerung des Marktes in jüngster Zeit und dient als Volatilitätsproxy für alle nachfolgenden Berechnungen. Das Ergebnis wird normalisiert, um der Preisgenauigkeit des Symbols zu entsprechen, bevor es zurückgegeben wird.
Berechnung der Eröffnungspreise
Der Eröffnungskurs für den Kauf wird berechnet, indem ein nutzerdefinierter Prozentsatz der Swing-Range zum heutigen Eröffnungskurs addiert wird. Dies lässt einen Aufwärtsausbruch über den Markt erwarten.
//+--------------------------------------------------------------------------------+ //| Calculates the bullish breakout entry price using today's open and swing range | //+--------------------------------------------------------------------------------+ double CalculateBuyEntryPrice(double todayOpen, double swingRange, double buyMultiplier){ return todayOpen + (swingRange * buyMultiplier); }
Der Eröffnungspreis für den Verkauf wird berechnet, indem ein nutzerdefinierter Prozentsatz der Swing-Range vom heutigen Eröffnungskurs abgezogen wird. Dies deutet auf ein Ausbruchsniveau nach unten, unterhalb des Marktkurses, hin.
//+--------------------------------------------------------------------------------+ //| Calculates the bearish breakout entry price using today's open and swing range | //+--------------------------------------------------------------------------------+ double CalculateSellEntryPrice(double todayOpen, double swingRange, double sellMultiplier){ return todayOpen - (swingRange * sellMultiplier); }
Beide Funktionen sind absichtlich einfach gehalten. Sie übersetzen die Volatilität in umsetzbare Preisniveaus, ohne unnötige Komplexität hinzuzufügen.
Berechnung der Stop-Loss-Niveaus
Die Stop-Loss-Niveaus werden direkt von den Eröffnungskursen abgeleitet.
//+--------------------------------------------------------------------------------------------+ //| Calculates the stop-loss price for a bullish position based on entry price and swing range | //+--------------------------------------------------------------------------------------------+ double CalculateBullishStopLoss(double entryPrice, double swingRange, double stopMultiplier){ return entryPrice - (swingRange * stopMultiplier); } //+--------------------------------------------------------------------------------------------+ //| Calculates the stop-loss price for a bearish position based on entry price and swing range | //+--------------------------------------------------------------------------------------------+ double CalculateBearishStopLoss(double entryPrice, double swingRange, double stopMultiplier){ return entryPrice + (swingRange * stopMultiplier); }
Für einen Kauf wird der Stop-Loss um einen nutzerdefinierten Teil der Swing-Range unter dem Eröffnungskurs platziert. Bei einem Verkauf wird der Stop-Loss nach derselben Logik über dem Eröffnungskurs platziert. Dadurch bleibt das Risiko proportional zur aktuellen Volatilität, und die Konsistenz zwischen den einzelnen Trades wird gewährleistet.
Berechnung von Take-Profit-Niveaus
Die Take-Profit-Niveaus werden nach einem Risiko-Ertrags-Ansatz berechnet.
//+--------------------------------------------------------------------------+ //| 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()); }
Bei Käufen bestimmt der Abstand zwischen dem Eröffnungskurs und dem Stop-Loss das Risiko. Dieser Abstand wird mit dem Ertragsfaktor multipliziert, um den Take-Profit über den Eröffnungskurs zu projizieren.
Für Verkäufe gilt die gleiche Logik in umgekehrter Richtung. Die berechneten Take-Profit-Niveaus werden auf die Genauigkeit des Symbols normalisiert, bevor sie zurückgegeben werden.
Alles in OnTick vereinen
Nachdem alle unterstützenden Funktionen vorhanden sind, vereint die OnTick-Funktion nun alles miteinander.
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick(){ ... //--- Run this block only when a new bar is detected on the selected timeframe if(IsNewBar(_Symbol, timeframe, lastBarOpenTime)){ lwVolatilityLevels.dominantSwingRange = CalculateLwSwingVolatilityRange(_Symbol, timeframe); lwVolatilityLevels.buyEntryPrice = CalculateBuyEntryPrice (askPrice, lwVolatilityLevels.dominantSwingRange, inpBuyRangeMultiplier ); lwVolatilityLevels.sellEntryPrice = CalculateSellEntryPrice(bidPrice, lwVolatilityLevels.dominantSwingRange, inpSellRangeMultiplier); lwVolatilityLevels.bullishStopLoss = CalculateBullishStopLoss(lwVolatilityLevels.buyEntryPrice, lwVolatilityLevels.dominantSwingRange, inpStopRangeMultiplier); lwVolatilityLevels.bearishStopLoss = CalculateBearishStopLoss(lwVolatilityLevels.sellEntryPrice, lwVolatilityLevels.dominantSwingRange, inpStopRangeMultiplier); lwVolatilityLevels.bullishTakeProfit = CalculateBullishTakeProfit(lwVolatilityLevels.buyEntryPrice, lwVolatilityLevels.bullishStopLoss, inpRewardValue); lwVolatilityLevels.bearishTakeProfit = CalculateBearishTakeProfit(lwVolatilityLevels.sellEntryPrice, lwVolatilityLevels.bearishStopLoss, inpRewardValue); } }
Bei jedem Tick aktualisiert der Expert Advisor zunächst die Geld- und Briefkurse. Anschließend wird geprüft, ob sich im ausgewählten Zeitrahmen eine neue Bar gebildet hat. Wenn keine neue Bar entdeckt wird, passiert nichts weiter.
Wenn eine neue Bar erkannt wird, werden alle volatilitätsbasierten Niveaus neu berechnet. Zunächst wird die Swing-Range berechnet. Eröffnungskurse, Stop-Losses und Take-Profit-Niveaus werden dann nacheinander anhand der zuvor berechneten Swing-Range abgeleitet.
Diese Werte werden in der globalen Struktur gespeichert und bleiben unverändert, bis die nächste neue Bar erkannt wird. Dieser Ansatz gewährleistet, dass Handelsentscheidungen während des Handelszeitraums auf festen, im Voraus berechneten Niveaus beruhen und nicht auf sich ständig ändernden Werten.
Zu diesem Zeitpunkt verfügt der Expert Advisor über einen vollständigen Rahmen für die Berechnung und Speicherung aller für die Handelsausführung erforderlichen Kursniveaus. Im nächsten Abschnitt werden diese Niveaus zur Auslösung von Trades und zur Verwaltung von Positionen gemäß den zuvor festgelegten Regeln verwendet.
Vervollständigen der Handelslogik und Ausführen von Trades
In diesem Stadium haben wir bereits alles, was wir benötigen, um Handelsentscheidungen zu treffen. Unsere täglichen Volatilitätsniveaus werden berechnet und gespeichert, und sie bleiben gültig, bis sich eine neue Bar bildet und neue Niveaus berechnet werden. Es bleibt zu definieren, wie und wann Trades ausgelöst werden, wie wir doppelte Positionen verhindern und wie wir Aufträge kontrolliert und konsistent ausführen.
Die Kernidee ist einfach. Sobald der Kurs eines unserer vordefinierten Eröffnungsniveaus überschreitet, eröffnen wir eine Position in dieser Richtung. Wenn das Kaufniveau zuerst überschritten wird, eröffnen wir einen Long-Trade. Wenn das Verkaufsniveau zuerst überschritten wird, eröffnen wir einen Short-Trade. Wir halten uns immer an eine strikte Regel. Es kann immer nur eine Position offen sein.
Um dies sauber zu implementieren, unterteilen wir die Logik in kleine Hilfsfunktionen.
Erkennen von Kursdurchbrüchen
Das erste Problem, das wir lösen müssen, besteht darin, zu erkennen, wann der Preis ein bestimmtes Niveau überschreitet. Wir sind nicht daran interessiert, dass der Preis einfach nur ein Niveau erreicht. Wir wollen die Bestätigung, dass sich der Preis von einer Seite des Niveaus zur anderen bewegt hat. Zu diesem Zweck definieren wir zwei Hilfsfunktionen. Die Funktion IsCrossOver erkennt, wenn der Kurs ein Niveau von unten nach oben kreuzt.
//+------------------------------------------------------------------+ //| 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; }
Diese Funktion vergleicht zwei aufeinanderfolgende einminütige Schlusskurse mit dem Zielniveau. Der Wert bei Index eins steht für den letzten abgeschlossenen Minutenbalken. Der Wert bei Index Null stellt die jüngsten Bar dar. Ein Crossover tritt auf, wenn der vorherige Schlusskurs auf oder unter dem Niveau lag und der aktuelle Schlusskurs über dem Niveau liegt. Dieser einfache Vergleich liefert ein klares und zuverlässiges Signal dafür, dass der Preis über das Niveau gestiegen ist.
Die Funktion IsCrossUnder prüft genau das Umgekehrte. Sie erkennt, wenn der Preis ein Niveau von oben nach unten überschreitet.
//+------------------------------------------------------------------+ //| 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; }
Hier ist die Logik genau andersherum. Wir bestätigen, dass der letzte Schlusskurs auf oder über diesem Niveau lag und der letzte Schlusskurs darunter. Dies zeigt uns, dass der Kurs das Niveau nach unten durchquert hat. Diese beiden Funktionen bilden zusammen die Grundlage für unsere Eingabelogik.
Speichern der Daten der Minutenpreise
Beide Crossover-Funktionen basieren auf 1-Minuten-Schlusskursdaten. Um dies zu unterstützen, definieren wir ein globales Array, das diese Daten speichert.
//--- To store minutes data double closePriceMinutesData [];
Dieses Array ist wie eine Zeitreihe zu behandeln. In MQL5 verhalten sich Arrays standardmäßig nicht wie Zeitreihen. Wenn wir die Indizierungsrichtung nicht ausdrücklich ändern, stellt der Index Null nicht die jüngsten Bar dar. Das würde unsere Crossover-Logik durchbrechen. Deswegen konfigurieren wir das Array während der Initialisierung.
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit(){ ... //--- Treat the following arrays as timeseries (index 0 becomes the most recent bar) ArraySetAsSeries(closePriceMinutesData, true); }
Diese Anweisung kehrt die Indizierungsreihenfolge um, sodass sich Index Null immer auf die jüngste Bar bezieht. Ohne diesen Schritt würden unsere Crossover-Prüfungen falsche Preiswerte verwenden, und der EA würde sich unvorhersehbar verhalten.
Aktualisierung der Minutendaten bei jedem Tick
In der Funktion OnTick aktualisieren wir unser Array mit den Minutendaten, sobald neue Kursdaten eintreffen.
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick(){ ... //--- Get some minutes data if(CopyClose(_Symbol, PERIOD_M1, 0, 7, closePriceMinutesData) == -1){ Print("Error while copying minutes datas ", GetLastError()); return; } }
Wir kopieren die letzten sieben einminütigen Schlusskurse. Obwohl wir nur die letzten beiden Werte für die Crossover-Erkennung benötigen, bietet das Kopieren einiger zusätzlicher Bars eine kleine Sicherheitsmarge und beeinträchtigt die Leistung nicht.
Wenn die Datenkopie fehlschlägt, stoppen wir die Ausführung für diesen Tick. Ein Handeln auf der Grundlage unvollständiger oder fehlender Preisdaten würde zu unzuverlässigen Handelsentscheidungen führen.
Verhindern von mehreren aktiven Positionen
Der Preis kann dasselbe Niveau mehrmals überqueren, insbesondere bei volatilen Bedingungen. Ohne entsprechende Sicherheitsvorkehrungen könnte dies dazu führen, dass mehrere Trades in kurzer Folge eröffnet werden.
Um dies zu verhindern, definieren wir zwei Funktionen, die prüfen, ob dieser EA bereits eine aktive Position hat. Um das Vorhandensein von Long-Positionen zu prüfen, definieren wir die folgende nutzerdefinierte Funktion:
//+------------------------------------------------------------------+ //| 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 Funktion durchläuft in einer Schleife alle offenen Positionen des Terminals. Für jede Position werden zwei Bedingungen geprüft. Zunächst muss die magische Zahl mit der magischen Zahl des Expert Advisors übereinstimmen. Dadurch wird sichergestellt, dass wir nur Trades prüfen, die von diesem Expert Advisor eröffnet wurden. Zweitens muss die Positionsart eine Kaufposition sein. Wenn beide Bedingungen erfüllt sind, gibt die Funktion sofort true zurück. Wenn keine passende Position gefunden wird, gibt die Funktion false zurück.
Um zu prüfen, ob eine aktive Short-Position vorliegt, definieren wir die folgende Funktion:
//+------------------------------------------------------------------+ //| 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 Funktion folgt der gleichen Struktur wie die vorherige, prüft aber ausdrücklich auf Short-Positionen. Mit diesen beiden Kontrollen wird sichergestellt, dass wir nie mehr als einen Trade zur gleichen Zeit eröffnen.
Automatische Positionsgrößenbestimmung nach Risiko
Um die automatische Losgröße zu unterstützen, definieren wir eine Funktion, die die Positionsgröße auf der Grundlage eines festen Prozentsatzes des Kontostands berechnet.
//+----------------------------------------------------------------------------------+ //| 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 Logik ist ganz einfach. Wir berechnen zunächst den Geldbetrag, den wir pro Handel zu riskieren bereit sind. Dieser richtet sich nach dem Kontostand und dem eingestellten Risikoprozentsatz. Als Nächstes wird die Kontraktgröße für das aktuelle Symbol abgefragt. Daran kann abgelesen werden, wie viel Wert eine ganze Losgröße hat. Schließlich wird der Risikobetrag durch den Stop-Loss-Abstand geteilt. Das Ergebnis ist die Positionsgröße, die mit unseren Risikoregeln übereinstimmt. Der Wert ist auf zwei Dezimalstellen normalisiert, um den Anforderungen der Makler zu entsprechen.
Ausführen von Kaufaufträgen
Jetzt definieren wir die Funktion, die eine Marktkaufposition eröffnet.
//+------------------------------------------------------------------+ //| Function to open a market buy position | //+------------------------------------------------------------------+ bool OpenBuy(double entryPrice, double stopLoss, double takeProfit, double lotSize){ if(lotSizeMode == MODE_AUTO){ lotSize = CalculatePositionSizeByRisk(lwVolatilityLevels.buyEntryPrice - lwVolatilityLevels.bullishStopLoss); } if(!Trade.Buy(lotSize, _Symbol, entryPrice, stopLoss, takeProfit)){ Print("Error while executing a market buy order: ", GetLastError()); Print(Trade.ResultRetcode()); Print(Trade.ResultComment()); return false; } return true; }
Wenn die automatische Losgrößenbestimmung aktiviert ist, berechnet die Funktion die Positionsgröße auf der Grundlage des Abstands zwischen dem Eröffnungskurs und dem Stop-Loss. Der Handel wird dann mit der Klasse CTrade ausgeführt. Wenn der Auftrag fehlschlägt, protokollieren wir detaillierte Fehlerinformationen. Wenn der Auftrag erfolgreich ist, gibt die Funktion true zurück.
Die Verkaufsfunktion folgt der gleichen Struktur.
//+------------------------------------------------------------------+ //| Function to open a market sell position | //+------------------------------------------------------------------+ bool OpenSel(double entryPrice, double stopLoss, double takeProfit, double lotSize){ if(lotSizeMode == MODE_AUTO){ lotSize = CalculatePositionSizeByRisk(lwVolatilityLevels.bearishStopLoss - lwVolatilityLevels.sellEntryPrice); } if(!Trade.Sell(lotSize, _Symbol, entryPrice, stopLoss, takeProfit)){ Print("Error while executing a market sell order: ", GetLastError()); Print(Trade.ResultRetcode()); Print(Trade.ResultComment()); return false; } return true; }
Der einzige Unterschied besteht in der Richtung des Stoppabstands und in der Verwendung eines Verkaufsauftrags. Die Struktur bleibt konsistent, was die Pflege des Codes erleichtert.
Alles in OnTick vereinen
Da nun alle Bausteine vorhanden sind, können wir alles in der OnTick-Funktion zusammenfügen.
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick(){ ... //--- Long position logic if(direction == TRADE_BOTH || direction == ONLY_LONG){ if(IsCrossOver(lwVolatilityLevels.buyEntryPrice, closePriceMinutesData)){ if(!IsThereAnActiveBuyPosition(magicNumber) && !IsThereAnActiveSellPosition(magicNumber)){ OpenBuy(askPrice, lwVolatilityLevels.bullishStopLoss, lwVolatilityLevels.bullishTakeProfit, positionSize); } } } //--- Short position logic if(direction == TRADE_BOTH || direction == ONLY_SHORT){ if(IsCrossUnder(lwVolatilityLevels.sellEntryPrice, closePriceMinutesData)){ if(!IsThereAnActiveBuyPosition(magicNumber) && !IsThereAnActiveSellPosition(magicNumber)){ OpenSel(bidPrice, lwVolatilityLevels.bearishStopLoss, lwVolatilityLevels.bearishTakeProfit, positionSize); } } } }
Dieser Block prüft, ob Long-Trades erlaubt sind. Wenn dies der Fall ist, prüfen wir, ob ein Crossover oberhalb der Eröffnungsschwelle zum Kauf vorliegt. Wenn ein Crossover erkannt wird und es keine aktiven Positionen gibt, eröffnen wir einen Kauf unter Verwendung der vorher berechneten Niveaus. Die kurze Logik folgt der gleichen Struktur. Durch diese Strukturierung der Logik gewährleisten wir Klarheit, Kontrolle und die strikte Einhaltung der Strategieregeln.
Konfigurieren der Chart-Darstellung
Vor dem Testen verbessern wir die Lesbarkeit des Charts, indem wir die visuellen Einstellungen konfigurieren. Definieren wir die folgende nutzerdefinierte Nutzenfunktion:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
Diese Funktion setzt einen sauberen weißen Hintergrund, entfernt das Raster, erzwingt den Kerzen-Modus und wendet klare Farben für Aufwärts- und Abwärtskerzen an. Wenn eine Konfiguration fehlschlägt, meldet die Funktion den Fehler und bricht die Ausführung ab. Wir rufen diese Funktion während der Initialisierung auf.
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit(){ ... //--- To configure the chart's appearance if(!ConfigureChartAppearance()){ Print("Error while configuring chart appearance", GetLastError()); return INIT_FAILED; } }
Auf diese Weise wird sichergestellt, dass das Chart vorbereitet ist, bevor die Handelsaktivitäten beginnen.
Zu diesem Zeitpunkt ist der Expert Advisor vollständig implementiert. Jetzt können wir den Quellcode kompilieren. Wenn alles korrekt hinzugefügt wurde, sollte der Code ohne Fehler kompiliert werden. Sollten Probleme auftreten, kann die beigefügte Quelldatei lwVolatilitySwingBreakoutExpert.mq5 als Referenz verwendet werden.
Testen des Expert Advisors
Durch das Testen können wir überprüfen, ob sich die Handelslogik wie erwartet verhält, und bewerten, wie sich die Strategie unter historischen Marktbedingungen verhält. In diesem Abschnitt konzentrieren wir uns auf den Backtest des EA mit dem MetaTrader 5 Strategietester.
Backtest-Umgebung und Einrichtung
Ein Backtest wurde für Gold (Symbol XAUUSD) mit einem täglichen Zeitrahmen durchgeführt. Der Testzeitraum erstreckt sich vom 1. Januar 2025 bis zum 31. Dezember 2025. Damit steht eine vollständige einjährige Stichprobe zur Verfügung, die ausreicht, um zu beobachten, wie die Strategie unter verschiedenen Marktbedingungen reagiert.
Der EA wurde für den Betrieb im Modus ONLY_LONG konfiguriert. Das bedeutet, dass der Algorithmus nur Long-Positionen eingehen darf. Short-Trades wurden vollständig deaktiviert. Bei jedem Handel wurde genau ein Prozent des Kontosaldos riskiert, wobei die zuvor implementierte Logik der automatischen Positionsgrößenbestimmung zum Einsatz kam.
Um sicherzustellen, dass die Ergebnisse reproduzierbar sind, wurden diesem Artikel zwei wichtige Dateien beigefügt. Die erste Datei ist configurations.ini. Diese Datei enthält alle Umgebungseinstellungen des Strategietesters, wie Symbol, Zeitrahmen und Testbereich. Die zweite Datei ist parameters.set. Diese Datei enthält die genauen Eingabeparameter, die während des Tests verwendet wurden, einschließlich Risikoeinstellungen und Volatilitätsmultiplikatoren. Wenn Sie beide Dateien in den Strategietester laden, können Sie ähnliche Testbedingungen einrichten.
Backtest-Ergebnisse im Überblick
Der Backtest begann mit einem anfänglichen Kontostand von 10.000 $. Am Ende des Testzeitraums erwirtschaftete das System einen Nettogewinn von insgesamt 4.450,23 $. Dies entspricht einer Investitionsrendite von etwas mehr als vierundvierzig Prozent innerhalb eines Jahres.

Das System verzeichnete eine Gewinnquote von 48,39 %. Dies mag auf den ersten Blick bescheiden erscheinen, entspricht aber der Risiko-Ertrags-Struktur der Strategie. Eine profitable Performance wird nicht durch eine hohe Gewinnquote erreicht, sondern durch disziplinierte Risikokontrolle und eine günstige Renditemessung.
Die auf dem Screenshot gezeigte Kapitalkurve ist glatt und gleichmäßig.

Es gibt keine starken Rücksetzer oder plötzliche Einbrüche. Dieses Verhalten deutet auf kontrollierte Drawdowns und eine konsequente Ausführung der Handelsregeln hin.
Ich möchte Sie darauf hinweisen, dass dieser Test nur eine Konfiguration und einen Markt repräsentiert. Die Strategie wurde absichtlich flexibel gestaltet. Eingabeparameter wie Risikoprozentsatz, Volatilitätsmultiplikatoren und Handelsrichtung können alle angepasst werden. So können die Händler verschiedene Varianten testen und die Logik an ihre eigenen Vorstellungen anpassen.
Ich wollte Sie nur wissen lassen, dass das primäre Ziel dieses Artikels nicht ist, ein fertiges oder optimiertes Handelssystem zu präsentieren. Ziel ist es, zu zeigen, wie die Methodik von Larry Williams in einen funktionierenden Algorithmus umgesetzt werden kann, den Händler studieren, verändern und erweitern können. Die Leser werden ermutigt, ihre eigenen Tests durchzuführen. Verschiedene Symbole, Zeitrahmen und Parameterkombinationen können zu unterschiedlichen Ergebnissen führen. Durch Experimentieren lassen sich Varianten entdecken, die den individuellen Handelspräferenzen und der Risikotoleranz besser entsprechen.
Wir laden alle dazu ein, ihre Beobachtungen, Testergebnisse und Erkenntnisse in den Kommentaren mitzuteilen. Durch gemeinsames Experimentieren und Diskutieren kommen oft Ideen zum Vorschein, die aus einem einzelnen Backtest nicht sofort ersichtlich sind.
Schlussfolgerung
In diesem Artikel haben wir das swingbasierte Volatilitätsausbruchskonzept von Larry Williams erfolgreich in einen voll funktionsfähigen Expert Advisor für MetaTrader 5 umgesetzt. Ausgehend von den ursprünglichen Erklärungen in seinem Buch haben wir die Swing-Messungen sorgfältig interpretiert, ihre praktischen Auswirkungen geklärt und sie in präzise, regelbasierte Berechnungen umgewandelt, die sich für die Automatisierung eignen.
Wir haben ein komplettes Handelssystem entworfen und in einem schrittweisen Prozess implementiert. Dazu gehörten das Erkennen neuer Handelsperioden, die Messung der vorherrschenden Swing-Range, die Projektion von Ausbruchsniveaus für den Eröffnung, die Festlegung von Stop-Loss- und Take-Profit-Zielen und die Durchsetzung strenger Handelsmanagementregeln. Jede Komponente wurde mit Klarheit und Zweckmäßigkeit entwickelt, was zu einem strukturierten, lesbaren und leicht zu erweiternden EA führte.
Über die strategische Logik hinaus zeigte der Artikel solide technische Praktiken in MQL5. Wir behandelten die Zustandsverwaltung mithilfe von Strukturen, die sichere Initialisierung globaler Variablen, die zuverlässige Crossover-Erkennung, die Positionsfilterung mithilfe magischer Zahlen sowie die manuelle und risikobasierte Positionsbestimmung. Dies sind wesentliche Bausteine für jedes ernsthafte algorithmische Handelssystem.
Der Backtest-Abschnitt hat gezeigt, dass die Strategie bei disziplinierter Anwendung und angemessener Risikokontrolle konsistent funktionieren kann. Noch wichtiger ist, dass das System absichtlich so konzipiert wurde, dass es flexibel bleibt. Durch die Offenlegung von Schlüsselparametern ermöglicht der EA Händlern, verschiedene Ideen zu testen, die Logik an verschiedene Märkte anzupassen und ihren eigenen Vorteil zu erkunden, anstatt sich auf feste Annahmen zu verlassen.
Dieser Artikel scheint keine perfekte oder optimierte Strategie zu bieten. Stattdessen wird veranschaulicht, wie man eine diskretionäre Handelsidee in einen strukturierten, testbaren und wiederholbaren Algorithmus umwandelt. Die Leser, die mitgemacht haben, verfügen nun über einen funktionierenden Volatilitäts-Breakout Expert Advisor, ein tieferes Verständnis der Methodik von Larry Williams und ein solides Gerüst, auf dem sie bei zukünftigen Recherchen aufbauen können.
In der folgenden Tabelle sind alle Zusatzdateien zu diesem Artikel aufgeführt, zusammen mit einer kurzen Beschreibung des Zwecks jeder Datei. Diese Dateien sollen den Lesern helfen, die besprochenen Ergebnisse zu reproduzieren und die Umsetzung genau zu verfolgen.
| Dateiname | Beschreibung | |
|---|---|---|
| 1 | lwVolatilitySwingBreakoutExpert.mq5 | Der vollständige Quellcode des Expert Advisors, der in diesem Artikel entwickelt und erläutert wird. |
| 2 | configurations.ini | Die für den Backtest verwendete Konfiguration der Strategietester-Umgebung. |
| 3 | parameters.set | Die während des Backtests verwendeten Eingabeparameter sind im Artikel aufgeführt. |
Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/20862
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.
Einführung in MQL5 (Teil 33): Beherrschen der API- und WebRequest-Funktion in MQL5 (VII)
Statistische Arbitrage durch kointegrierte Aktien (Teil 9): Backtests, Portfolio-Gewichtungen, Updates
Python-MetaTrader 5 Strategietester (Teil 03): MetaTrader 5-ähnliche Handelsoperationen – Handhabung und Verwaltung
Datenbanken sind einfach (Teil 1): Ein leichtes ORM-Framework für MQL5 unter Verwendung von SQLite
- 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.