Beliebte Beiträge

Tipp Der Redaktion - 2019

MQL4: Fehler und Warnungen beim Kompilieren in MetaEditor korrigieren

Die Entwicklung von MQL4-Handelsexperten ist keine leichte Aufgabe. Erstens ist die Algorithmusisierung eines komplexen Handelssystems bereits ein Problem, da Sie viele Details berücksichtigen müssen, angefangen bei den Merkmalen des TS bis hin zu den Besonderheiten der MetaTrader 4-Umgebung. Zweitens beseitigt das Vorhandensein eines detaillierten Algorithmus nicht die Schwierigkeiten, die beim Übertragen des entwickelten Systems auftreten Algorithmus auf die Programmiersprache MQL4.

Der Compiler bietet Unterstützung beim Schreiben der richtigen Experten. Nach dem Start der Kompilierung meldet MetaEditor alle Syntaxfehler in Ihrem Code. Leider kann Ihr Advisor neben Syntaxfehlern auch logische Fehler enthalten, die der Compiler nicht abfangen kann. Deshalb müssen wir dies selbst tun. Wie das geht, lesen Sie heute in unserem Material.

Die häufigsten Übersetzungsfehler

Wenn der Code fehlerhaft ist, kann das Programm nicht kompiliert werden. Für die vollständige Kontrolle aller Fehler wird empfohlen, den strengen Kompilierungsmodus zu verwenden, der in der Direktive festgelegt ist:

#eigenschaft streng

Dieser Modus vereinfacht die Suche nach Fehlern erheblich. Kommen wir nun zu den häufigsten Kompilierungsfehlern.

Der Bezeichner stimmt mit dem reservierten Wort überein

Wenn der Name der Variablen oder Funktion mit einem der reservierten Wörter übereinstimmt:

int char; // falsch int char1; // richtig int char () // falsch {return (0); }

dann zeigt der Compiler Fehlermeldungen an:

Um diesen Fehler zu beheben, müssen Sie den Namen der Variablen oder Funktion korrigieren. Ich empfehle, an folgendem Benennungssystem festzuhalten:

Alle Funktionen müssen eine Aktion anzeigen. Das heißt, es muss ein Verb sein. Zum Beispiel OpenLongPosition () oder ModifyStopLoss (). Funktionen machen doch immer was, oder?

Außerdem ist es ratsam, Funktionen im sogenannten CamelCase-Stil aufzurufen. Und die Variablen haben den Stil cebab_case. Dies ist eine gängige Praxis.

Apropos Variablennamen. Variablen sind Substantive. Zum Beispiel my_stop_loss, day_of_week, current_month. Es ist nicht so beängstigend, eine Variable mit einem langen Namen zu benennen, es ist viel schlimmer, sie unverständlich zu benennen. Was ist ein Dow, Dow Jones Index? Nein, es wird der Wochentag. Natürlich verstehen Sie heute schon, was diese Variable ist. Wenn Sie den Advisor-Code jedoch einen Monat später öffnen, ist nicht alles so offensichtlich. Und diese Zeit geht verloren, wenn Sie Nachrichten aus der Vergangenheit entschlüsseln - brauchen Sie sie?

Sonderzeichen in Variablen- und Funktionsnamen

Mach weiter. Wenn die Namen von Variablen oder Funktionen Sonderzeichen enthalten ($, @, Punkt):

int $ var1; // falsch int @ var2; // falsch int var.3; // falsch void f @ () // falsch {return; }

dann zeigt der Compiler Fehlermeldungen an:

Um diesen Fehler zu beheben, müssen Sie die Namen von Variablen oder Funktionen erneut anpassen oder sie sofort menschlich aufrufen. Im Idealfall sollte der Code so geschrieben sein, dass selbst eine Person, die keine Programmierkenntnisse hat, ihn einfach liest und versteht, was dort vor sich geht.

Fehler bei der switch-Anweisung

Die alte Version des Compilers erlaubte die Verwendung beliebiger Werte in den Ausdrücken und Konstanten der switch-Anweisung:

void start () {double n = 3.14; Schalter (n) {Fall 3.14: Print ("Pi"); brechen; Fall 2.7: Drucken ("E"); brechen; }}

Im neuen Compiler müssen die Ausdrücke und Konstanten der switch-Anweisung Ganzzahlen sein. Bei der Verwendung solcher Konstrukte treten also Fehler auf:

Wenn Sie also klassischen Code wie WallStreet, Ilan und andere unbestechliche Elemente analysieren (was für die Selbstentwicklung sehr nützlich ist), können Sie auf diesen Fehler stoßen. Es wird zum Beispiel sehr einfach behandelt, wenn hier eine solche Zeile verwendet wird:

switch (MathMod (Tag_48, 10))

So können Sie das Problem leicht lösen:

switch ((int) MathMod (day_48, 10))

Funktionsrückgabewerte

Alle Funktionen außer void müssen einen Wert des deklarierten Typs zurückgeben. Zum Beispiel:

int function () {}

Im strengen Kompilierungsmodus (strict) tritt ein Fehler auf:

Im Kompilierungsmodus zeigt der Compiler standardmäßig eine Warnung an:

Wenn der Rückgabewert der Funktion nicht mit der Deklaration übereinstimmt:

int init () {return; }

Im strengen Kompilierungsmodus tritt dann ein Fehler auf:

Im Kompilierungsmodus zeigt der Compiler standardmäßig eine Warnung an:

Um solche Fehler im Funktionscode zu beheben, müssen Sie lediglich eine return-Anweisung return mit einem Rückgabewert des entsprechenden Typs hinzufügen.

Arrays in Funktionsargumenten

Arrays in Funktionsargumenten werden nur als Referenz übergeben. Bisher war dies nicht der Fall, so dass Sie in alten Beratern diesen Fehler finden können. Hier ist ein Beispiel:

double ArrayAverage (double a) {return (0); }

Gegebener Code im strengen Kompilierungsmodus (strict) führt zu einem Fehler:

Im Kompilierungsmodus zeigt der Compiler standardmäßig eine Warnung an:

Um solche Fehler zu beheben, müssen Sie die Übertragung des Arrays explizit als Referenz angeben und dem Arraynamen das Präfix & hinzufügen:

double ArrayAverage (double & a) {return (0); }

Konstante Arrays (Time, Open, High, Low, Close, Volume) können übrigens nicht als Referenz übergeben werden. Zum Beispiel ein Anruf:

ArrayAverage (Open);

unabhängig vom Kompilierungsmodus führt zu einem Fehler:

Um solche Fehler zu beseitigen, müssen Sie die erforderlichen Daten aus dem konstanten Array kopieren:

// --- Array zum Speichern offener Preiswerte double OpenPrices; // --- kopiere die Eröffnungspreise in das OpenPrices ArrayCopy Array (OpenPrices, Open, 0,0, WHOLE_ARRAY); // --- rufe die Funktion ArrayAverage (OpenPrices) auf;

Einer der häufigsten Fehler ist der Verlust eines Indikators durch einen Berater. In solchen Fällen schreiben in der Regel die Fachanwender in den Foren wütend: "Der Berater arbeitet nicht!" oder "Ich habe den Berater auf die Karte gesetzt und nichts passiert!". Die Lösung für dieses Problem ist eigentlich sehr einfach. Schauen Sie wie immer einfach auf den "Log" -Reiter des Terminals und finden Sie dort einen Eintrag wie:

2018.07.08 09: 15: 44.957 2016.01.04 00:51 Datei 'C: Users1AppDataRoamingMetaQuotesTerminal MQL4indicatorsKELTNER_F12.ex4' kann nicht geöffnet werden 2

Dies sagt uns, dass sie vergessen haben, den Indikator in den Ordner zu stellen, oder dass er einen anderen Namen hat. Wenn der Indikator fehlt, müssen Sie ihn dem Ordner mit den Indikatoren hinzufügen. Ist dies der Fall, lohnt es sich, den Namen im Code des Beraters zu überprüfen - höchstwahrscheinlich wird er dort anders genannt.

Compiler-Warnungen

Compiler-Warnungen dienen zu Informationszwecken und sind keine Fehlermeldungen. Sie weisen jedoch auf mögliche Fehlerquellen hin und es ist besser, sie zu korrigieren. Sauberer Code sollte keine Warnungen enthalten.

Überschneidungen von globalen und lokalen Variablennamen

Wenn es auf globaler und lokaler Ebene Variablen mit demselben Namen gibt:

int i; // globale Variable void OnStart () {int i = 0, j = 0; // lokale Variablen für (i = 0; i <5; i ++) {j + = i; } PrintFormat ("i =% d, j =% d", i, j); }

Dann zeigt der Compiler eine Warnung an und gibt die Zeilennummer an, in der die globale Variable deklariert ist:

Um solche Warnungen zu korrigieren, müssen Sie die Namen der globalen Variablen anpassen.

Typenkonflikt

Im folgenden Beispiel:

#property strict void OnStart () {double a = 7; float b = a; int c = b; string str = c; Drucken (c); }

Im strengen Kompilierungsmodus mit Typenkonflikt zeigt der Compiler Warnungen an:

In diesem Beispiel warnt der Compiler vor einem möglichen Genauigkeitsverlust, wenn verschiedene Datentypen zugewiesen werden und der Typ int implizit in einen String konvertiert wird.

Um dies zu beheben, müssen Sie die explizite Typkonvertierung verwenden:

#property strict void OnStart () {double a = 7; float b = (float) a; int c = (int) b; string str = (string) c; Drucken (c); }

Nicht verwendete Variablen

Das Vorhandensein von Variablen, die nicht im Programmcode verwendet werden (zusätzliche Entitäten), ist keine gute Form.

void OnStart () {int i, j = 10, k, l, m, n2 = 1; für (i = 0; i <5; i ++) {j + = i;}}

Meldungen zu solchen Variablen werden unabhängig vom Kompilierungsmodus angezeigt:

Um dies zu beheben, müssen Sie nur nicht verwendete Variablen aus dem Programmcode entfernen.

Diagnose von Übersetzungsfehlern

Nach dem Schreiben eines Programms treten häufig Kompilierungsprobleme auf, die auf Fehler im Code zurückzuführen sind. Dies können die unterschiedlichsten Fehler sein, aber in jedem Fall muss ein Codeabschnitt, in dem ein Fehler gemacht wird, schnell erkannt werden.

Oft nehmen sich die Leute viel Zeit und Nerven, um nach einer zusätzlichen Klammer zu suchen. Es gibt jedoch eine Möglichkeit, Fehler schnell zu erkennen, die auf der Verwendung von Kommentaren basiert.

Es ist sehr schön, einen Code zu schreiben, der groß genug ist, ohne einen einzigen Fehler zu machen. Dies kommt aber leider nicht oft vor. Ich betrachte hier keine Fehler, die zu einer fehlerhaften Codeausführung führen. Hier geht es um Fehler, die eine Kompilierung unmöglich machen.

Häufige Fehler sind das Einfügen einer zusätzlichen Klammer in einem schwierigen Zustand, das Fehlen einer Klammer, das Setzen eines Doppelpunkts, ein Komma beim Deklarieren von Variablen, ein Tippfehler im Variablennamen usw. Oft sieht man beim Übersetzen sofort, in welcher Zeile ein ähnlicher Fehler gemacht wurde. Manchmal ist es jedoch nicht so einfach, einen solchen Fehler zu finden. Weder der Compiler noch das scharfe Auge können uns helfen, den Fehler sofort zu finden. In solchen Fällen "gehen" Programmieranfänger in der Regel den gesamten Code durch und versuchen, den Fehler visuell zu identifizieren. Und immer wieder, solange die Nerven aushalten.

MQL bietet jedoch, wie andere Programmiersprachen auch, ein hervorragendes Werkzeug - das Kommentieren. Damit können Sie einige Teile des Codes deaktivieren. Normalerweise werden Kommentare verwendet, um Kommentare einzufügen oder nicht verwendete Codeabschnitte zu deaktivieren. Das Kommentieren kann auch erfolgreich bei der Suche nach Fehlern angewendet werden.

Bei der Suche nach Fehlern wird in der Regel der Codeabschnitt ermittelt, in dem der Fehler auftritt. In diesem Abschnitt wird der Fehler dann visuell lokalisiert. Ich halte es für unwahrscheinlich, dass irgendjemand Zweifel daran hat, dass es einfacher und schneller ist, 5-10 Codezeilen schneller als 100-500 oder sogar mehrere Tausend zu untersuchen.

Bei der Verwendung von Kommentaren ist die Aufgabe äußerst einfach. Zuerst müssen Sie die verschiedenen Abschnitte des Codes (manchmal fast den gesamten Code) auskommentieren und ihn dadurch "deaktivieren". Anschließend wird der Kommentar aus diesen Codeabschnitten entfernt. Nach dem nächsten Rückzug der Kommentierung wird versucht zu kompilieren. Wenn die Kompilierung erfolgreich ist, befindet sich der Fehler nicht in diesem Codeabschnitt. Dann öffnet sich der nächste Codeabschnitt und so weiter. Wenn es einen problematischen Teil des Codes gibt, wird ein Fehler visuell gesucht und dann behoben. Es wird erneut versucht zu kompilieren. Wenn alles gut gelaufen ist, ist der Fehler behoben.

Es ist wichtig, die Codeabschnitte, die Sie kommentieren müssen, korrekt zu identifizieren. Wenn diese Bedingung (oder eine andere logische Konstruktion), sollte sie vollständig kommentiert werden. Wenn Sie den Codeabschnitt kommentieren, in dem die Variablen deklariert sind, ist es wichtig, dass der Abschnitt, in dem auf diese Variablen zugegriffen wird, nicht geöffnet wird. Mit anderen Worten, das Kommentieren sollte gemäß der Programmierlogik erfolgen. Die Nichteinhaltung dieses Ansatzes führt zu neuen, irreführenden Kompilierungsfehlern.

Hier ist ein großartiges Beispiel für einen Fehler, bei dem nicht klar ist, wo er zu suchen ist, und das Kommentieren des Codes kann uns helfen.

Laufzeitfehler

Fehler, die während der Ausführung von Programmcode auftreten, werden häufig als Laufzeitfehler bezeichnet. Solche Fehler hängen normalerweise vom Programmstatus ab und sind mit falschen Variablenwerten verbunden.

Wenn beispielsweise eine Variable als Index für Array-Elemente verwendet wird, führen ihre negativen Werte zwangsläufig zu einem Abfluss des Arrays.

Array außerhalb des zulässigen Bereichs

Dieser Fehler tritt häufig bei Indikatoren auf, wenn auf Indikatorpuffer zugegriffen wird. Die IndicatorCounted () -Funktion gibt die Anzahl der Balken zurück, die sich seit dem letzten Indikatoraufruf nicht geändert haben. Die Werte der Indikatoren für zuvor berechnete Balken müssen nicht neu berechnet werden. Um die Berechnungen zu beschleunigen, ist es daher ausreichend, nur die letzten Balken zu verarbeiten.

Die meisten Indikatoren, die diese Methode zur Optimierung von Berechnungen verwenden, haben die folgende Form:

// + ----------------------------------------------- ------------------- + // | Benutzerdefinierte Indikator-Iterationsfunktion | // + ----------------------------------------------- ------------------- + int start () {// --- manchmal sind mindestens N Balken für die Berechnung erforderlich (z. B. 100) // wenn das Diagramm keine solchen enthält die Anzahl der Balken (zum Beispiel im MN-Zeitrahmen), wenn (Balken <100) {return (-1); // Berechnung stoppen und vorzeitig beenden} // --- Anzahl der Balken, die sich seit dem letzten Aufruf des Indikators nicht geändert haben int counted_bars = IndicatorCounted (); // --- Beenden Sie im Fehlerfall if (counted_bars0 // --- Erhöhen Sie bei wiederholten Aufrufen das Limit um 1 auf // ---, um die Anzeigewerte für das letzte Balkenlimit ++ zu aktualisieren;} // --- den Hauptberechnungszyklus für (int i = limit; i> 0; i--) {// Verwenden der Werte von Balken, die um 5 und 10 tiefer in den Verlauf eingehen Buff1i = 0,5 * (Openi + 5 + Closei + 10)}}

Oft gibt es eine falsche Fallbehandlung counted_bars == 0 (die Anfangsposition von limit muss um einen Wert reduziert werden, der gleich 1 + dem maximalen Index relativ zur Schleifenvariablen ist).

Es sollte auch beachtet werden, dass wir zum Zeitpunkt der Ausführung der Funktion start () auf die Elemente der Indikatorpuffer-Arrays von 0 bis Bars () - 1 zugreifen können. Wenn Sie mit Arrays arbeiten müssen, die keine Indikatorpuffer sind, sollten Sie deren Größe mit der Funktion ArrayResize () entsprechend der aktuellen Größe der Indikatorpuffer erhöhen. Der maximale Index eines Elements zur Adressierung kann auch durch Aufrufen von ArraySize () mit einem der Indikatorpuffer als Argument ermittelt werden.

Division durch Null (Nulldivision)

Der Fehler "Nulldivision" tritt auf, wenn der Teiler während der Divisionsoperation gleich Null ist:

void OnStart () {int a = 0, b = 0, c; c = a / b; Drucken ("c =", c); }

Wenn dieses Skript ausgeführt wird, wird auf der Registerkarte "Experten" eine Fehlermeldung angezeigt und das Programm wird beendet:

Typischerweise tritt ein solcher Fehler in Fällen auf, in denen der Wert des Teilers durch die Werte einiger externer Daten bestimmt wird. Wenn beispielsweise Handelsparameter analysiert werden, ist der Wert der betroffenen Margin 0, wenn keine offenen Orders vorhanden sind. Ein weiteres Beispiel: Wenn die analysierten Daten aus einer Datei gelesen werden und nicht vorhanden sind, kann der ordnungsgemäße Betrieb nicht garantiert werden. Aus diesem Grund ist es ratsam, solche Fälle zu berücksichtigen und korrekt zu behandeln.

Am einfachsten ist es, den Divisor vor dem Divisionsvorgang zu überprüfen und eine Meldung über den falschen Parameterwert anzuzeigen:

void OnStart () {int a = 0, b = 0, c; wenn (b! = 0) {c = a / b; Drucken (c); } else {Print ("Fehler: b = 0"); zurückkehren }}

Infolgedessen tritt kein kritischer Fehler auf, es wird jedoch eine Meldung über den falschen Wert des Parameters angezeigt, und die Operation wird beendet:

Verwenden Sie für das aktuelle Zeichen 0 anstelle von NULL

In der alten Version des Compilers war es zulässig, 0 (Null) als Argument in Funktionen zu verwenden, die ein Finanzinstrument erfordern.

Beispielsweise könnte der Wert des technischen Indikators für den gleitenden Durchschnitt für das aktuelle Symbol wie folgt angefordert werden:

AlligatorJawsBufferi = iMA (0,0,13,8, MODE_SMMA, PRICE_MEDIAN, i); // falsch

Um das aktuelle Zeichen im neuen Compiler anzugeben, müssen Sie explizit NULL angeben:

AlligatorJawsBufferi = iMA (NULL, 0.13.8, MODE_SMMA, PRICE_MEDIAN, i); // richtig

Darüber hinaus können das aktuelle Symbol und die Diagrammperiode mit den Funktionen Symbol () und Periode () angegeben werden.

AlligatorJawsBufferi = iMA (Symbol (), Punkt (), 13.8, MODE_SMMA, PRICE_MEDIAN, i); // richtig

Noch besser, wenn Sie die vordefinierten Variablen _Symbol und _Period verwenden - sie werden schneller verarbeitet:

AlligatorJawsBufferi = iMA (_Symbol, _Period, 13.8, MODE_SMMA, PRICE_MEDIAN, i); // richtig

Unicode-Zeichenfolgen und ihre Verwendung in DLL

Zeichenfolgen sind eine Folge von Unicode-Zeichen. Beachten Sie diese Tatsache und verwenden Sie die entsprechenden Windows-Funktionen. Wenn Sie beispielsweise die Bibliotheksfunktionen wininet.dll anstelle von InternetOpenA () und InternetOpenUrlA () verwenden, sollten Sie InternetOpenW () und InternetOpenUrlW () aufrufen. Verwenden Sie beim Übergeben von Zeichenfolgen an eine DLL die MqlString-Struktur:

#pragma pack (push, 1) struct MqlString {int size; // 32-Bit-Ganzzahl, enthält die Größe des Puffers, der für den String LPWSTR-Puffer zugewiesen wurde. // 32-Bit-Adresse des Puffers mit der Zeichenfolge int reserved; // 32-Bit-Ganzzahl, reserviert, nicht verwenden}; #pragma pack (pop, 1)

Dateifreigabe

Beim Öffnen von Dateien müssen Sie die FILE_SHARE_WRITE- und FILE_SHARE_READ-Flags für die Freigabe explizit angeben.

Wenn sie nicht anwesend sind, wird die Datei im exklusiven Modus geöffnet, sodass andere Personen sie erst öffnen können, nachdem sie vom Monopolisten geschlossen wurden.

Wenn Sie beispielsweise mit Offlinediagrammen arbeiten, müssen Sie die freigegebenen Flags explizit angeben:

// Erste Änderung - Freigabe-Flags hinzufügen ExtHandle = FileOpenHistory (c_symbol + i_period + ". Hst", FILE_BIN | FILE_WRITE | FILE_SHARE_WRITE | FILE_SHARE_READ);

Datetime-Konvertierungsfunktion

Beachten Sie, dass die Konvertierung eines Datetime-Typs in einen String vom Kompilierungsmodus abhängt:

datetime date = D'2014.03.05 15:46:58 '; string str = "mydate =" + date; // --- str = "mydate = 1394034418" - ohne die Anweisung #property strict // --- str = "mydate = 2014.03.05 15:46:58" - mit der Anweisung #property strict

Beispielsweise führt der Versuch, mit Dateien zu arbeiten, deren Name einen Doppelpunkt enthält, zu einem Fehler.

Laufzeitfehlerbehandlung

Da kein Handelsexperte auf die eingebauten benutzerdefinierten Funktionen verzichten kann, werden wir zunächst versuchen, unser Leben zu vereinfachen, wenn wir die von diesen Funktionen zurückgegebenen Fehler analysieren.

Einige Bibliotheken sind im Set "out of the box" verfügbar, um das Schreiben von Beratern zu vereinfachen, einschließlich solcher für die Arbeit mit Fehlern. Sie werden im Ordner MQL4 / Include gespeichert:

Wir werden zwei Bibliotheken brauchen:

  • stderror.mqh - enthält Konstanten für die Nummer jedes Fehlers;
  • stdlib.mqh - enthält mehrere Hilfsfunktionen, einschließlich der Funktion, die Fehlerbeschreibung als Zeichenfolge zurückzugeben:
string ErrorDescription (int error_code)

Deshalb werden wir diese beiden Bibliotheken mit unserem Projekt verbinden:

#include #include 

Die Fehlerbeschreibungen selbst befinden sich in der Datei MQL4 / Library / stdlib.mql4 und sind in englischer Sprache. Wenn Sie gegen Fremdsprachen sind, können Sie die Beschreibungen daher immer in Ihrer Muttersprache schreiben.

Eine weitere integrierte Funktion, die wir benötigen, ist GetLastError (). Sie gibt die Fehlercodes in Form einer Ganzzahl (int) zurück, die wir dann verarbeiten. Fehlercodes und ihre Beschreibungen in russischer Sprache finden Sie im Handbuch zu mql4 von MetaQuotes. Von dort aus können Sie Informationen zur Übersetzung der Datei stdlib.mql4 ins Russische verwenden.

Nachdem wir die erforderlichen Bibliotheken verbunden haben, werden wir die Ergebnisse der Funktionen betrachten, die in direktem Zusammenhang mit Handelsoperationen stehen, da das Ignorieren von Fehlfunktionen in diesen Funktionen zu kritischen Konsequenzen für den Bot führen kann.

Leider können Sie mit MQL4 keine verallgemeinerte Bibliothek schreiben, um alle möglichen Fehlersituationen zu behandeln. In jedem Fall müssen Sie die Fehler separat behandeln. Aber nicht alles ist so schlimm - viele Fehler müssen nicht bearbeitet werden, es reicht aus, sie in der Entwicklungs- und Testphase eines Experten zu beseitigen, obwohl Sie dafür rechtzeitig über deren Vorhandensein Bescheid wissen müssen.

Betrachten Sie beispielsweise zwei für MQL4-Experten typische Fehler:

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

Einer der Fälle, in denen der erste Fehler auftritt, ist der Versuch des Experten, eine ausstehende Bestellung zu nahe am Markt zu platzieren. Seine Anwesenheit kann in einigen Fällen die Leistung von Experten ernsthaft beeinträchtigen. Nehmen wir zum Beispiel an, ein Experte, der eine profitable Position eröffnet hat, macht alle 150 Punkte Gewinn. Wenn der nächste derartige Versuch einen Fehler von 130 verursacht und der Preis unwiederbringlich zum vorherigen Stop-Level zurückkehrt, kann der Experte Ihnen einen legitimen Gewinn vorenthalten. Trotz der Möglichkeit solcher Konsequenzen kann dieser Fehler grundlegend behoben werden, indem der Expertencode so finalisiert wird, dass der minimal zulässige Abstand zwischen dem Preis und den Anschlägen berücksichtigt wird.

Der zweite Fehler, der mit der Geschäftigkeit des Handelskontexts des Terminals zusammenhängt, kann nicht vollständig beseitigt werden. Wenn mehrere Experten im selben Terminal arbeiten, ist es immer möglich, dass einer der Experten versucht, eine Position zu eröffnen, während der andere dies noch tut. Daher sollte ein solcher Fehler immer behandelt werden.

Wir sollten daher immer wissen, ob eine der verwendeten integrierten Funktionen einen Fehler zurückgibt, während der EA arbeitet. Dies kann mit der folgenden einfachen Hilfsfunktion erreicht werden:

void logError (Zeichenfolge functionName, Zeichenfolge msg, int errorCode = -1) {Print ("ERROR: in" + functionName + "()"); Drucken ("ERROR:" + msg); int err = GetLastError (); if (errorCode! = -1) {err = errorCode; } if (err! = ERR_NO_ERROR) {Print ("ERROR: code =" + err + "-" + ErrorDescription (err)); }}

Wir werden es wie folgt verwenden:

void openLongTrade () {int ticket = OrderSend (Symbol (), OP_BUY, 1.0, Ask + 5, 5, 0, 0); if (ticket == -1) {logError ("openLongTrade", "Bestellung konnte nicht geöffnet werden"); }}

Dies ist natürlich ein vereinfachtes Beispiel. In dieser Lektion erfahren Sie, wie Sie kompetentere Funktionen zum Öffnen, Schließen und Ändern von Aufträgen erstellen.

Der erste Parameter der Funktion logError () ist der Name der Funktion, in der der Fehler festgestellt wurde, in unserem Beispiel in der Funktion openLongTrade (). Wenn unser Experte die Funktion OrderSend () an mehreren Stellen aufruft, können wir so genau bestimmen, an welcher Stelle der Fehler aufgetreten ist. Der zweite Parameter übergibt die Beschreibung des Fehlers, damit Sie genau verstehen können, wo der Fehler in der Funktion openLongTrade () festgestellt wurde. Dies kann entweder eine kurze Beschreibung des Fehlers oder eine detailliertere Beschreibung der Werte aller an die integrierte Funktion übergebenen Parameter sein.

Ich bevorzuge die letztere Option, da Sie im Fehlerfall sofort alle für die Analyse erforderlichen Informationen erhalten. Angenommen, vor dem Aufruf von OrderSend () musste der aktuelle Preis erheblich vom letzten bekannten Preis abweichen. Infolgedessen tritt während der Ausführung dieses Beispiels ein Fehler auf, und die folgenden Zeilen werden im Expertenprotokoll angezeigt:

FEHLER: in openLongTrade () FEHLER: Bestellung konnte nicht geöffnet werden FEHLER: Code = 138 - erforderlich

Das heißt, Sie werden sofort sehen:

  • In welcher Funktion ist der Fehler aufgetreten?
  • worauf es sich bezieht (in diesem Fall ein Versuch, eine Position zu eröffnen);
  • welcher Fehler aufgetreten ist (Fehlercode und Beschreibung).

Betrachten Sie nun den dritten optionalen Parameter für die Funktion logError (). Dies ist in den Fällen erforderlich, in denen wir einen bestimmten Fehlertyp verarbeiten möchten, und wir werden über den Rest im Protokoll der Expertenarbeit wie zuvor berichten:

void updateStopLoss (double newStopLoss) {bool modified = OrderModify (OrderTicket (), OrderOpenPrice (), newStopLoss, OrderTakeProfit (), OrderExpiration ()); if (! modified) {int errorCode = GetLastError (); if (errorCode! = ERR_NO_RESULT) {logError ("updateStopLoss", "Fehler beim Ändern der Reihenfolge", errorCode); }}}

Hier wird die integrierte Funktion OrderModify () in der Funktion updateStopLoss () aufgerufen. Diese Funktion unterscheidet sich hinsichtlich der Fehlerbehandlung geringfügig von OrderSend (). Wenn sich keiner der Parameter der zu ändernden Reihenfolge von den aktuellen Parametern unterscheidet, gibt die Funktion einen Fehler ERR_NO_RESULT zurück. Wenn in unserem Experten eine solche Situation zulässig ist, sollten wir diesen Fehler ausdrücklich ignorieren. Dazu analysieren wir den von GetLastError () zurückgegebenen Wert. Wenn ein Fehler mit dem Code ERR_NO_RESULT auftritt, geben wir nichts an das Protokoll aus.

Wenn jedoch ein anderer Fehler aufgetreten ist, muss dieser wie zuvor vollständig gemeldet werden. Deshalb speichern wir das Ergebnis der Funktion GetLastError () in einer Zwischenvariablen und übergeben den dritten Parameter an die Funktion logError (). Tatsache ist, dass die integrierte Funktion GetLastError () den Code des letzten Fehlers nach seinem Aufruf automatisch zurücksetzt. Wenn wir den Fehlercode nicht explizit an logError () übergeben würden, würde sich ein Fehler mit Code 0 und der Beschreibung „no error“ im Protokoll widerspiegeln.

Ähnliche Aktionen müssen ausgeführt werden, wenn andere Fehler verarbeitet werden, z. B. Requotes. Die Hauptidee besteht darin, nur Fehler zu behandeln, die verarbeitet werden müssen, und den Rest an die Funktion logError () zu übergeben. Dann wissen wir immer Bescheid, wenn während der Arbeit des Experten ein unerwarteter Fehler aufgetreten ist. Nach der Analyse der Protokolle können wir entscheiden, ob dieser Fehler eine separate Verarbeitung erfordert oder durch Finalisieren des Expertencodes behoben werden kann. Dieser Ansatz vereinfacht häufig das Leben erheblich und verkürzt die Zeit, die zur Behebung von Fehlern benötigt wird.

Diagnose von logischen Fehlern

Logische Fehler im Expertencode können viele Probleme verursachen. Das Fehlen der Möglichkeit, Experten schrittweise zu debuggen, macht den Kampf gegen solche Fehler zu einer nicht sehr erfreulichen Aufgabe. Das Hauptwerkzeug zur Diagnose dieses Problems ist derzeit die integrierte Print () - Funktion. Mit ihm können Sie die aktuellen Werte wichtiger Variablen ausdrucken und die Arbeit des Experten während des Tests direkt im Terminal protokollieren. Beim Debuggen eines Experten während des Testens mit Visualisierung kann auch die integrierte Comment () -Funktion helfen, die Meldungen in einem Diagramm anzeigt. Um sicherzustellen, dass der Experte nicht wie beabsichtigt arbeitet, müssen Sie in der Regel temporäre Aufrufe an die Funktion Print () hinzufügen und den internen Status des Experten an den mutmaßlichen Stellen aufzeichnen, an denen der Fehler aufgetreten ist.

Um jedoch komplexe Fehlersituationen zu erkennen, müssen Sie manchmal Dutzende solcher Aufrufe zur Funktion Print () hinzufügen, und nachdem Sie das Problem gefunden und behoben haben, müssen Sie sie löschen oder kommentieren, damit der Expertencode nicht unübersichtlich wird und das Testen nicht verlangsamt wird. Die Situation verschlechtert sich, wenn die Print () - Funktion bereits im Expertencode verwendet wird, um regelmäßig verschiedene Zustände zu protokollieren. In diesem Fall können temporäre Aufrufe von Print () nicht entfernt werden, indem einfach nach dem Ausdruck 'Print' im Expertencode gesucht wird. Sie müssen darüber nachdenken, um nützliche Aufrufe dieser Funktion nicht zu entfernen.

Wenn Sie beispielsweise die Fehler der Funktionen OrderSend (), OrderModify () und OrderClose () protokollieren, ist es hilfreich, den aktuellen Wert der Bid- und Ask-Variablen in das Protokoll einzutragen. Dies erleichtert das Erkennen der Fehlerursachen wie ERR_INVALID_STOPS und ERR_OFF_QUOTES.

Um solche Befunde im Protokoll hervorzuheben, empfehle ich folgende Hilfsfunktion:

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

Dies ist aus mehreren Gründen wünschenswert. Erstens werden solche Aufrufe jetzt bei der Suche nach "Print" im Expertencode nicht mehr vorkommen, da wir logInfo durchsuchen. Zweitens hat diese Funktion eine weitere nützliche Funktion, auf die wir später noch eingehen werden.

Das Hinzufügen und Entfernen temporärer Diagnoseaufrufe zur Funktion Print () kostet uns wertvolle Zeit. Aus diesem Grund schlage ich vor, einen anderen Ansatz in Betracht zu ziehen, mit dem sich logische Fehler im Code effektiv erkennen lassen und der es uns ermöglicht, Zeit zu sparen. Betrachten Sie die folgende einfache Funktion:

void openLongTrade (double stopLoss) {int ticket = OrderSend (Symbol (), OP_BUY, 1.0, Ask, 5, stopLoss, 0); if (ticket == -1) {logError ("openLongTrade", "Bestellung konnte nicht geöffnet werden"); }}

In diesem Fall ist es ziemlich offensichtlich, dass der Wert des stopLoss-Parameters während des normalen Betriebs des Experten niemals größer oder gleich dem aktuellen Geldkurs ist, da wir eine Long-Position eröffnen. Das heißt, wenn die openLongTrade () -Funktion aufgerufen wird, ist die stopLoss <Bid-Bedingung immer erfüllt. Da wir dies bereits beim Schreiben der betreffenden Funktion wissen, können wir sie sofort wie folgt 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", "Bestellung konnte nicht geöffnet werden"); }}

Das heißt, wir protokollieren unsere Anweisung im Code mit der neuen Hilfsfunktion assert (). Die Funktion selbst sieht ziemlich einfach aus:

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

Der erste Parameter der Funktion ist ihr Name, in dem unsere Bedingung geprüft wird (analog zur Funktion logError ()). Der zweite Parameter übergibt das Ergebnis der Überprüfung dieser Bedingung. Und der dritte Parameter ist seine Beschreibung. Wenn die erwartete Bedingung nicht erfüllt ist, werden die folgenden Informationen im Protokoll angezeigt:

  • der Name der Funktion, in der die Bedingung verletzt wurde;
  • Beschreibung der verletzten Bedingung.

Als Beschreibung können Sie beispielsweise die Bedingung selbst übergeben oder eine detailliertere Beschreibung mit den Werten der Regelgrößen zum Zeitpunkt der Prüfung der Bedingung anzeigen, wenn dies zum Verständnis der Fehlerursachen beiträgt.

Das betrachtete Beispiel ist natürlich maximal vereinfacht. Aber ich hoffe, die Hauptidee spiegelt sich ganz gut wider. Beim Aufbau der Funktionalität des Experten wissen wir, wie es funktionieren soll und welche Zustände und Eingabeparameter der Funktionen gültig sind und welche nicht. Wenn wir dies mithilfe der assert () -Funktion im Expertencode beheben, erhalten wir wertvolle Informationen über die Stelle, an der die Logik der Arbeit des Experten verletzt wird. Darüber hinaus entlasten wir uns teilweise von der Notwendigkeit, temporäre Aufrufe der Print () - Funktion hinzuzufügen und zu entfernen, da die assert () - Funktion nur dann Diagnosemeldungen an das Protokoll ausgibt, wenn unter den erwarteten Bedingungen Inkonsistenzen festgestellt werden.

Ein weiterer nützlicher Trick besteht darin, diese Funktion vor jeder Divisionsoperation zu verwenden. Tatsache ist, dass manchmal infolge des einen oder anderen logischen Fehlers eine Division durch Null auftritt. In diesem Fall wird die Arbeit des Experten abgebrochen, und im Protokoll wird nur eine Zeile mit der traurigen Diagnose "Nulldivision" angezeigt. Es ist ziemlich schwierig herauszufinden, wo genau dieser Fehler aufgetreten ist, wenn die Divisionsoperation wiederholt im Code verwendet wird. Die assert () - Funktion hilft hier weiter. Wir fügen die entsprechenden Checks vor jeder Divisionsoperation ein:

assert ("buildChannel", distance> 0, "distance> 0"); doppelte Steigung = Delta / Entfernung;

Und jetzt, im Fall einer Division durch Null, reicht es aus, nur in den Protokollen nachzuschlagen, an welcher Stelle genau der Fehler aufgetreten ist.

Staatshandhabung

Während der Bearbeitung Ihres Kontos durch den Experten können Situationen auftreten, die keine Fehler sind - sogenannte Expertenstatus. Solche Zustände sind keine Fehler, sollten aber trotzdem protokolliert werden. Dabei helfen die speziellen Funktionen der Sprache mql4.

Die Funktion IsExpertEnabled () gibt Informationen über die Fähigkeit zur Ausführung von Experten zurück. Die Funktion gibt true zurück, wenn Experten im Client-Terminal ausgeführt werden dürfen, andernfalls false. Wenn false zurückgegeben wird, ist es hilfreich, den Benutzer mit einer Aufforderung zu benachrichtigen, die entsprechende Einstellung zu aktivieren. Ein Beispiel:

void OnStart () {if (! IsExpertEnabled () {// Berater dürfen nicht mit Alerts handeln ("Achtung! Bitte drücken Sie den" Expert Advisors "-Button in MT4");} // Arbeitsalgorithmus des Beraters return;}

Wenn der Experte externe Bibliotheken verwendet, ist die Funktion IsLibrariesAllowed () hilfreich. Es gibt true zurück, wenn der Experte die Bibliotheksfunktion aufrufen kann, andernfalls false.

Wenn die Bibliothek in Form einer DLL-Datei vorliegt, ist die Funktion IsDllsAllowed () hilfreich. Es ist auch hilfreich zu prüfen, ob es generell möglich ist, mit Hilfe von Experten mit der Funktion IsTradeAllowed () zu handeln.

Wenn Sie herausfinden möchten, ob es sich um ein Demo-Konto oder ein echtes Konto handelt, können Sie die Funktion IsDemo () verwenden.

Alle oben genannten Überprüfungen sollten in der OnInit () - Funktion durchgeführt werden.

Es lohnt sich natürlich, regelmäßig die Verbindung zum Server zu überprüfen. Die Funktion IsConnected () hilft dabei.

Mithilfe der folgenden drei Funktionen können Sie ermitteln, in welchem ​​Modus sich der EA befindet. Wenn IsOptimisation () true zurückgibt, wird die Optimierung durchgeführt, wenn IsTesting (), dann wird IsVisualMode () getestet - Testen im Visualisierungsmodus. Für jede dieser Optionen kann der Berater eine eigene Logik haben. Beispielsweise können Sie für den Visualisierungsmodus etwas im Diagramm anzeigen (und aus Gründen der Ressourcenschonung nicht in anderen Modi anzeigen). Im Testmodus können Sie Debugging-Informationen anzeigen und im Optimierungsmodus den Code so weit wie möglich vereinfachen, um Zeit zu sparen.

Und die letzte Funktion ist IsTradeContextBusy (). Es wird true zurückgegeben, wenn der Thread zum Ausführen von Handelsoperationen ausgelastet ist. Dies kann nützlich sein, wenn ein Experte Handelsgeschäfte durchführt. Mit der Sleep-Funktion können Sie einen Moment warten und es erneut versuchen.

Eine weitere nützliche Funktion ist UninitializeReason ().

int deinit () {switch (UninitializeReason ()) {case REASON_CHARTCLOSE: case REASON_REMOVE: CleanUp (); brechen; // saubere und freie Ressourcen. case REASON_RECOMPILE: case REASON_CHARTCHANGE: case REASON_PARAMETERS: case REASON_ACCOUNT: StoreData (); brechen; // Vorbereitung zum Neustart. } // ...}

Sie können auch den Grund für das Beenden des Beraters protokollieren.

Codes der häufigsten Fehler und deren wahrscheinliche Lösung

FehlernummerWertDas problemLösung
4, 146Handelsserver ausgelastetWährend des Vorgangs hat der Berater zu viele Aufträge gleichzeitig erteilt oder nicht auf eine Antwort vom Server gewartet. Der Berater versucht, einen neuen Auftrag zu sendenStarten Sie das Terminal neu oder optimieren Sie den Advisor-Code mithilfe von Fehlerbehandlungsfunktionen
8, 141Zu häufige AnfragenFrühere Fehlerursachen bei einer sehr häufigen AnfrageÄhnliche Lösung
129Falscher PreisDer Preis, zu dem Sie versuchen, eine Position zu eröffnen (KAUFEN oder VERKAUFEN), ist falschKAUF muss von Ask geöffnet und von BID geschlossen werden;
SELL muss von BID geöffnet und von ASK geschlossen werden
130, 145Falsche FüßeStop Loss, Take Profit oder der Eröffnungslevel eines Pending oder Limiters sind falsch.
Die Haltestellen sind zu nah am Preis.
Ihr Konto wird in der Gruppe ECN (ETSN) oder NDD (NDD) eröffnet, sodass Sie keine Stopps sofort setzen können
Überprüfen Sie die Werte Ihrer Stop-Verluste, nehmen Sie Gewinne mit, überprüfen Sie den Mindeststopp-Level für Ihr Instrument bei einem Broker, wenn Sie Stopps setzen - beachten Sie den Mindestabstand-Level. Ein gut geschriebener Berater sollte über Funktionen zum Bearbeiten von ECN- und NDD-Konten verfügen. Dies geschieht durch Ändern der Reihenfolge nach dem Öffnen
131Falsche LautstärkeFalsches Los bei der Eröffnung eines Geschäfts oder weniger als das Minimum (mehr als das Maximum). Die Bittiefe eines Loses kann sich auch von einem Broker-Bit unterscheidenÜberprüfen Sie die korrekte Eröffnung des Loses, lesen Sie die Spezifikation des Vertrags und die Geschäftsbedingungen in Ihrem DC, überprüfen Sie das minimale und maximale Los in Ihrem DC und auf Ihrem Konto
132Der Markt ist geschlossenDer Markt ist am Wochenende geschlossenVersuchen Sie nach dem Wochenende den Markt zu kontaktieren
133Kein HandelDerzeit kein HandelEs ist verboten, mit diesem Währungspaar zu handeln - zu einem bestimmten Zeitpunkt oder allgemein. Oft haben Broker um Mitternacht eine Pause von ein paar Minuten
134Nicht genug Geld, um die Operation abzuschließenDas Los, das Sie öffnen möchten, ist zu groß, es hat nicht genügend SpielraumÜberprüfen Sie den Stand der verfügbaren Mittel und berechnen Sie die Mittel, die Sie zum Öffnen eines Loses benötigen. Überwachen Sie den Stand Ihrer verfügbaren Mittel
135-138Preis hat sich geändertRequote, too fast market (news), Broker oder DC erlauben es Ihnen nicht, eine Position zum angegebenen Preis zu platzierenHandeln Sie in solchen Momenten nicht und erhöhen Sie den Schlupf. Beachten Sie jedoch, dass dies die Eröffnung von Positionen mit sich bringt, die nicht zu dem von Ihnen angegebenen Preis erfolgen. Stellen Sie eine Fehlerbehandlungsfunktion bereit und geben Sie die Anzahl der Versuche an, Positionen im EA zu eröffnen
147Die Verwendung des Ablaufdatums der Bestellung ist vom Broker untersagtIhr Berater oder Sie versuchen, das Ablaufdatum einer ausstehenden Bestellung festzulegenSetzen Sie im Expert Advisor in der OrderSend-Funktion den Ablaufparameter auf 0 (Null). Legen Sie kein Ablaufdatum fest
148Die Anzahl der offenen und ausstehenden Bestellungen hat das vom Broker festgelegte Limit erreichtDie maximale Anzahl offener Aufträge und Positionen hat das vom Broker festgelegte Limit erreichtLöschen oder schließen Sie einen Teil der Positionen. Unterbrechen Sie den Prozess der Eröffnung neuer Positionen
4012, 4013Rest der Division durch NullSie versuchen die Zahl durch 0 (Null) zu teilenÜberprüfen Sie den Advisor-Code auf einen Fehler oder alle Werte aus MarketInfo-Funktionen zum Zeitpunkt der Rückgabe von 0, wenn MarketInfo (Symbol (), MODE_SPREAD) nicht den Spread zurückgibt, sondern 0 (für Broker mit einem variablen Spread).
4017DLL-Aufrufe nicht erlaubtDLL-Aufruf in Ihrem Terminal verbotenDLL-Aufruf zulassen über Menü - Dienst - Einstellungen - Ratgeber - DLL-Aufruf zulassen
4018, 4019Bibliothek kann nicht geladen werdenDie Bibliothek ist beschädigt oder ihr Aufruf schlägt fehl, möglicherweise fehlt sie überhauptÜberprüfen Sie die DLL
4020Aufrufe an externe Bibliotheksfunktionen nicht zulässigDas Aufrufen von Funktionen durch externe Experten ist in Ihrem Terminal nicht zulässigAnruffunktionen erlauben über Menü - Service - Einstellungen - Berater - Externe Experten anrufen lassen
4103Datei kann nicht geöffnet werdenDiese Datei existiert nicht oder ist von einem anderen Prozess gesperrt.Überprüfen Sie die angegebene Datei. Prüfen Sie, ob die Datei vom Antivirensystem gesperrt ist, ob der Schreib- / Lesemodus der Datei zulässig ist
4106Unbekannter CharakterKein Symbol in der MarktübersichtIn der Marktübersicht - Rechtsklick - alle Symbole anzeigen. Überprüfen Sie den Namen des Symbols im Berater und seine Präsenz in der Marktübersicht. Einige Berater verwenden eindeutige Namen ohne Suffixe und Broker setzen absichtlich Suffixe, z. B. EURUSDx, wobei x das Suffix ist
4108Ungültige TicketnummerDas vom Experten ausgewählte Bestellungsticket ist nicht vorhanden. Ein Experte versucht, ein Ticket zu wählen, aber dieser Auftrag wurde von einem anderen Berater oder von Hand geschlossen. Beim Versuch, eine Bestellung über eine Bestellung auszuführen, wurde das Ticket von einem Broker ausgeführt und geschlossenWenn dieser Fehler sehr häufig auftritt (100-1000 Mal pro Minute), überprüfen Sie die Funktionen Ihres Beraters. Deaktivieren Sie andere Berater oder konfigurieren Sie sie so, dass keine Konflikte auftreten. Schließen Sie den Auftrag nicht mit Ihren Händen, wenn der Experte die Operation ausführt. Dies kann vorkommen, wenn mehrere Berater dieselbe MagicNumber verwenden.
4109Handel nicht erlaubtEs ist Advisor untersagt, mit einem traurigen Lächeln oder einem Kreuz auf der Karte zu handelnAktivieren Sie das Kontrollkästchen "Advisor zum Handel zulassen" in der Einzahlung, wenn Sie den Advisor installieren, oder im Menü - Service - Einstellungen - Advisor
4110, 4111Long / Short Positionen nicht erlaubtIn den Advisor-Einstellungen auf der Registerkarte Allgemein ist die Art der Positionen nicht zulässigAuf der Registerkarte Allgemein können bei der Installation des Advisor verschiedene Positionen geöffnet werden

Fazit

Die berücksichtigten Hilfsfunktionen und einfachen Tricks können das Erkennen und Korrigieren von Fehlern im Code von Handelsexperten, die in der Programmiersprache MQL4 geschrieben sind, erheblich vereinfachen und beschleunigen. Das ordnungsgemäße Schreiben von Code und Funktionen zum Protokollieren und Verfolgen der Arbeit des Beraters beschleunigt den Entwicklungsprozess erheblich.

Sehen Sie sich das Video an: Robot Building Tutorials #6 - Intro to MQL4 (Dezember 2019).

Lassen Sie Ihren Kommentar