English Русский 中文 Español Deutsch 日本語 Português 한국어 Italiano Türkçe
Débogage des programmes MQL5

Débogage des programmes MQL5

MetaTrader 5Exemples | 13 janvier 2022, 10:46
278 0
Mykola Demko
Mykola Demko

Introduction

Cet article est principalement destiné aux programmeurs qui ont déjà appris le langage mais qui ne maîtrisent pas encore complètement le développement du programme. Il met en évidence les problèmes clés que chaque développeur traite lors du débogage d'un programme. Alors, qu'est-ce que le débogage ?

Le débogage est une étape du développement du programme destinée à détecter et à supprimer les erreurs d'exécution du programme. Au cours du processus de débogage, un développeur analyse une application en essayant de détecter d'éventuels problèmes. Les données à analyser sont reçues en observant les variables et l'exécution du programme (quelles fonctions sont appelées et quand).

Il existe deux technologies de débogage complémentaires :

  • Utilisation du debugger - utilitaire montrant l'exécution étape par étape du programme développé.
  • Affichage interactif des états des variables et des appels de fonctions sur un écran, dans le journal ou dans un fichier.

Supposons que vous connaissiez MQL5, y compris les variables, les structures, etc. Mais vous n'avez pas encore développé de programmes vous-même. La première chose que vous effectuerez est une compilation. En fait, il s'agit de la première étape du débogage.


1. Compilation

Compilation traduit un code source d'un langage de programmation de haut niveau vers un langage de niveau inférieur.

Le compilateur MetaEditor traduit les programmes en bytecode, pas en code natif (suivez le lien pour plus de détails). Cela permet de développer des programmes cryptés. En outre, un bytecode peut être lancé dans les systèmes d'exploitation 32 et 64 bits.

Mais revenons à la compilation, qui est la première étape du débogage. Après avoir appuyé sur F7 (ou sur le bouton Compiler), MetaEditor 5 rapportera toutes les erreurs que vous avez commises lors de l'écriture du code. L'onglet "Errors" de la fenêtre "Toolbox" contient la description des erreurs détectées et leur emplacement. Mettez en évidence la ligne de description à l'aide du curseur et appuyez sur Entrée pour accéder directement à l'erreur.

Seuls deux types d'erreurs sont affichés par le compilateur :

  • Syntax errors (affichées en rouge) - un code source ne peut pas être compilé tant qu'il n'est pas éliminé.
  • Warnings (affichés en jaune) - un code sera respecté mais il serait préférable de corriger de telles erreurs.

Les erreurs de syntaxe sont souvent causées par la négligence. Par exemple, "," et ";" peut être facilement confondu lors de la déclaration de variables :

int a; b; // incorrect declaration

Le compilateur renverra une erreur dans le cas d'une telle déclaration. La déclaration correcte ressemblera à ceci :

int a, b; // correct declaration

ou

int a; int b; // correct declaration

Les avertissements ne doivent pas non plus être ignorés (de nombreux développeurs sont trop négligents à leur sujet). Si MetaEditor 5 a renvoyé des avertissements lors de la compilation, un programme sera créé mais il n'y a aucune garantie qu'il fonctionnera comme vous l'aviez prévu.

Les avertissements ne sont que la pointe de l'iceberg cachant les efforts majeurs des développeurs MQL5 pour systématiser les fautes de frappe courantes des programmeurs.

Supposons que vous allez comparer deux variables :

if(a==b) { } // if a is equal to b, then ...

Mais à cause d'une faute de frappe ou d'un oubli, vous utilisez "=" au lieu de "==". Dans ce cas, le compilateur interprète le code de la manière suivante :

if(a=b) { } // assign b to a, if a is true, then ... (unlike MQL4, it is applicable in MQL5)

Comme nous pouvons le voir, cette faute de frappe peut changer radicalement le fonctionnement du programme. Par conséquent, le compilateur affichera l'avertissement pour cette ligne.

Résumons-le : la compilation est la première étape du débogage. Les avertissements du compilateur ne doivent pas être ignorés.

Fig. 1. Débogage des données pendant la compilation

Fig. 1. Débogage des données lors de la compilation.


2. Débogueur

La deuxième étape de débogage utilise Debugger (raccourci F5). Le débogueur lance votre programme en mode émulation en l'exécutant pas à pas. Le débogueur est une nouvelle fonctionnalité de MetaEditor 5, car il est absent de MetaEditor 4. C'est pourquoi il n'y a aucune expérience de son utilisation par les programmeurs passant de MQL4 à MQL5.

L'interface du débogueur comporte trois boutons principaux et trois boutons auxiliaires :

  • Démarrer [F5] - démarrer le débogage.
  • Pause [Break] - interrompt le débogage.
  • Arrêter [Maj+F5] - arrêter le débogage.
  • Entrez dans [F11] - l'utilisateur est déplacé à l'intérieur de la fonction appelée dans cette ligne.
  • Passer au-dessus de [F10] - le débogueur ignore un corps de la fonction appelée dans cette chaîne de caractères et passe à la ligne suivante.
  • Sortir [Shift+F11] - un utilisateur quitte le corps de la fonction dans laquelle il se trouve actuellement.

C'est l'interface du débogueur. Mais comment doit-on l'utiliser ? Le débogage du programme peut commencer à partir de la ligne à laquelle un programmeur a défini la fonction de débogage spéciale DebugBreak(), ou à partir d'un point d'arrêt qui peut être défini en appuyant sur le bouton F9 ou en cliquant sur un bouton spécial de la barre d'outils :

Fig. 2. Définition des points d'arrêt

Fig. 2. Définir des points d'arrêt.

Sans points d'arrêt, le débogueur exécutera simplement le programme et signalera que le débogage est réussi mais vous ne verrez rien. En utilisant DebugBreak, vous pouvez ignorer une partie du code qui ne vous intéresse pas et lancer une vérification étape par étape du programme à partir de la ligne que vous considérez comme problématique.

Nous avons donc lancé le débogueur, placé DebugBreak au bon endroit et nous examinons maintenant l'exécution du programme. Quoi de neuf ? Comment peut-il nous aider à comprendre ce qui se passe avec le programme ?

Tout d'abord, regardez la partie gauche de la fenêtre du débogueur. Il affiche le nom de la fonction et le numéro de la ligne où vous vous trouvez actuellement. Deuxièmement, regardez du côté droit de la fenêtre. Il est vide mais vous pouvez saisir le nom de n'importe quelle variable dans le champ Expression. Entrez le nom de la variable pour voir sa valeur actuelle dans le champ Valeur.

La variable peut également être sélectionnée et ajoutée à l'aide de la touche de raccourci [Shift+F9] ou à partir du menu contextuel comme indiqué ci-dessous :

Fig. 3. Ajout de la surveillance des variables lors du débogage

Fig. 3. Ajout de la surveillance des variables lors du débogage.

Ainsi, vous pouvez suivre une ligne de code, à laquelle vous vous trouvez en ce moment, et afficher les valeurs des variables importantes. En analysant tout cela, vous finirez par être en mesure de comprendre si le programme fonctionne correctement.

Il n'y a pas lieu de s'inquiéter que la variable qui vous intéresse soit déclarée localement alors que vous n'avez pas encore atteint la fonction dans laquelle elle est déclarée. Tant que vous êtes en dehors de la portée de la variable, elle aura la valeur "Identifiant inconnu". Cela signifie que la variable n'est pas déclarée. Cela ne provoquera pas l'erreur du débogueur. Après avoir atteint la portée de la variable, vous verrez sa valeur et son type.

Fig. 4. Processus de débogage - visualisation des valeurs des variables

Fig. 4. Processus de débogage. Affichage des valeurs des variables.

Ce sont les principales fonctionnalités du débogueur. La section Tester montre ce qui ne peut pas être fait dans le débogueur.


3. Profileur

Le code profiler est un ajout important au débogueur. En fait, il s'agit de la dernière étape du débogage du programme consistant en son optimisation.

Le profileur est appelé à partir du menu MetaEditor 5 en cliquant sur le bouton "Start profiling". Au lieu de l'analyse de programme étape par étape offerte par le débogueur, le profileur exécute le programme. Si un programme est un indicateur ou un Expert Advisor, le profileur fonctionnera jusqu'à ce que le programme soit déchargé. Le déchargement peut être effectué en supprimant un indicateur ou un Expert Advisor du graphique, ainsi qu'en cliquant sur "Stop profiling".

Le profilage nous fournit des statistiques importantes : combien de fois chaque fonction a été appelée, combien de temps a été consacré à son exécution. Peut-être serez-vous un peu confus par les statistiques en termes de pourcentage. Il faut comprendre que les statistiques ne prennent pas en compte les fonctions imbriquées. Par conséquent, la somme de toutes les valeurs en pourcentage dépassera largement 100 %.

Mais malgré ce fait, le profileur reste un outil puissant pour optimiser les programmes permettant aux utilisateurs de voir quelle fonction doit être optimisée pour la vitesse et où vous pouvez économiser de la mémoire.

Fig. 5. Résultats de l'opération du profileur

Fig. 5. Résultats de l'opération du profileur.


4. Interactivité

Quoi qu'il en soit, je pense que les fonctions d'affichage des messages - Print et Comment sont les principaux outils de débogage. Premièrement, ils sont très faciles à utiliser. Deuxièmement, les programmeurs passant à MQL5 à partir de la version précédente du langage les connaissent déjà.

La fonction "Print" envoie le paramètre passé au fichier journal et à l'onglet de l'outil Experts sous forme de chaîne de texte. L'heure d'envoi et le nom du programme qui a appelé la fonction sont affichés à gauche du texte. Lors du débogage, la fonction est utilisée pour définir quelles valeurs ​sont contenus dans les variables.

En plus des valeurs des variables, il est parfois nécessaire de connaître la séquence d'appels de ces fonctions. Les macros "__FUNCTION__" et "__FUNCSIG__" peuvent être utilisées pour cela. La première macro renvoie une chaîne avec le nom de la fonction à partir de laquelle elle est appelée, tandis que la seconde affiche en plus la liste des paramètres de la fonction appelée.

Ci-dessous, vous pouvez voir l'utilisation des macros :

//+------------------------------------------------------------------+
//| Example of displaying data for debugging                             |
//+------------------------------------------------------------------+
void myfunc(int a)
  {
   Print(__FUNCSIG__); // display data for debugging 
//--- here is some code of the function itself
  }

Je préfère utiliser la macro "__FUNCSIG__" car elle montre la différence entre les fonctions surchargées (ayant le même nom mais des paramètres différents).

Il est souvent nécessaire de sauter certains appels ou même de se concentrer sur un appel de fonction particulier. À ces fins, la fonction d'impression peut être protégée par une condition. Par exemple, print ne peut être appelé qu'après la 1013e itération :

//+------------------------------------------------------------------+
//| Example of data output for debugging                             |
//+------------------------------------------------------------------+
void myfunc(int a)
  {
//--- declare the static counter
   static int cnt=0;
//--- condition for the function call
   if(cnt==1013)
      Print(__FUNCSIG__," a=",a); // data output for debugging
//--- increment the counter
   cnt++;
//--- here is some code of the function itself
  }

La même chose peut être faite pour la fonction Commentaire, qui affiche les commentaires dans le coin supérieur gauche d'un graphique. C'est un grand avantage, car vous n'avez pas à basculer vers n'importe où pendant le débogage. Cependant, lors de l'utilisation de la fonction, chaque nouveau commentaire supprime le précédent. Cela peut être considéré comme un inconvénient (même si c'est parfois pratique).

Pour éliminer cet inconvénient, la méthode d'écriture supplémentaire d'une nouvelle chaîne dans la variable peut être appliquée. Tout d'abord, la variable de type string est déclarée (dans la plupart des cas, globalement) et initialisée par la valeur vide. Ensuite, chaque nouvelle chaîne de texte est placée au début avec le caractère de saut de ligne ajouté, tandis que la valeur précédente de la variable est ajoutée à la fin.

string com=""; // declare the global variable for storing debugging data
//+------------------------------------------------------------------+
//| Example of data output for debugging                             |
//+------------------------------------------------------------------+
void myfunc(int a)
  {
//--- declare the static counter
   static int cnt=0;
//--- storing debugging data in the global variable
   com=(__FUNCSIG__+" cnt="+(string)cnt+"\n")+com;
   Comment(com); // вывод информации для отладки
//--- increase the counter
   cnt++;
//--- here is some code of the function itself
  }

Nous arrivons ici à une autre occasion de visualiser le contenu du programme en détail - l'impression dans un fichier. Les fonctions d'impression et de commentaire peuvent ne pas toujours convenir aux gros volumes de données ou à l'impression à grande vitesse. Le premier peut parfois ne pas avoir assez de temps pour afficher les modifications (car les appels peuvent s'exécuter avant l'affichage et causer de la confusion), le second - car il fonctionne encore plus lentement. En outre, le commentaire ne peut pas être relu et examiné en détail.

L'impression dans un fichier est la méthode de sortie de données la plus pratique lorsque vous devez vérifier la séquence d'appels ou consigner une grande quantité de données. Cependant, il faut garder à l'esprit que l'impression n'est pas utilisée à chaque itération mais à la fin du fichier, tandis que la sauvegarde des données dans une variable chaîne est utilisée à chaque itération selon le principe décrit ci-dessus (la seule différence est que le de nouvelles données sont en outre écrites à la fin de la variable).

string com=""; // declare the global variable for storing debugging data
//+------------------------------------------------------------------+
//| Program shutdown                                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- saving data to the file when closing the program
   WriteFile();
  }
//+------------------------------------------------------------------+
//| Example of data output for debugging                             |
//+------------------------------------------------------------------+
void myfunc(int a)
  {
//--- declare the static counter
   static int cnt=0;
//--- storing debugging data in the global variable
   com+=__FUNCSIG__+" cnt="+(string)cnt+"\n";
//--- increment the counter
   cnt++;
//--- here is some code of the function itself
  }
//+------------------------------------------------------------------+
//| Save data to file                                                |
//+------------------------------------------------------------------+
void WriteFile(string name="Отладка")
  {
//--- open the file
   ResetLastError();
   int han=FileOpen(name+".txt",FILE_WRITE|FILE_TXT|FILE_ANSI," ");
//--- check if the file has been opened
   if(han!=INVALID_HANDLE)
     {
      FileWrite(han,com); // печать данных
      FileClose(han);     // закрытие файла
     }
   else
      Print("File open failed "+name+".txt, error",GetLastError());
  }

La fonction WriteFile est appelée dans OnDeinit. Par conséquent, toutes les modifications apportées au programme sont écrites dans le fichier.

Remarque : si votre log est trop gros, il serait judicieux de le stocker dans plusieurs variables. La meilleure façon de le faire est de placer le contenu de la variable texte dans une cellule de tableau de type chaîne et de mettre à zéro la variable com (préparation à l'étape suivante du travail).

Cela doit être fait après chaque 1-2 millions de chaînes (entrées non récurrentes). Tout d'abord, vous éviterez la perte de données causée par le débordement de variable (d'ailleurs, je n'ai pas pu le faire malgré tous mes efforts, car les développeurs ont travaillé dur sur le type de chaîne). Deuxièmement, et le plus important - vous pourrez afficher les données dans plusieurs fichiers au lieu d'ouvrir un gros fichier dans l'éditeur.

Afin de ne pas suivre constamment la quantité de chaînes enregistrées, vous pouvez utiliser la séparation des fonctions pour travailler avec des fichiers en trois parties. La première partie ouvre le fichier, la seconde écrit dans le fichier à chaque itération et la troisième ferme le fichier.

//--- open the file
int han=FileOpen("Debugging.txt",FILE_WRITE|FILE_TXT|FILE_ANSI," ");
//--- print data
if(han!=INVALID_HANDLE) FileWrite(han,com);
if(han!=INVALID_HANDLE) FileWrite(han,com);
if(han!=INVALID_HANDLE) FileWrite(han,com);
if(han!=INVALID_HANDLE) FileWrite(han,com);
//--- close the file
if(han!=INVALID_HANDLE) FileClose(han);

Mais cette méthode doit être utilisée avec prudence. Si l'exécution de votre programme échoue (par exemple, en raison d'une division nulle), vous pouvez recevoir un fichier ouvert ingérable qui peut perturber le travail de votre système d'exploitation.

De plus, je déconseille fortement d'utiliser la boucle complète d'ouverture-écriture-fermeture à chaque itération. Mon expérience personnelle dit que votre disque dur mourra dans quelques mois dans ce cas.


5. Testeur

Lors du débogage des Expert Advisors, vous devez souvent vérifier l'activation d'une condition particulière. Mais le débogueur mentionné ci-dessus lance un Expert Advisor uniquement en mode temps réel et vous pouvez attendre pas mal de temps avant que ces conditions soient enfin activées.

En fait, des conditions commerciales spécifiques peuvent survenir rarement. Cependant, nous savons qu'ils se produisent, mais ce serait une chose absurde de les attendre pendant des mois. Alors, que pouvons-nous faire?

Le strategytester peut vous aider dans ce cas. Les mêmes fonctions Imprimer et Commenter sont utilisées pour le débogage. Le commentaire prend toujours la première place pour évaluer la situation, tandis que la fonction Imprimer est utilisée pour une analyse plus détaillée. Le testeur stocke les données affichées dans les journaux du testeur (répertoire distinct pour chaque agent testeur).

Pour lancer un Expert Advisor au bon intervalle, je localise l'heure (où se produisent les pannes, à mon avis), fixe la date nécessaire dans le testeur et le lance en mode visualisation à tous les ticks.

J'aimerais également mentionner que j'ai emprunté cette méthode de débogage à MetaTrader 4 où c'était presque le seul moyen de déboguer un programme pendant son exécution.

Fig. 6. Débogage à l'aide du Strategy Tester

Fig. 6. Débogage à l'aide du Strategy Tester.


6. Débogage en POO

La programmation orientée objet, apparue dans MQL5, a influencé le processus de débogage. Lors du débogage des procédures, vous pouvez naviguer facilement dans le programme en utilisant uniquement les noms de fonction. Cependant, en POO, il est souvent nécessaire de connaître l'objet à partir duquel les différentes méthodes sont appelées. C'est particulièrement important lorsque les objets sont conçus verticalement (en utilisant l'héritage). Les Modèles (récemment introduits dans MQL5) peuvent aider dans ce cas.

La fonction modèle permet de recevoir le type de pointeur sous forme de valeur de type chaîne.

template<typename T> string GetTypeName(const T &t) { return(typename(T)); }

J'utilise cette propriété pour déboguer de la manière suivante :

//+------------------------------------------------------------------+
//| Base class contains the variable for storing the type             |
//+------------------------------------------------------------------+
class CFirst
  {
public:
   string            m_typename; // variable for storing the type
   //--- filling the variable by the custom type in the constructor
                     CFirst(void) { m_typename=GetTypeName(this); }
                    ~CFirst(void) { }
  };
//+------------------------------------------------------------------+
//| Derived class changes the value of the base class variable  |
//+------------------------------------------------------------------+
class CSecond : public CFirst
  {
public:
   //--- filling the variable by the custom type in the constructor
                     CSecond(void) { m_typename=GetTypeName(this); }
                    ~CSecond(void) {  }
  };

La classe de base contient la variable pour stocker son type (la variable est initialisée dans le constructeur de chaque objet). La classe dérivée utilise également la valeur de cette variable pour stocker son type. Ainsi, lorsque la macro est appelée, je viens d'ajouter la variable m_typename recevant non seulement le nom de la fonction appelée mais aussi le type de l'objet qui a appelé cette fonction.

Le pointeur lui-même peut être dérivé pour une reconnaissance plus précise des objets permettant aux utilisateurs de différencier les objets par des nombres. A l'intérieur de l'objet, cela se fait comme suit :

Print((string)this); // print pointer number inside the class

À l'extérieur, il se présente comme suit :

Print((string)GetPointer(pointer)); // print pointer number outside the class

De plus, la variable de stockage du nom de l'objet peut être utilisée à l'intérieur de chaque classe. Dans ce cas, il est possible de passer le nom de l'objet en paramètre de constructeur lors de la création d'un objet. Cela vous permettra non seulement de diviser les objets par leur nombre, mais aussi de comprendre ce que chaque objet représente (comme vous les nommerez). Cette méthode peut être réalisée de la même manière que le remplissage de la variable m_typename.


7. Traçage

Toutes les méthodes mentionnées ci-dessus se complètent et sont très importantes pour le débogage. Cependant, il existe une autre méthode qui n'est pas si populaire - le traçage.

Cette méthode est rarement utilisée en raison de sa complexité. Lorsque vous êtes bloqué et que vous ne comprenez pas ce qui se passe, le traçage peut vous aider.

Cette méthode permet de comprendre la structure de l'application - la séquence et les objets des appels. En utilisant le traçage, vous comprendrez ce qui ne va pas avec le programme. De plus, la méthode donne une vue d'ensemble du projet.

Le traçage est effectué de la manière suivante. Créez deux macros :

//--- opening substitution  
#define zx Print(__FUNCSIG__+"{");
//--- closing substitution
#define xz Print("};");

Il s'agit d'ouvrir les macros zx et de fermer les macros xz en conséquence. Plaçons-les sur des corps fonctionnels à tracer :

//+------------------------------------------------------------------+
//| Example of function tracing                                       |
//+------------------------------------------------------------------+
void myfunc(int a,int b)
  {
   zx
//--- here is some code of the function itself
   if(a!=b) { xz return; } // exit in the middle of the function
//--- here is some code of the function itself
   xz return;
  }

Si la fonction contient une sortie selon des conditions, nous devons définir la fermeture xz dans la zone protégée avant chaque retour. Cela évitera de perturber la structure de traçage.

Notez que la macro mentionnée ci-dessus est utilisée uniquement pour simplifier l'exemple. Il est préférable d'utiliser print to file pour le traçage. En outre, j'utilise une astuce lors de l'impression dans un fichier. Pour voir toute la structure de traçage, j'enveloppe les noms de fonction dans la construction syntaxique suivante :

if() {...}

Le fichier résultant est défini avec l'extension ".mqh" qui permet de l'ouvrir dans MetaEditor et d'utiliser styler [Ctrl+,] pour afficher la structure de traçage.

Le code de traçage complet est indiqué ci-dessous :

string com=""; // declare global variable for storing debugging data
//--- opening substitution
#define zx com+="if("+__FUNCSIG__+"){\n";
//--- closing substitution
#define xz com+="};\n"; 
//+------------------------------------------------------------------+
//| Program shutdown                                      |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- //--- saving data to the file when closing the program
   WriteFile();
  }
//+------------------------------------------------------------------+
//| Example of the function tracing                                       |
//+------------------------------------------------------------------+
void myfunc(int a,int b)
  {
   zx
//--- here is some code of the function itself
   if(a!=b) { xz return; } // exit in the middle of the function
//--- here is some code of the function itself
   xz return;
  }
//+------------------------------------------------------------------+
//| Save data to file                                              |
//+------------------------------------------------------------------+
void WriteFile(string name="Tracing")
  {
//--- open the file
   ResetLastError();
   int han=FileOpen(name+".mqh",FILE_WRITE|FILE_TXT|FILE_ANSI," ");
//--- check if the file has opened
   if(han!=INVALID_HANDLE)
     {
      FileWrite(han,com); // print data
      FileClose(han);     // close the file
     }
   else
      Print("File open failed "+name+".mqh, error",GetLastError());
  }

Pour commencer le traçage à partir d'un endroit particulier, les macros doivent être complétées par des conditions :

bool trace=0; // variable for protecting tracing by condition
//--- opening substitution
#define zx if(trace) com+="if("+__FUNCSIG__+"){\n";
//--- closing substitution
#define xz if(trace) com+="};\n";

Dans ce cas, vous pourrez activer ou désactiver le traçage après avoir défini la valeur "true" ou "false" sur la variable "trace" après un certain événement ou à un certain endroit.

Si le traçage n'est pas déjà requis bien qu'il puisse devenir nécessaire plus tard ou qu'il n'y ait pas assez de temps pour effacer la source pour le moment, il peut être désactivé en modifiant les valeurs des macros en des valeurs vides :

//--- substitute empty values
#define zx
#define xz

Le fichier avec l'Expert Advisor standard contenant les modifications pour le traçage est joint ci-dessous. Les résultats du traçage sont visibles dans le répertoire Files après le lancement de l'Expert Advisor sur le graphique (le fichier tracing.mqh est créé). Voici le passage du texte du fichier résultant :

if(int OnInit()){
};
if(void OnTick()){
if(void CheckForOpen()){
};
};
if(void OnTick()){
if(void CheckForOpen()){
};
};
if(void OnTick()){
if(void CheckForOpen()){
};
};
//--- ...

Notez que la structure des appels imbriqués ne peut pas être clairement définie dans le fichier nouvellement créé initialement, mais vous pourrez voir toute sa structure après avoir utilisé le styler de code. Vous trouverez ci-dessous le texte du fichier résultant après utilisation du styler :

if(int OnInit())
  {
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
if(void OnTick())
  {
   if(void CheckForOpen())
     {
     };
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
if(void OnTick())
  {
   if(void CheckForOpen())
     {
     };
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
if(void OnTick())
  {
   if(void CheckForOpen())
     {
     };
  };
//--- ...

Ce n'est que mon astuce et non un exemple de la façon dont le traçage doit être effectué. Chacun est libre de faire le traçage à sa manière. L'essentiel est que le traçage révèle la structure des appels de fonction.


Remarque importante sur le débogage

Si vous implémentez des modifications dans votre code pendant le débogage, utilisez l'encapsulation des appels de fonctions MQL5 directes. Ci-dessous comment cela se fait :

//+------------------------------------------------------------------+
//| Example of wrapping a standard function in a shell function      |
//+------------------------------------------------------------------+
void DebugPrint(string text) { Print(text); }

Cela vous permettra d'effacer facilement le code une fois le débogage terminé :

  • supprimer l'appel de la fonction "DebugPrint",
  • puis compiler
  • et supprimez les appels de cette fonction dans les lignes où MetaEditor avertit des erreurs de compilation.

Il en va de même pour les variables utilisées dans le débogage. Par conséquent, essayez d'utiliser des variables et des fonctions déclarées globalement. Cela vous évitera de chercher des constructions perdues dans les profondeurs de votre application.


Conclusion

Le débogage est une partie importante du travail du programmeur. Une personne qui n'est pas capable d'effectuer le débogage de programme ne peut pas être appelée programmeur. Mais le débogage principal est toujours effectué dans votre tête. Cet article ne montre que certaines méthodes utilisées dans le débogage. Mais sans compréhension des principes de fonctionnement de l'application, ces méthodes ne seront d'aucune utilité.

Je vous souhaite un débogage réussi !

Traduit du russe par MetaQuotes Ltd.
Article original : https://www.mql5.com/ru/articles/654

Fichiers joints |
Indicateur pour le graphique en Points et Figures Indicateur pour le graphique en Points et Figures
hIl existe de nombreux types de graphiques qui fournissent des informations sur la situation actuelle du marché. Beaucoup d’entre eux, tels que le graphique en Points et Figures, sont l’héritage du passé lointain. L’article décrit un exemple de graphique en Points et Figures à l’aide d’un indicateur en temps réel.
MQL5 Cookbook : Réduction de l'effet du surajustement et traitement de l'absence de cotations MQL5 Cookbook : Réduction de l'effet du surajustement et traitement de l'absence de cotations
Quelle que soit la stratégie de trading que vous utilisez, il y aura toujours une question de quels paramètres choisir pour assurer des bénéfices futurs. Cet article donne un exemple d'Expert Advisor avec la possibilité d'optimiser plusieurs paramètres de symboles en même temps. Cette méthode est destinée à réduire l'effet du surajustement des paramètres et à traiter les situations où les données d'un seul symbole ne sont pas suffisantes pour l'étude.
Le réseau MQL5 Cloud Network : Calculez-vous toujours ? Le réseau MQL5 Cloud Network : Calculez-vous toujours ?
Cela fera bientôt un an et demi que le réseau MQL5 Cloud Network a été lancé. Cet événement de pointe a inauguré une nouvelle ère de trading algorithmique - désormais, en quelques clics, les traders peuvent disposer de centaines et de milliers de cœurs de calcul pour l'optimisation de leurs stratégies de trading.
MQL5 Cookbook : Écriture de l'historique des transactions dans un fichier et création des graphiques d’équilibre pour chaque symbole dans Excel MQL5 Cookbook : Écriture de l'historique des transactions dans un fichier et création des graphiques d’équilibre pour chaque symbole dans Excel
Lorsque je communiquais dans divers forums, j'utilisais souvent des exemples de mes résultats de test affichés sous forme de captures d'écran de graphiques Microsoft Excel. On m'a souvent demandé d'expliquer comment de tels graphiques peuvent être créés. Enfin, j'ai maintenant un peu de temps pour tout expliquer dans cet article.