Articles Populaires

Choix De L'Éditeur - 2019

MQL4: Correction des erreurs et des avertissements lors de la compilation dans MetaEditor

Développer des experts en trading MQL4 n’est pas une tâche facile. Premièrement, l’algorithmisation de tout système commercial complexe pose déjà un problème, puisqu’il est nécessaire de prendre en compte de nombreux détails, à commencer par les caractéristiques du TS et par les spécificités de l’environnement MetaTrader 4. Deuxièmement, même un algorithme détaillé n’élimine pas les difficultés rencontrées lors du transfert de l’environnement développé. algorithme au langage de programmation MQL4.

Le compilateur fournit une aide pour écrire les experts appropriés. Après avoir démarré la compilation, MetaEditor signalera toutes les erreurs de syntaxe dans votre code. Mais, malheureusement, en plus des erreurs de syntaxe, votre conseiller peut également contenir des erreurs logiques que le compilateur ne peut pas détecter. Par conséquent, nous devrons le faire nous-mêmes. Comment faire cela est dans notre matériel aujourd'hui.

Les erreurs de compilation les plus courantes

S'il y a des erreurs dans le code, le programme ne peut pas être compilé. Pour un contrôle total de toutes les erreurs, il est recommandé d'utiliser le mode de compilation strict, défini par la directive:

#propriété stricte

Ce mode simplifie grandement la recherche d’erreurs. Passons maintenant aux erreurs de compilation les plus courantes.

L'identifiant correspond au mot réservé

Si le nom de la variable ou de la fonction correspond à l'un des mots réservés:

int char; // faux int char1; // corrige int char () // faux {return (0); }

alors le compilateur affiche des messages d'erreur:

Pour corriger cette erreur, vous devez corriger le nom de la variable ou de la fonction. Je recommande de rester avec le système de nommage suivant:

Toutes les fonctions doivent indiquer une action. C'est-à-dire que ça doit être un verbe. Par exemple, OpenLongPosition () ou ModifyStopLoss (). Après tout, les fonctions font toujours quelque chose, non?

En outre, il est conseillé d’appeler des fonctions dans le style dit CamelCase. Et les variables sont dans le style cebab_case. Ceci est une pratique courante.

En parlant de noms de variables. Les variables sont des noms. Par exemple, my_stop_loss, day_of_week, current_month. Nommer une variable par un nom long n’est pas si effrayant, c’est bien pire de le nommer de manière incompréhensible. Qu'est-ce qu'un Dow, indice Dow Jones? Non, c'est le jour de la semaine. Bien sûr, vous comprenez déjà ce qu'est cette variable aujourd'hui. Mais si vous ouvrez le code du conseiller un mois plus tard, tout ne sera pas si évident. Et cette fois, perdu à décoder les messages du passé - en avez-vous besoin?

Caractères spéciaux dans les noms de variables et de fonctions

Allez-y. Si les noms de variables ou de fonctions contiennent des caractères spéciaux ($, @, point):

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

alors le compilateur affiche des messages d'erreur:

Pour corriger cette erreur, vous devez encore une fois ajuster les noms des variables ou des fonctions, ou les appeler immédiatement de manière humaine. Idéalement, le code devrait être écrit de sorte que même une personne ne connaissant pas la programmation se contente de le lire et de comprendre ce qui se passe là-bas.

Erreurs d'utilisation de l'instruction switch

L'ancienne version du compilateur permettait d'utiliser n'importe quelle valeur dans les expressions et les constantes de l'instruction switch:

void start () {double n = 3.14; commutateur (n) {cas 3.14: Imprimer ("Pi"); pause cas 2.7: impression ("E"); pause }}

Dans le nouveau compilateur, les expressions et les constantes de l'instruction switch doivent être des entiers. Ainsi, lors de l'utilisation de telles constructions, des erreurs se produisent:

Par conséquent, lorsque vous analysez du code classique, tel que WallStreet, Ilan et d'autres incorruptibles (ce qui est très utile pour le développement personnel), vous pouvez rencontrer cette erreur. Il est traité très simplement, par exemple, lorsque vous utilisez une telle ligne ici:

commutateur (MathMod (day_48, 10))

Voici comment vous pouvez facilement résoudre le problème:

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

Fonction Valeurs de retour

Toutes les fonctions sauf void doivent renvoyer une valeur du type déclaré. Par exemple:

int function () {}

En mode de compilation strict (strict), une erreur se produit:

En mode compilation, le compilateur affiche un avertissement par défaut:

Si la valeur de retour de la fonction ne correspond pas à la déclaration:

int init () {return; }

Ensuite, en mode de compilation strict, une erreur se produit:

En mode compilation, le compilateur affiche un avertissement par défaut:

Pour corriger de telles erreurs dans le code de fonction, il vous suffit d'ajouter une instruction return avec une valeur de retour du type correspondant.

Tableaux dans les arguments de fonction

Les tableaux dans les arguments de fonction ne sont transmis que par référence. Auparavant, ce n’était pas le cas, vous pouvez donc trouver cette erreur chez les anciens conseillers. Voici un exemple:

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

Le code donné en mode compilation strict (strict) entraînera une erreur:

En mode compilation, le compilateur affiche un avertissement par défaut:

Pour corriger de telles erreurs, vous devez explicitement indiquer le transfert du tableau par référence, en ajoutant le préfixe & au nom du tableau:

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

Soit dit en passant, les tableaux constants (Time, Open, High, Low, Close, Volume) ne peuvent pas être passés par référence. Par exemple, un appel:

ArrayAverage (Open);

quel que soit le mode de compilation conduit à une erreur:

Pour éliminer de telles erreurs, vous devez copier les données nécessaires à partir du tableau constant:

// --- tableau pour stocker les valeurs de prix d'ouverture double OpenPrices; // --- copie les prix d'ouverture dans le tableau OpenPrices ArrayCopy (OpenPrices, Open, 0,0, WHOLE_ARRAY); // --- appelle la fonction ArrayAverage (OpenPrices);

L'une des erreurs les plus courantes est la perte d'un indicateur par un conseiller. Dans de tels cas, les utilisateurs experts des forums écrivent généralement avec colère: "Le conseiller ne fonctionne pas!" ou "je mets le conseiller sur la carte et rien ne se passe!". La solution à ce problème est en réalité très simple. Comme toujours, il suffit de regarder l'onglet "Journal" du terminal et d'y trouver une entrée comme:

2018.07.08 09: 15: 44.957 2016.01.04 00:51 ne peut pas ouvrir le fichier 'C: Users1AppDataRoamingMetaQuotesTerminal MQL4indicatorsKELTNER_F12.ex4' 2

Cela nous indique qu'ils ont oublié de mettre l'indicateur dans le dossier ou que son nom est différent. Si l'indicateur est manquant, vous devez l'ajouter au dossier avec les indicateurs. Si tel est le cas, il convient de vérifier son nom dans le code du conseiller - le plus probablement, il est appelé différemment ici.

Avertissements du compilateur

Les avertissements du compilateur ont un but informatif et ne constituent pas un message d'erreur. Cependant, ils indiquent des sources d'erreur possibles et il est préférable de les corriger. Un code propre ne doit pas contenir d'avertissements.

Intersections de noms de variables globales et locales

Si aux niveaux global et local, il existe des variables portant le même nom:

int i; // variable globale void OnStart () {int i = 0, j = 0; // variables locales pour (i = 0; i <5; i ++) {j + = i; } PrintFormat ("i =% d, j =% d", i, j); }

puis le compilateur affiche un avertissement et indique le numéro de ligne sur lequel la variable globale est déclarée:

Pour corriger de tels avertissements, vous devez ajuster les noms des variables globales.

Incompatibilité de type

Dans l'exemple suivant:

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

en mode de compilation strict avec incompatibilité de type, le compilateur affiche des avertissements:

Dans cet exemple, le compilateur met en garde contre une possible perte de précision lors de l'affectation de divers types de données et de la conversion implicite du type int en chaîne.

Pour résoudre ce problème, vous devez utiliser une conversion de type explicite:

#property strict void OnStart () {double a = 7; float b = (float) a; int c = (int) b; chaîne str = (chaîne) c; Imprimer (c); }

Variables inutilisées

La présence de variables qui ne sont pas utilisées dans le code du programme (entités supplémentaires) n'est pas une bonne forme.

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

Les messages concernant de telles variables sont affichés quel que soit le mode de compilation:

Pour résoudre ce problème, il vous suffit de supprimer les variables inutilisées du code du programme.

Diagnostic des erreurs de compilation

Souvent, après l'écriture d'un programme, des problèmes de compilation surviennent à cause d'erreurs dans le code. Ce sont peut-être les erreurs les plus diverses, mais dans tous les cas, il est nécessaire de détecter rapidement une section de code dans laquelle une erreur a été commise.

Souvent, les gens prennent beaucoup de temps et de nerfs à la recherche d’un support supplémentaire. Cependant, il existe un moyen de détecter rapidement les erreurs, qui repose sur l'utilisation de commentaires.

Écrire un code assez gros sans une seule erreur est très agréable. Mais malheureusement, cela n'arrive pas souvent. Je ne considère pas ici les erreurs qui conduisent à une exécution de code incorrecte. Nous allons parler ici d’erreurs rendant la compilation impossible.

Les erreurs courantes sont l'insertion d'un crochet supplémentaire dans une condition difficile, l'absence d'un crochet, le fait de ne pas mettre deux points, une virgule lors de la déclaration de variables, une faute de frappe dans le nom de la variable, etc. Souvent, lors de la compilation, vous pouvez voir immédiatement sur quelle ligne une erreur similaire a été commise. Mais il y a des moments où trouver une telle erreur n'est pas si simple. Ni le compilateur ni l'œil vif ne peuvent nous aider à trouver immédiatement l'erreur. Dans de tels cas, en règle générale, les programmeurs débutants commencent à "contourner" tout le code, en essayant d'identifier visuellement l'erreur. Et encore et encore, tandis que les nerfs résistent.

Cependant, MQL, comme d’autres langages de programmation, offre un excellent outil de commentaire. En l'utilisant, vous pouvez désactiver certaines parties du code. Généralement, les commentaires sont utilisés pour insérer un type de commentaire ou pour désactiver les sections de code inutilisées. Les commentaires peuvent également être appliqués avec succès lors de la recherche d'erreurs.

La recherche d’erreurs revient généralement à déterminer la section de code où l’erreur est commise, puis, dans cette section, l’erreur est localisée visuellement. Je pense qu’il est peu probable que quiconque doute qu'il est plus facile et plus rapide d'examiner 5 à 10 lignes de code plus rapidement que 100 à 500, voire plusieurs milliers.

Lorsque vous utilisez des commentaires, la tâche est extrêmement simple. Tout d'abord, vous devez commenter les différentes sections du code (parfois presque tout le code), le "désactivant" ainsi. Ensuite, le commentaire est supprimé de ces sections du code. Après le prochain retrait de commentaires, une tentative de compilation est effectuée. Si la compilation réussit, l'erreur ne se trouve pas dans cette section de code. Ensuite, la prochaine section de code s'ouvre et ainsi de suite. Lorsqu'il y a une partie problématique du code, une erreur est recherchée visuellement, puis elle est éliminée. Encore une fois, une tentative est faite pour compiler. Si tout s'est bien passé, l'erreur est résolue.

Il est important d'identifier correctement les sections de code que vous devez commenter. Si cette condition (ou une autre construction logique), alors elle devrait être entièrement commentée. Si vous commentez la section de code dans laquelle les variables sont déclarées, il est important que la section dans laquelle ces variables sont consultées ne soit pas ouverte. En d'autres termes, les commentaires doivent être appliqués conformément à la logique de programmation. Le non-respect de cette approche entraîne de nouvelles erreurs de compilation, trompeuses.

Voici un excellent exemple d'erreur lorsque vous ne savez pas où chercher et que commenter le code peut nous aider.

Erreurs d'exécution

Les erreurs qui se produisent lors de l'exécution du code de programme sont généralement appelées erreurs d'exécution. De telles erreurs dépendent généralement de l'état du programme et sont associées à des valeurs incorrectes de variables.

Par exemple, si une variable est utilisée comme index d'éléments de tableau, ses valeurs négatives entraîneront inévitablement une sortie du tableau.

Tableau hors de portée

Cette erreur se produit souvent dans les indicateurs lors de l'accès à des tampons d'indicateurs. La fonction IndicatorCounted () renvoie le nombre de barres qui n'ont pas changé depuis le dernier appel de l'indicateur. Les valeurs des indicateurs sur les barres précédemment calculées n'ont pas besoin d'être recalculées. Par conséquent, pour accélérer les calculs, il suffit de traiter uniquement les dernières barres.

La plupart des indicateurs utilisant cette méthode d'optimisation des calculs ont la forme suivante:

// + --------------------------------------------------- ------------------- + // | Fonction d'itération d'indicateur personnalisé | // + --------------------------------------------------- ------------------- + int start () {// --- parfois, au moins N barres sont requises pour le calcul (par exemple, 100) // s'il n'y en a pas sur le graphique le nombre de barres (par exemple, sur la période MN) if (barres <100) {return (-1); // arrête le calcul et quitte en avance sur la planification} // --- le nombre de barres qui n'ont pas changé depuis le dernier appel de l'indicateur int counted_bars = IndicatorCounted (); // --- en cas d'erreur, quitte si (counted_bars0 // // lors d'appels répétés, augmente la limite de 1 à // --- garantit de mettre à jour les valeurs d'indicateur pour la dernière limite de barre ++;} // --- le cycle de calcul principal de (int i = limite; i> 0; i--) {// en utilisant les valeurs des barres allant plus loin dans l'historique de 5 à 10 Buff1i = 0.5 * (Openi + 5 + Closei + 10)}}

Il y a souvent un traitement incorrect des cas, counted_bars == 0 (la position initiale de la limite doit être réduite d'une valeur égale à 1 + l'indice maximal relatif à la variable de boucle).

Il convient également de rappeler qu’au moment de l’exécution de la fonction start (), nous pouvons accéder aux éléments des tableaux de tampons indicateurs de 0 à Bars () - 1. S'il est nécessaire de travailler avec des tableaux qui ne sont pas des tampons d'indicateurs, vous devez augmenter leur taille à l'aide de la fonction ArrayResize () conformément à la taille actuelle des tampons d'indicateurs. L'indice maximum d'un élément pour l'adressage peut également être obtenu en appelant ArraySize () avec l'un des tampons d'indicateurs comme argument.

Division par zéro (division zéro)

L'erreur «Zéro division» se produit si le diviseur est égal à zéro pendant l'opération de division:

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

Lorsque ce script est exécuté, l'onglet "Experts" affiche un message d'erreur et le programme se termine:

En règle générale, une telle erreur se produit lorsque la valeur du diviseur est déterminée par les valeurs de certaines données externes. Par exemple, si les paramètres de négociation sont analysés, la valeur de la marge impliquée sera égale à 0 s'il n'y a pas d'ordres ouverts. Autre exemple: si les données analysées sont lues dans un fichier, alors si elles sont absentes, le fonctionnement correct ne peut pas être garanti. Pour cette raison, il est conseillé d'essayer de prendre en compte de tels cas et de les traiter correctement.

Le moyen le plus simple consiste à vérifier le diviseur avant l'opération de division et à afficher un message concernant la valeur incorrecte du paramètre:

void OnStart () {int a = 0, b = 0, c; si (b! = 0) {c = a / b; Imprimer (c); } else {Print ("Erreur: b = 0"); retour }}

Par conséquent, aucune erreur critique ne se produit, mais un message s'affiche concernant la valeur incorrecte du paramètre et l'opération se termine:

Utiliser 0 au lieu de NULL pour le caractère actuel

Dans l'ancienne version du compilateur, il était autorisé à utiliser 0 (zéro) comme argument dans les fonctions nécessitant un instrument financier.

Par exemple, la valeur de l'indicateur technique de moyenne mobile pour le symbole actuel peut être demandée comme suit:

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

Dans le nouveau compilateur, pour indiquer le caractère actuel, vous devez spécifier explicitement NULL:

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

De plus, le symbole et la période du graphique en cours peuvent être spécifiés à l'aide des fonctions Symbole () et Période ().

AlligatorJawsBufferi = iMA (Symbole (), Période (), 13.8, MODE_SMMA, PRICE_MEDIAN, i); // correct

Mieux encore, si vous utilisez les variables prédéfinies _Symbol et _Period, elles sont traitées plus rapidement:

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

Les chaînes Unicode et leur utilisation dans les DLL

Les chaînes sont une séquence de caractères Unicode. Gardez cela à l’esprit et utilisez les fonctionnalités Windows appropriées. Par exemple, lorsque vous utilisez les fonctions de bibliothèque wininet.dll au lieu d'InternetOpenA () et InternetOpenUrlA (), vous devez appeler InternetOpenW () et InternetOpenUrlW (). Lorsque vous passez des chaînes à une DLL, utilisez la structure MqlString:

#pragma pack (push, 1) struct MqlString {int size; // entier 32 bits, contient la taille du tampon alloué pour la chaîne LPWSTR buffer; // adresse 32 bits du tampon contenant la chaîne int reserved; // entier 32 bits, réservé, ne pas utiliser}; #pragma pack (pop, 1)

Partage de fichiers

Lors de l'ouverture de fichiers, vous devez spécifier explicitement les indicateurs FILE_SHARE_WRITE et FILE_SHARE_READ pour le partage.

S'ils sont absents, le fichier sera ouvert en mode exclusif, ce qui ne permettra à personne de l'ouvrir tant qu'il n'aura pas été fermé par le monopoleur.

Par exemple, lorsque vous utilisez des graphiques hors connexion, vous devez spécifier explicitement les indicateurs partagés:

// 1ère modification - ajout d'indicateurs de partage ExtHandle = FileOpenHistory (c_symbol + i_period + ". Hst", FILE_BIN | FILE_WRITE | FILE_SHARE_WRITE | FILE_SHARE_READ);

Fonction de conversion de date / heure

Gardez à l'esprit que la conversion d'un type de date / heure en chaîne dépend du mode de compilation:

datetime date = D'2014.03.05 15:46:58 '; string str = "mydate =" + date; // --- str = "mydate = 1394034418" - sans la directive stricte #property // --- str = "mydate = 2014.03.05 15:46:58" - avec la directive stricte #property

Par exemple, une tentative d'utilisation de fichiers dont le nom contient un signe deux-points entraîne une erreur.

Gestion des erreurs d'exécution

Etant donné qu'aucun expert commercial ne peut se passer des fonctions intégrées définies par l'utilisateur, nous tenterons tout d'abord de nous simplifier la vie en analysant les erreurs renvoyées par ces fonctions.

Certaines bibliothèques sont disponibles «prêtes à l'emploi» pour simplifier l'écriture des conseillers, y compris ceux permettant de travailler avec des erreurs. Ils sont stockés dans le dossier MQL4 / Include:

Nous aurons besoin de deux bibliothèques:

  • stderror.mqh - contient des constantes pour le nombre de chaque erreur;
  • stdlib.mqh - contient plusieurs fonctions auxiliaires, notamment la fonction de renvoi de la description de l'erreur sous forme de chaîne:
string ErrorDescription (int error_code)

Par conséquent, nous allons connecter ces deux bibliothèques à notre projet:

#include #include 

Les descriptions d'erreur elles-mêmes sont dans le fichier MQL4 / Library / stdlib.mql4 et elles sont en anglais. Par conséquent, si vous êtes contre des langues étrangères, vous pouvez toujours réécrire les descriptions dans votre langue maternelle.

Une autre fonction intégrée dont nous avons besoin est GetLastError (). C'est elle qui renvoie les codes d'erreur sous la forme d'un entier (int), que nous allons ensuite traiter. Les codes d'erreur et leurs descriptions en russe sont disponibles dans le manuel mql4 de MetaQuotes. À partir de là, vous pouvez prendre des informations pour traduire le fichier stdlib.mql4 en russe.

Maintenant que nous avons connecté les bibliothèques nécessaires, nous allons examiner les résultats des fonctions directement liées aux opérations de trading, car ignorer les dysfonctionnements de ces fonctions peut avoir des conséquences critiques pour le bot.

Malheureusement, en utilisant MQL4, vous ne pouvez pas écrire une bibliothèque généralisée pour gérer toutes les situations d'erreur possibles. Dans chaque cas, vous devrez gérer les erreurs séparément. Mais tout n'est pas si grave: de nombreuses erreurs ne doivent pas être traitées, il suffit de les éliminer au stade de développement et de test d'un expert, bien que pour cela, vous devez savoir à temps leur présence.

Par exemple, considérons deux erreurs typiques des experts MQL4:

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

L’un des cas où la première erreur se produit est la tentative de l’expert de placer une commande en attente trop près du marché. Sa présence peut nuire gravement aux performances des experts dans certains cas. Supposons par exemple qu’un expert ayant ouvert une position rentable réalise un profit tous les 150 points. Si la tentative suivante entraîne une erreur de 130 et que le prix revient irrémédiablement au niveau d'arrêt précédent, l'expert peut vous priver d'un profit légitime. Malgré la possibilité de telles conséquences, cette erreur peut être éliminée de manière fondamentale en finalisant le code expert afin qu'il prenne en compte la distance minimale autorisée entre le prix et les arrêts.

La deuxième erreur liée à l’activité du contexte commercial du terminal ne peut pas être complètement éliminée. Lorsque plusieurs experts travaillent sur le même terminal, il est toujours possible que l'un des experts tente d'ouvrir un poste alors que l'autre le fait toujours. Par conséquent, une telle erreur doit toujours être traitée.

Ainsi, nous devrions toujours savoir si l’une des fonctions intégrées utilisées renvoie une erreur pendant que l’AE fonctionne. Ceci peut être réalisé en utilisant la fonction d'assistance simple suivante:

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 ("ERREUR: code =" + err + "-" + ErrorDescription (err)); }}

Nous allons l'utiliser comme suit:

void openLongTrade () {int ticket = OrderSend (Symbol (), OP_BUY, 1.0, Ask + 5, 5, 0, 0); if (ticket == -1) {logError ("openLongTrade", "impossible d'ouvrir la commande"); }}

Bien sûr, ceci est un exemple simplifié. Pour écrire des fonctions plus compétentes d'ouverture, de fermeture et de modification des ordres, reportez-vous à cette leçon.

Le premier paramètre de la fonction logError () est le nom de la fonction dans laquelle l'erreur a été détectée, dans notre exemple, dans la fonction openLongTrade (). Si notre expert appelle la fonction OrderSend () à plusieurs endroits, cela nous permettra de déterminer exactement à quel endroit l'erreur est survenue. Le second paramètre passe la description de l'erreur afin que vous puissiez comprendre exactement où l'erreur a été détectée dans la fonction openLongTrade (). Il peut s’agir d’une brève description de l’erreur ou d’une description plus détaillée des valeurs de tous les paramètres transmis à la fonction intégrée.

Je préfère cette dernière option, car si une erreur se produit, vous pouvez immédiatement obtenir toutes les informations nécessaires à l'analyse. Par exemple, supposons qu'avant d'appeler OrderSend (), le prix actuel devait s'écarter considérablement du dernier prix connu. Par conséquent, une erreur se produira lors de l'exécution de cet exemple et les lignes suivantes apparaîtront dans le journal expert:

ERREUR: dans openLongTrade () ERREUR: impossible d'ouvrir la commande ERREUR: code = 138 - requote

C'est-à-dire que vous verrez immédiatement:

  • dans quelle fonction l'erreur s'est produite;
  • à quoi il fait référence (dans ce cas, une tentative d'ouvrir une position);
  • quelle erreur est survenue (code d'erreur et sa description).

Considérons maintenant le troisième paramètre facultatif de la fonction logError (). Il est nécessaire dans les cas où nous souhaitons traiter un type d’erreur spécifique et nous ferons rapport sur le reste dans le protocole du travail de l’expert, comme auparavant:

void updateStopLoss (double newStopLoss) {bool modified = OrderModify (OrderTicket (), OrderOpenPrice (), newStopLoss, OrderTakeProfit (), OrderExpiration ()); if (! modified) {int errorCode = GetLastError (); if (errorCode! = ERR_NO_RESULT) {logError ("updateStopLoss", "échec de la modification de la commande", errorCode); }}}

C'est ici que la fonction intégrée OrderModify () est appelée dans la fonction updateStopLoss (). Cette fonction est légèrement différente en termes de traitement des erreurs de OrderSend (). Si aucun des paramètres de l'ordre à modifier ne diffère de ses paramètres actuels, la fonction renvoie une erreur ERR_NO_RESULT. Si, dans notre cas, une telle situation est admissible, nous devons ignorer spécifiquement cette erreur. Pour ce faire, nous analysons la valeur renvoyée par GetLastError (). Si une erreur se produit avec le code ERR_NO_RESULT, nous n'émettons rien dans le protocole.

Cependant, si une autre erreur se produisait, il est nécessaire d’en faire un rapport complet, comme nous l’avions fait auparavant. C'est pourquoi nous sauvegardons le résultat de la fonction GetLastError () dans une variable intermédiaire et le passons avec le troisième paramètre à la fonction logError (). Le fait est que la fonction GetLastError () intégrée réinitialise automatiquement le code de la dernière erreur après son appel. Si nous ne transmettions pas explicitement le code d'erreur à logError (), une erreur avec le code 0 et une description de «pas d'erreur» seraient reflétées dans le protocole.

Des actions similaires doivent être effectuées lors du traitement d'autres erreurs, par exemple, des recotes. L'idée principale est de gérer uniquement les erreurs devant être traitées et de laisser le reste à la fonction logError (). Nous saurons alors toujours si une erreur inattendue s’est produite pendant le travail de l’expert. Après analyse des journaux, nous pouvons décider si cette erreur nécessite un traitement séparé ou si elle peut être éliminée en finalisant le code expert. Cette approche simplifie souvent considérablement la vie et réduit le temps nécessaire pour traiter les erreurs.

Diagnostic des erreurs logiques

Les erreurs logiques dans le code expert peuvent causer de nombreux problèmes. L'absence de possibilité de débogage d'experts étape par étape rend la lutte contre de telles erreurs peu agréable. Le principal outil de diagnostic pour le moment est la fonction intégrée Print (). En l’utilisant, vous pouvez imprimer les valeurs actuelles des variables importantes et enregistrer le travail de l’expert directement dans le terminal pendant les tests. Lors du débogage d'un expert pendant les tests avec visualisation, la fonction intégrée Comment () peut également aider, car elle affiche les messages sur un graphique. En règle générale, pour vous assurer que l'expert ne fonctionne pas comme prévu, vous devez ajouter des appels temporaires à la fonction Print () et enregistrer son état interne aux emplacements présumés où l'erreur s'est produite.

Toutefois, pour détecter des situations d'erreur complexes, vous devez parfois ajouter des dizaines d'appels de ce type à la fonction Print (). Une fois le problème trouvé et résolu, vous devez le supprimer ou le commenter afin que le code expert ne soit pas encombré et que ses tests ne soient pas ralentis. La situation empire si la fonction Print () est déjà utilisée dans le code expert pour consigner périodiquement divers états. Ensuite, la suppression des appels temporaires à Print () ne peut pas être effectuée en recherchant simplement la phrase "Print" dans le code expert. Vous devez y réfléchir afin de ne pas supprimer les appels utiles à cette fonction.

Par exemple, lors de la journalisation des erreurs des fonctions OrderSend (), OrderModify () et OrderClose (), il est utile d’imprimer la valeur actuelle des variables Bid et Ask dans le protocole. Cela facilite la reconnaissance des causes d'erreur telles que ERR_INVALID_STOPS et ERR_OFF_QUOTES.

Pour mettre en évidence ces diagnostics dans le protocole, je vous recommande d’utiliser la fonction auxiliaire suivante:

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

Ceci est souhaitable pour plusieurs raisons. Tout d'abord, de tels appels ne seront plus rencontrés lors de la recherche de 'Print' dans le code expert, car nous rechercherons logInfo. Deuxièmement, cette fonction a une autre fonctionnalité utile, dont nous parlerons un peu plus tard.

Ajouter et supprimer des appels de diagnostic temporaires à la fonction Print () nous prend un temps précieux. Par conséquent, je propose d’envisager une autre approche efficace pour détecter les erreurs logiques dans le code et nous permettant d’économiser un peu de temps. Considérons la fonction simple suivante:

void openLongTrade (double stopLoss) {int ticket = OrderSend (Symbol (), OP_BUY, 1.0, Ask, 5, stopLoss, 0); if (ticket == -1) {logError ("openLongTrade", "impossible d'ouvrir la commande"); }}

Dans ce cas, étant donné que nous ouvrons une position longue, il est bien évident que lors du fonctionnement normal de l'expert, la valeur du paramètre stopLoss ne sera jamais supérieure ou égale au cours acheteur actuel. En d’autres termes, lorsque la fonction openLongTrade () est appelée, la condition stopLoss <Bid est toujours satisfaite. Puisque nous en sommes conscients même au stade de l'écriture de la fonction en question, nous pouvons immédiatement l'utiliser comme suit:

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", "impossible d'ouvrir la commande"); }}

Autrement dit, nous enregistrons notre déclaration dans le code à l'aide de la nouvelle fonction d'assistance assert (). La fonction elle-même semble assez simple:

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

Le premier paramètre de la fonction est son nom, dans lequel notre condition est vérifiée (par analogie avec la fonction logError ()). Le deuxième paramètre transmet le résultat de la vérification de cette condition. Et le troisième paramètre est sa description. Par conséquent, si la condition attendue n’est pas remplie, les informations suivantes seront affichées dans le protocole:

  • le nom de la fonction dans laquelle la condition a été violée;
  • description de la condition violée.

En tant que description, vous pouvez transmettre, par exemple, la condition elle-même ou afficher une description plus détaillée contenant les valeurs des variables contrôlées au moment de la vérification de la condition, si cela vous aide à comprendre les causes de l'erreur.

Bien entendu, l'exemple considéré est simplifié au maximum. Mais j'espère que l'idée principale reflète assez bien. Au cours du processus de création des fonctionnalités de l’expert, nous sommes conscients de son fonctionnement, ainsi que des états et paramètres d’entrée des fonctions valides et des autres. En corrigeant cela dans le code expert en utilisant la fonction assert (), nous obtenons des informations précieuses sur le lieu où la logique du travail de l’expert est violée. De plus, nous nous libérons partiellement de la nécessité d'ajouter et de supprimer des appels temporaires à la fonction Print (), car la fonction assert () envoie des messages de diagnostic au protocole uniquement lorsque des incohérences sont détectées dans les conditions attendues.

Une autre astuce utile consiste à utiliser cette fonction avant chaque opération de division. Le fait est que parfois, à la suite d'une erreur logique ou d'une autre, une division par zéro se produit parfois. Dans ce cas, le travail de l’expert est terminé et une seule ligne apparaît dans le protocole avec un triste diagnostic: "division zéro". Il est assez difficile de savoir exactement où cette erreur s'est produite si l'opération de division est utilisée à plusieurs reprises dans le code. La fonction assert () aidera ici. Nous insérons les contrôles correspondants avant chaque opération de division:

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

Et maintenant, dans le cas d'une division par zéro, il suffira simplement de regarder dans les journaux pour savoir à quel endroit exact l'erreur s'est produite.

Traitement d'état

Tandis que l'expert travaille sur votre compte, il peut arriver que certaines situations ne soient pas des erreurs - les soi-disant États experts. De tels états ne sont pas des erreurs, mais ils doivent quand même être enregistrés. Les fonctions spéciales du langage mql4 aident à cela.

La fonction IsExpertEnabled () renvoie des informations sur la possibilité d’exécuter des experts. La fonction retournera true si les experts sont autorisés à s'exécuter dans le terminal client, sinon renvoie false. Si false est renvoyé, il sera utile d'informer l'utilisateur par une demande d'activation du paramètre approprié. Un exemple:

void OnStart () {if (! IsExpertEnabled () {// les conseillers ne sont pas autorisés à échanger Alert ("Attention! Veuillez appuyer sur le bouton" Experts conseillers "dans MT4");} // l'algorithme de travail du retour de conseiller;}

Si l'expert utilise des bibliothèques externes, la fonction IsLibrariesAllowed () est utile. Il renvoie true si l'expert peut appeler la fonction de bibliothèque, sinon renvoie false.

Si la bibliothèque se présente sous la forme d'un fichier dll, la fonction IsDllsAllowed () est utile. Il sera également utile de vérifier s’il est généralement possible d’échanger avec l’aide d’experts utilisant la fonction IsTradeAllowed ().

Si vous voulez savoir si le compte est réel ou démo, vous pouvez utiliser la fonction IsDemo ().

Toutes les vérifications ci-dessus doivent être effectuées dans la fonction OnInit ().

Bien sûr, il convient de vérifier périodiquement la connexion avec le serveur. La fonction IsConnected () aidera.

Les trois fonctions suivantes aideront à déterminer le mode dans lequel se trouve l’AE. Si IsOptimisation () renvoie true, l'optimisation est effectuée si IsTesting () teste, puis IsVisualMode () - teste en mode visualisation. Pour chacune de ces options, le conseiller peut avoir sa propre logique. Par exemple, pour le mode de visualisation, vous pouvez afficher quelque chose sur le graphique (et non dans d'autres modes pour économiser des ressources). En mode test, vous pouvez afficher les informations de débogage, en mode optimisation, alléger le code autant que possible afin de gagner du temps.

Et la dernière fonction est IsTradeContextBusy (). Il retournera true si le thread pour effectuer les opérations de trading est occupé. Cela peut être utile lorsqu'un expert effectue des opérations de trading. Vous pouvez utiliser la fonction Veille pour attendre un moment et réessayer.

UninitializeReason () est une autre fonctionnalité utile.

int deinit () {switch (UninitializeReason ()) {case REASON_CHARTCLOSE: case REASON_REMOVE: CleanUp (); pause // ressources propres et gratuites. case REASON_RECOMPILE: case REASON_CHARTCHANGE: case REASON_PARAMETERS: case REASON_ACCOUNT: StoreData (); pause // préparation au redémarrage. } // ...}

Vous pouvez également enregistrer la raison pour laquelle le conseiller quitte.

Codes des erreurs les plus courantes et leur solution probable

Numéro d'erreurValeurLe problèmeLa solution
4, 146Serveur de négociation occupéLe conseiller a passé trop de commandes en même temps ou sans attendre de réponse du serveur pendant l'opération. Le conseiller tente d'envoyer une nouvelle commande.Redémarrage du terminal ou optimisation du code du conseiller à l'aide de fonctions de traitement des erreurs
8, 141Demandes trop fréquentesLes erreurs précédentes dans une requête très fréquenteSolution similaire
129Mauvais prixLe prix auquel vous essayez d'ouvrir une position (ACHETER ou VENDRE) est incorrectACHETER doit être ouvert par Ask et fermé par BID;
SELL doit être ouvert par BID et fermé par ASK
130, 145Mauvais piedsStop Loss, Take Profit ou le niveau d'ouverture d'un en attente ou d'un limiteur sont incorrects.
Les arrêts sont trop proches du prix.
Votre compte est ouvert dans le groupe ECN (ETSN) ou NDD (NDD), ce qui vous empêche de placer immédiatement des arrêts.
Vérifiez les valeurs de vos stop loss, prenez des bénéfices, vérifiez le niveau minimum de votre instrument avec un courtier lors du paramétrage - observez le niveau minimum de la distance. Un conseiller bien écrit devrait avoir des fonctions pour travailler sur les comptes ECN et NDD - cela se produit en modifiant l'ordre après son ouverture.
131Mauvais volumeMauvais lot lors de l'ouverture d'une transaction, ou inférieur au minimum (supérieur au maximum). La profondeur de bits d'un lot peut également différer d'un bit de courtierVérifiez l’ouverture correcte du lot, étudiez le cahier des charges du contrat et lisez les termes de l’échange dans votre centre de distribution, vérifiez les lots minimal et maximal dans votre centre de distribution et sur votre compte.
132Le marché est ferméLe marché est fermé le week-endEssayez de contacter le marché après le week-end
133Pas de commerceAucun échange en ce momentIl est interdit de négocier sur cette paire de devises - à un moment donné ou en général. Les courtiers ont souvent une pause de quelques minutes à minuit
134Pas assez d'argent pour terminer l'opérationLe lot que vous essayez d'ouvrir est trop grand, il n'a pas assez de margeVérifiez le niveau de fonds disponibles et calculez les fonds dont vous avez besoin pour ouvrir beaucoup, surveillez le niveau de vos fonds disponibles
135-138Le prix a changéRequote, marché trop rapide (news), Broker ou DC ne vous permet pas de placer une position au prix déclaréNe négociez pas à de tels moments, augmentez le niveau de glissement, mais souvenez-vous que cela implique l’ouverture de positions qui ne sont pas au prix que vous avez indiqué. Fournit une fonction de traitement des erreurs et le nombre de tentatives d'ouverture de positions dans l'EA
147L'utilisation de la date d'expiration de la commande est interdite par le courtierVotre conseiller ou vous essayez de définir la date d'expiration d'une commande en attenteDans Expert Advisor, dans la fonction OrderSend, définissez le paramètre d'expiration sur 0 (zéro). Ne pas définir de date d'expiration
148Le nombre d'ordres ouverts et en attente a atteint la limite fixée par le courtierLe nombre maximum d'ordres ouverts et de positions a atteint la limite fixée par le courtierSupprimer ou fermer une partie des positions. Arrêtez le processus d'ouverture de nouvelles positions
4012, 4013Reste de la division par zéroVous essayez de diviser le nombre par 0 (zéro)Vérifiez le code du conseiller pour rechercher une erreur ou vérifiez toutes les valeurs des fonctions MarketInfo au moment de renvoyer 0, parfois lorsque MarketInfo (Symbol (), MODE_SPREAD) ne renvoie pas un spread, mais 0 (pour les courtiers avec un spread flottant)
4017Appels DLL non autorisésAppels DLL interdits dans votre terminalAutoriser les appels DLL via Menu - Service - Paramètres - Conseiller - Autoriser les appels DLL
4018, 4019Impossible de charger la bibliothèqueLa bibliothèque est endommagée ou son appel échoue, peut-être qu'il manque du toutVérifier la DLL
4020Les appels aux fonctions de bibliothèque externes ne sont pas autorisésL'appel de fonctions d'experts externes est interdit dans votre terminalAutoriser les fonctions d'appel via Menu - Service - Paramètres - Advisor - Autoriser l'appel d'experts externes
4103Impossible d'ouvrir le fichierCe fichier n'existe pas ou est verrouillé par un autre processus.Recherchez le fichier spécifié. Vérifiez si le fichier est verrouillé par le système anti-virus, si le mode lecture-écriture du fichier est autorisé
4106Personnage inconnuAucun symbole dans l'aperçu du marchéDans l’aperçu du marché, cliquez avec le bouton droit de la souris pour afficher tous les symboles. Vérifiez le nom du symbole dans le conseiller et sa présence dans la vue d'ensemble du marché. Certains conseillers utilisent des noms clairs sans suffixe et les courtiers définissent intentionnellement des suffixes, par exemple EURUSDx où x est le suffixe.
4108Numéro de billet invalideLe ticket de commande sélectionné par l'expert n'existe pas. Un expert essaie de choisir un billet, mais cette commande a été clôturée par un autre conseiller ou manuellement. En essayant d'exécuter un ordre sur un ordre, le ticket a été exécuté et fermé par un courtierSi cette erreur apparaît très souvent, 100 à 1 000 fois par minute, vérifiez les fonctions de votre conseiller. Désactivez les autres conseillers ou configurez-les afin qu'ils ne soient pas en conflit, ne fermez pas l'ordre avec vos mains lorsque l'expert effectue l'opération. Cela se produit parfois lorsque plusieurs conseillers utilisent le même numéro MagicNumber.
4109Commerce non autoriséIl est interdit au conseiller d’échanger, sur le graphique un sourire triste ou une croixActivez la case à cocher "Autoriser le conseiller à négocier" dans le dépôt lors de l'installation du conseiller ou dans le menu - service - paramètres - conseillers.
4110, 4111Positions longues / courtes non autoriséesDans les paramètres du conseiller, dans l'onglet Général, le type de postes n'est pas autorisé.Dans l’onglet Général, lors de l’installation du conseiller, vous avez le choix entre plusieurs positions autorisées.

Conclusion

Les fonctions auxiliaires envisagées et les astuces simples peuvent considérablement simplifier et accélérer le processus de détection et de correction des erreurs dans le code des experts en trading rédigé dans le langage de programmation MQL4. La rédaction correcte du code et des fonctions permettant de consigner et de suivre le travail du conseiller accélère considérablement son processus de développement.

Regarde la vidéo: Robot Building Tutorials #6 - Intro to MQL4 (Décembre 2019).

Laissez Vos Commentaires