Wie man das Erkennen und Beheben von Fehlern in einem Expert Advisor Code einfacher macht

Roman Kramar | 8 Februar, 2016


Einführung

Die Entwicklung von Trading EAs in der MQL4 Sprache ist aus den Sichtweisen mehrerer Aspekte keine einfache Angelegenheit:

  • erstens, die Algorithmierung jedes mehr oder weniger schwierigen Handelssystems ist bereits ein Problem für sich selbst, weil es viele Einzelheiten berücksichtigen muss, von den Eigenheiten eines algorithmisierten EAs, bis zu der spezifischen MetaTrader 4 Umgebung,
  • zweitens, auch das Vorhandensein eines weiteren EA Algorithmus beseitigt nicht die Schwierigkeiten, die auftreten, bei der Übertragung des entwickelten Algorithmus in die Programmiersprache MQL4.

Wir sollten gerecht zu der Handelsplattform MetaTrader 4 sein - die Existenz der Programmiersprache zum Schreiben von Trading Expert Advisors, ist bereits ein großer Schritt vorwärts, verglichen mit früher verfügbaren Alternativen. Der Compiler ist ein große Hilfe beim Schreiben korrekter EAs. Sofort nach dem Klicken auf 'Compile' wird er über alle Syntaxfehler in Ihrem Code berichten. Wenn wir mit einer interpretierten Sprache gearbeitet haben, können solche Fehler nur während des EA-Betriebs entdeckt werden, und dies würde die Schwierigkeiten und die Dauer der Entwicklung erhöhen. Allerdings kann jeder EA, abgesehen von Syntaxfehlern, auch logische Fehler enthalten. Wir werden uns nun mit solchen Fehlern befassen.

Fehler in der Verwendung Eingebauter Funktionen

Da kein Trading EA ohne die Verwendung eingebauter Funktionen arbeiten kann, lassen Sie uns versuchen unser Leben bei der Fehleranalyse zu vereinfachen, zurückgegeben durch solche Funktionen. Betrachten wir zuerst die Betriebsergebnisse von Funktionen, direkt verbunden mit Handelsoperationen, weil das Ignorieren von Fehlern in solchen Funktionen zu kritischen Auswirkungen für einen EA führen kann. Allerdings beziehen sich weitere Ausführungen auch auf andere eingebaute Funktionen.

Leider kann man mit der Verwendung der MQL4 Optionen keine pauschalisierte Bibliothek zur Verarbeitung aller möglichen Fehlersituationen schreiben. In jedem einzelnen Fall müssen wir Fehler einzeln verarbeiten. Dennoch gibt es gute Nachrichten - wir müssen nicht alle Fehler verarbeiten, weil viele von ihnen bereits in der Phase der EA Entwicklung beseitigt werden. Aber um dies zu machen, sollten sie rechtzeitig erkannt werden. Betrachten wir als Beispiel zwei typische EA Fehler in MQL4:

1) Fehler 130 - ERR_INVALID_STOPS
2) Fehler 146 - ERR_TRADE_CONTEXT_BUSY

Einer der Fälle, wenn der erste Fehler auftritt, ist der Versuch des EA eine Pending Order zu nah am Markt zu platzieren. Seine Existenz kann die EA Eigenschaften in einigen Fällen ernsthaft zerstören. Zum Beispiel, angenommen der EA hat eine profitable Position eröffnet und nimmt alle 150 Punkte Teilgewinne mit. Wenn bei dem nächsten Versuch Fehler 130 auftritt und der Kurs zur vorherigen Stop-Ebene zurückkehrt, kann es Sie ihres ehrlichen Gewinns berauben. Trotz dieser Möglichkeit, kann dieser Fehler von Anfang an ausgeschlossen werden, durch das Einfügen einer Funktion in den EA, unter Berücksichtigung des minimal akzeptablen Abstands zwischen einem Kurs und Stops.

Der zweite Fehler, 'Trade Context is busy' kann nicht vollständig ausgeschlossen werden. Wenn mehrere EAs in einem Terminal arbeiten, können wird der Situation begegnen, wenn einer der Expert Advisors versuch eine Position zu öffnen, während der zweite dasselbe macht. folglich muss dieser Fehler immer verarbeitet werden.

Wir müssen also immer wissen, ob irgendeine der eingebauten Funktionen Fehler während des EA Betriebs zurückgibt. Dies kann erreicht werden mit deer Verwendung der folgenden einfachen zusätzlichen Funktion:

#include <stderror.mqh>
#include <stdlib.mqh>
 
void logError(string functionName, string msg, int errorCode = -1)
  {
    Print("ERROR: in " + functionName + "()");
    Print("ERROR: " + msg );
    
    int err = GetLastError();
    if(errorCode != -1) 
        err = errorCode;
        
    if(err != ERR_NO_ERROR) 
      {
        Print("ERROR: code=" + err + " - " + ErrorDescription( err ));
      }    
  }

Im einfachsten Fall sollte sie auf die folgende Art verwendet werden:


void openLongTrade()
  {
    int ticket = OrderSend(Symbol(), OP_BUY, 1.0, Ask + 5, 5, 0, 0);
    if(ticket == -1) 
        logError("openLongTrade", "could not open order");
  }

Der erste Parameter der Funktion logError() zeigt den Namen der Funktion, in der ein Fehler erkannt wurde. In unserem Fall ist dies die Funktion openLongTrade(). Wenn unser Expert Advisor die Funktion OrderSend() an mehreren Stellen aufruft, werden wir die Möglichkeit haben genau zu bestimmen, in welchem Fall der Fehler auftritt. Der Zweite Parameter liefert die Fehlerbeschreibung, die ermöglicht zu verstehen an welcher genauen Stelle der Funktion openLongTrade der Fehler erkannt wurde. Dies kann eine Kurzbeschreibung sein, oder eine detaillierte, einschlie0lich der Werte aller Parameter, die in die eingebaute Funktion übergeben werden.

Ich bevorzuge die letzte Variante, weil, Wenn ein Fehler auftritt, kann man sofort die gesamten Informationen erhalten, die für die Analyse notwendig sind. Angenommen, bevor OrderSend() aufgerufen wird hat sich der Kurs stark vom letzten bekannten Kurs bewegt. Als Ergebnis wird ein Fehler auftreten, und in der Protokolldatei des EA werden die folgenen Zeilen angezeigt:

 ERROR: in openLongTrade()
 ERROR: could not open order
 ERROR: code=138 - requote

Das heißt, es wird klar sein:

  1. in welcher Funktion der Fehler auftrat,
  2. worauf er sich bezieht (in unserem Fall auf den Versuch eine Position zu öffnen),
  3. welcher Fehler genau auftrat (Fehlercode und seine Beschreibung).

Betrachten wir nun den dritten, optionalen, Parameter der Funktion logError(). Er wird benötigt, wenn wir einen bestimmten Fehlertyp verarbeiten wollen, und andere Fehler werden in die Protokolldatei eingeschlossen, wie vorher:

void updateStopLoss(double newStopLoss)
  {
    bool modified = OrderModify(OrderTicket(), OrderOpenPrice(), 
                newStopLoss, OrderTakeProfit(), OrderExpiration());
    
    if(!modified)
      {
        int errorCode = GetLastError();
        if(errorCode != ERR_NO_RESULT ) 
            logError("updateStopLoss", "failed to modify order", errorCode);
      }
  }

Hier in der Funktion updateStopLoss() wird die eingebaute Funktion OrderModify() aufgerufen. Diese Funktion unterscheidet sich geringfügig von OrderSend() im Hinblick auf die Fehlerverarbeitung. Wenn sich kein Parameter einer geänderten Order von ihren aktuellen Parametern unterscheidet, wird die Funktion den Fehler ERR_NO_RESULT zurückgeben. wenn eine solche Situation in unserem EA akzeptabel ist, sollten wir diesen Fehler ignorieren. Zu diesem Zweck analysieren wir den von GetLastError() zurückgegebenen Wert. Wenn ein Fehler mit dem Code ERR_NO_RESULT auftritt, schreiben wir nichts in die Protokolldatei.

Tritt allerdings ein anderer Fehler auf, sollten wir darüber berichten, wie wir es vorher gemacht haben. Genau zu diesem Zweck speichern wir das Ergebnis der Funktion GetLastError() in eine Zwischenvariable und übergeben es mit dem dritten Parameter an die Funktion logError. Tatsächlich setzt die eingebaute Funktion den letzten Fehlercode automatisch auf Null, nachdem sie aufgerufen wurde. Wenn wir den Fehlercode nicht an logError() übergeben, würde die Protokolldatei des EAs einen Fehler mit dem Coe 0 und der Beschreibung "no error" enthalten.

Ähnliche Maßnahmen sollten bei der Verarbeitung anderer Fehler unternommen werden, zum Beispiel bei Requotes. Der Grundgedanke ist, nur Fehler zu verarbeiten, die verarbeitet werden müssen und andere an die Funktion logError() zu übergeben. In diesem Fall werden wir wissen, wenn ein unerwarteter Fehler während des EA-Betriebs aufgetreten ist. Nach der Analyse der Protokolldatei wissen wir, ob dieser Fehler einzeln verarbeitet werden muss, oder ob er beseitigt werden kann, um den EA Code zu verbessern. Solche Ansätze machen unser Leben leichter und verkürzen die Zeit der Fehlerbehebung.


Diagnose Logischer Fehler

Logische Fehler im Code eines Expert Advisor sind wahrscheinlich Ärger genug. Das Fehlen eine EA Stepping-Through Funktion macht die Bekämpfung solcher Fehler zu einer unangenehmen Aufgabe. Das wichtigste Instrument für die Diagnose solcher Fehler ist im Augenblick die Funktion Print(). Mit ihr können wir die aktuellen Werte wichtiger Variablen drucken und den EA Betriebsablauf aufzeichnen. Beim Debuggen eines EA während Tests mit Visualisierung, kann auch die eingebaute Funktion Comment() hilfreich sein. In der Regel, wenn die falsche Funktion eines EA bestätigt ist, müssen wir einen temporären Aufruf der Funktion Print() hinzufügen und den inneren Zustand des EA in den vermuteten Bereichen des Fehlerauftritts aufzeichnen.

Für die Erkennung schwieriger Fehlersituationen jedoch, müssen wir manchmal dutzende solcher Diagnose-Aufrufe der Funktion Print() hinzufügen. Nahc der Problemerkennung und Behebung, müssen die Funktionsaufrufe gelöscht oder auskommentiert werden, um die Protokolldatei des EA nicht zu überladen und die Tests langsamer zu machen. Die Situation ist noch schlimmer, wenn der Code des EA die Funktion Print() bereits zum Aufzeichnen verschiedener Zustände beinhaltet. Dann können die temporären Print() Funktionsaufrufe nicht mit einer einfachen Suche nach 'Print' im EA Code gelöscht werden. Man muss darüber nachdenken, ob man eine Funktion löscht, oder nicht.

Zum Beispiel, wenn Fehler der Funktionen OrderSend(), OrderModify() und OrderClose() aufgezeichnet werden, ist es hilfreich der aktuellen Wert der Variablen Bid und Ask in die Protokolldatei zu drucken. Dies macht die Suche nach Gründen für Fehler wie ERR_INVALID_STOPS und ERR_OFF_QUOTES einfacher.

Um diese Diagnose-Informationen in eine Protokolldatei zu schreiben, empfehle ich die Verwendung der folgenden zusätzlichen Funktion:

void logInfo(string msg)
  {
    Print("INFO: " + msg);
  }

weil:

  • erstens, nun eine solche Funktion nicht mit 'Print' während der Suche verwirrt wird,
  • zweitens, diese Funktion hat eine weitere nützliche Besonderheit, die später besprochen wird.

Es braucht viel Zeit, um temporäre Diagnose-Aufrufe der Funktion Print () hinzuzufügen und zu löschen. Deshalb schlage ich noch einen weiteren Ansatz vor, der effektiv bei der Erkennung logischer Fehler in einem Code ist und hilft Zeit zu sparen. Analysieren wir die folgende Funktion:

void openLongTrade(double stopLoss)
  {
    int ticket = OrderSend(Symbol(), OP_BUY, 1.0, Ask, 5, stopLoss, 0);
    if(ticket == -1) 
        logError("openLongTrade", "could not open order");
  }

In diesem Fall, während wir eine Long-Position öffnen, ist es klar, dass in einem korrekten EA Betrieb der Wert des Parameters StopLoss nicht größer oder gleich zu dem aktuellen Bid-Kurs sein kann, Das heißt, richtig ist die Aussage, dass wenn die Funktion openLongTrade() aufgerufen wird &li; immer erfüllt ist. Da wir es bereits in der Phase des Schreibens der analysierten Funktion wissen, können wir es in der folgenden Art verwenden:


void openLongTrade( double stopLoss )
  {
    assert("openLongTrade", stopLoss < Bid, "stopLoss < Bid");
    
    int ticket = OrderSend(Symbol(), OP_BUY, 1.0, Ask, 5, stopLoss, 0);
    if(ticket == -1) 
        logError("openLongTrade", "could not open order");
  }

D.h. wir fügen unsere Aussage in den Code ein unter Verwendung der zusätzlichen Funktion assert(). Die Funktion selbst ist ziemlich einfach:


void assert(string functionName, bool assertion, string description = "")
  {
    if(!assertion) 
        Print("ASSERT: in " + functionName + "() - " + description);
  }

Der erste Parameter der Funktion ist der Name der Funktion, in dem unsere Bedingung geprüft wird (in Analogie zu der Funktion logError()). Der zweite Parameter zeigt die Ergebnisse dieser Überprüfung der Bedingung. Und der dritte Parameter bezeichnet seine Beschreibung. Als Ergebnis, wenn eine erwartete Bedingung nicht erfüllt ist, wird die EA Protokolldatei die folgende Information enthalten:

  1. der Name der Funktion, in der die Bedingung nicht erfüllt wurde,
  2. Beschreibung dieser Bedingung.

Als Beschreibung können wir zum Beispiel anzeigen, die Bedingung selbst, oder eine detailliertere Beschreibung mit den Werten der kontrollierten Variablen in dem Augenblick der Überprüfung der Bedingung, wenn dies helfen kann den Grund für den Fehler zu finden.

Natürlich ist das gegebene Beispiel maximal vereinfacht. Aber ich hoffe es spiegelt die Idee sehr deutlich. Während die EA Funktionalität wächst, sehen wir deutlich wie er funktionieren sollte und welche Bedingungen und Eingabeparameter akzeptabel sind, und welche nicht. Beim Einfügen in einen EA Code mit Verwendung der Funktion assert(), erhalten wir eine nützliche Information über den Ort, an dem die EA Betriebslogik defekt ist. Darüber hinaus entfernen wir teilweise die Notwendigkeit temporäre Aufrufe der Funktion Print() hinzuzufügen und zu löschen, weil die Funktion assert()Diagnosemeldungen in der EA Protokolldatei nur in dem Moment der Erfassung von Diskrepanzen in erwarteten Bedingungen erzeugt.

Eine weitere nützliche Methode ist die Verwendung der Funktion vor jedem Divisionsvorgang. Tatsächlich kann der eine oder andere Logikfehler zu einer Division durch Null führen. In solchen Fällen stellt der Expert Advisor den Betrieb ein und eine einzelne Zeile erscheint in der Protokolldatei: 'zero divide'. Und wenn der Divisionsvorgang häufig im Code verwendet wird, kann es ziemlich schwierig sein die Stelle zu erkennen, an der der Fehler auftritt. Hier kann die Funktion assert() sehr hilfreich sein. Wir müssen einfach die entsprechende Prüfung vor jedem Divisionsvorgang einfügen::

assert("buildChannel", distance > 0, "distance > 0");
double slope = delta / distance;

Und nun, im Falle einer Division durch Null, scheuen wir nur in die Protokolldatei, um den genauen Ort des Fehlers zu finden, an dem der Fehler auftrat.



Analyse der EA Protokolldatei zur Erfassung von Fehlern

Die vorgeschlagenen Funktionen zur Fehleraufzeichnung helfen diese einfach in der Protokolldatei zu finden. Wir müssen nur die Protokolldatei im Textmodus öffnen und nach den Wörtern "ERROR" und "ASSERT" suchen. Manchmal begegnen wir allerdings Situationen, in denen wir während der Entwicklung die Aufruf-Ergebnisse dieser oder jener eingebauten Funktion weglassen. Manchmal wird Division durch Null weggelassen. Wie können wir Meldungen über solche Fehler unter tausenden von Zeilen, die Informationen über geöffnete geschlossene und geänderte Positionen enthalten, erkennen? Wenn Sie einem solchen Problem begegnen, empfehle ich den folgenden Ausweg.

Öffnen Sie Microsoft Excel und laden Sie die EA-Betrieb Protokolldatei als CSV-Datei herunter, und geben Sie an, dass als Trennzeichen ein oder mehrere Leerzeichen verwendet werden sollen. Nun aktivieren Sie "Autofilter". Dies gibt Ihnen die komfortable Möglichkeit die Filteroptionen in zwei benachbarten Spalten durchzusehen (Sie werden leicht verstehen, welche Spalten), um zu verstehen, wenn die Protokolldatei Fehler enthält, die durch ein Terminal in sie geschrieben wurden, oder nicht. Alle Einträge, erstellt durch die Funktionen logInfo(), logError() und assert(), beginnen mit dem Präfix ("INFO:", "ERROR:" und "ASSERT:"). Die Einträge des Terminal über Fehler können auch einfach erkannt werden, unter ein paar Varianten typischer Einträge über die Arbeit mit Ordern.

Diese Aufgabe kann auf eine elegantere Art gelöst werden. Wenn Sie sich zum Beispiel gut mit Tools zur Verarbeitung von Textdateien auskennen, können Sie ein kleines Skript schreiben, das nur diese Zeilen der EA Protokolldatei anzeigt, die sich auf EA Betriebsfehler beziehen. Ich empfehle jeden Expert Advisor über eine lange Periode durch den Tester laufen zu lassen, und dann die Protokolldatei seines Betriebsablaufs mit den beschriebenen Methoden zu untersuchen. Dies ermöglicht das Erkennen der Mehrheit möglicher Fehler, bevor Sie den EA auf einem Demokonto laufen lassen. Danach hilft Ihnen das gleiche analysieren von-Zeit-zu-Zeit, Fehler rechtzeitig zu erkennen, die für den EA Betrieb auf Konten in Echtzeit eigentümlich sind.


Fazit

Die beschriebenen zusätzlichen Funktionen und einfachen Methoden, ermöglichen die Vereinfachung des Vorgangs der Fehlererkennung und Behebung im EA Code, geschrieben in der Programmiersprache MQL4. Für Ihren Komfort sind die oben aufgeführten Funktionen in die angehangenen Dateien eingefügt. Fügen Sie die Datei einfach mit dem Verzeichnis #include in Ihren EA ein. Ich hoffe, die beschriebenen Methoden werden erfolgreich von Tradern eingesetzt, die sich um die Robustheit und Richtigkeit der Expert Advisors sorgen.