English Русский 中文 Español Deutsch 日本語 Português 한국어 Italiano Türkçe
Indicateurs personnalisés dans MQL5 pour débutants

Indicateurs personnalisés dans MQL5 pour débutants

MetaTrader 5Exemples | 22 décembre 2021, 16:18
778 0
Nikolay Kositsin
Nikolay Kositsin

Introduction

La base d'une compréhension profonde de tout sujet intellectuel (peu importe, les mathématiques, la musique ou la programmation) est l'étude de ses fondamentaux. C'est génial quand une étude similaire est commencée à un âge assez jeune, donc la compréhension des principes fondamentaux est beaucoup plus facile et les connaissances sont spécifiques et complètes.

Malheureusement, la plupart des gens commencent à étudier les marchés financiers et boursiers à un âge moyen, donc l'étude n'est pas facile. Dans cet article, je vais essayer d'aider à surmonter cette barrière initiale dans la compréhension de MQL5 et dans l'écriture d'indicateurs personnalisés pour le terminal client MetaTrader 5.

Indicateur SMA comme exemple simple

Le moyen le plus efficace et le plus rationnel d'étudier quelque chose est de résoudre des problèmes pratiques. Comme nous envisageons des indicateurs personnalisés, nous commencerons par l'étude de l'indicateur simple qui contient un code qui illustre les principes fondamentaux du fonctionnement de l'indicateur en MQL5.

À titre d'exemple, considérons l'indicateur le plus célèbre de l'analyse technique - la moyenne mobile simple (SMA). Son calcul est simple :

SMA = SUM (CLOSE (i), MAPeriod) / MAPeriod

Où :

  • SUM — somme des valeurs ;
  • CLOSE (i) — cours de clôture de la barre i ;
  • MAPeriod — nombre de barres par rapport à la moyenne (période moyenne).

Voici un code de cet indicateur exempt de tout excès :

//+------------------------------------------------------------------+
//|                                                          SMA.mq5 |
//|                        Copyright 2009, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_plots   1
#property indicator_type1   DRAW_LINE
#property indicator_color1  Red
  
input int MAPeriod = 13;
input int MAShift = 0; 
  
double ExtLineBuffer[]; 
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+  
void OnInit()
  {
   SetIndexBuffer(0, ExtLineBuffer, INDICATOR_DATA);
   PlotIndexSetInteger(0, PLOT_SHIFT, MAShift);
   PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, MAPeriod - 1);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
  {
   if (rates_total < MAPeriod - 1)
    return(0);
    
   int first, bar, iii;
   double Sum, SMA;
   
   if (prev_calculated == 0)
    first = MAPeriod - 1 + begin;
   else first = prev_calculated - 1;

   for(bar = first; bar < rates_total; bar++)
    {
     Sum = 0.0;
     for(iii = 0; iii < MAPeriod; iii++)
      Sum += price[bar - iii];
     
     SMA = Sum / MAPeriod;
      
     ExtLineBuffer[bar] = SMA;
    }
     
   return(rates_total);
  }
//+------------------------------------------------------------------+

Et voici un résultat de son travail dans le terminal client MetaTrader 5 :

Nous devons d'abord considérer deux choses - le but de chaque chaîne de code d'une part, et l'interaction entre ce code de programme et le terminal client d'autre part.

Utilisation des commentaires

Au premier coup d'œil au code indicateur, l'œil capte des objets qui suivent :

//+------------------------------------------------------------------+
//|                                                          SMA.mq5 |
//|                        Copyright 2009, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+  
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+

Il est nécessaire de noter qu'ils ne sont pas directement liés au code, ce ne sont que des commentaires, ils sont conçus pour la lisibilité du code et montrent un certain contenu sémantique de certaines parties de ce code. Bien sûr, ils pourraient être supprimés du code sans aucun dommage pour sa simplification ultérieure, mais le code perdrait alors sa brièveté laconique dans la compréhension. Dans notre cas, nous abordons le cas de commentaires d’une seule les commentaires, qui commencent toujours par une paire de caractères "//" et se terminent par une nouvelle ligne de caractère.

Il est clair que dans les commentaires, l'auteur peut écrire tout ce qui est nécessaire pour comprendre ce code après un certain temps. Dans notre cas, dans la première partie des chaînes commentées, il y a un nom de l'indicateur et des informations sur son auteur, les deuxième et troisième parties des commentaires divisent les fonctions OnInit() et OnCalculate(). La dernière ligne à la fin ferme simplement le code du programme.

Structure du code SMA

Ainsi, comme nous le voyons, l'ensemble du code de notre indicateur peut être divisé en 3 parties :

1. Le code, qui s'écrit sans parenthèses au universellement, se situe entre les deux premiers commentaires.
2. Description de la fonction OnInit().

3. Description de la fonction OnCalculate().

Il faut noter qu'en programmation le sens de fonction est beaucoup plus large qu'en mathématiques. Par exemple, dans les langages de programmation, les fonctions mathématiques reçoivent toujours des paramètres d'entrée et renvoient des valeurs calculées. En outre, les fonctions de MQL5 peuvent également effectuer certaines opérations de graphiques, d'opérations de trading, de fichiers, etc.

En fait, tout indicateur écrit en MQL5 a toujours un ensemble minimal de parties écrites par l'utilisateur, dont le contenu est individuel et dépend des caractéristiques d'un indicateur créé.

Outre ces composants, l'ensemble minimal de fonctions peut contenir la description d'une autre fonction MQL5 - OnDeInit() :

//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+  
void OnDeinit(const int reason)
  {

  }

Dans notre cas, ce n'est pas nécessaire, donc c'est absent ici.

Interaction entre SMA et le terminal client MetaTrader

Considérons maintenant le travail du fichier compilé SMA.ex5 que nous avons obtenu après avoir appuyé sur la touche « Compiler » dans MetaEditor avec SMA.mq5 ouvert. Il est nécessaire de noter que les fichiers texte avec l'extension .mq5 ne sont qu'un code source au format texte, qui doit d'abord être compilé pour être utilisé dans le terminal client.

Après avoir attaché cet indicateur à un graphique depuis la fenêtre du Navigateur, le MetaTrader exécutera le code de la première partie de l'indicateur. Après cela, il appellera la fonction OnInit() pour une seule exécution de cette fonction et de plus, à chaque nouveau trait (après l'arrivée de la nouvelle cotation) il appellera la fonction OnCalculate() et exécutera par conséquent le code de cette fonction.  SI OnDeInit() était présent dans l'indicateur, MetaTrader appellerait cette fonction une fois après avoir détaché l'indicateur du graphique ou après le changement du délai.

La signification et le but de toutes les parties de l'indicateur sont clairs après cette explication. Dans la première partie du code au niveau universel, il y a quelques opérateurs simples qui sont exécutés une fois après le début de l'indicateur. De plus, il y a une déclaration de variables, qui sont « visibles » dans tous les blocs de l'indicateur et qui mémorisent leurs valeurs pendant que l'indicateur est sur le graphique.

Les constantes et fonctions qui sont exécutées une fois doivent être situées à l'intérieur de la fonction OnInit(), car il serait inopportun de les placer dans un bloc de la fonction OnCalculate(). Le code de calcul de l'indicateur, qui permet de calculer ses valeurs pour chaque barre, doit être placé dans la fonction OnCalculate().

Les procédures qui suppriment la mémoire inutile (s'il y en a) du graphique après que l'indicateur en ait été retiré, doivent être placées à l'intérieur de OnDeInit(). Par exemple, il est nécessaire pour la suppression des objets graphiques, créés par l'indicateur.

Après ces explications, nous sommes prêts à examiner en détail le code de l'indicateur, qui a été examiné ci-dessus.

Code de programme de l'indicateur SMA

Le premier groupe de lignes de code commence par l'opérateur #property, qui permet de spécifier des paramètres supplémentaires de paramètres d'indicateur. La liste complète des propriétés de programme possibles se trouve dans la documentation de MQL5. Le cas échéant, il est possible d'écrire des propriétés supplémentaires de l'indicateur. Dans notre cas nous avons 5 lignes de code, le but de chaque ligne est décrit en commentaires :

//---- the indicator will be plotted in the main window
#property indicator_chart_window
//---- one buffer will be used for the calculations and plot of the indicator
#property indicator_buffers 1
//---- only one graphic plot is used 
#property indicator_plots   1
//---- the indicator should be plotted as a line
#property indicator_type1   DRAW_LINE
//---- the color of the indicator's line is red 
#property indicator_color1  Red 

Notez qu'il n'y a pas de point-virgule (";") à la fin des lignes. La raison est la suivante : en fait, dans notre cas c'est la définition de constantes, mais présentée d'une autre manière.

Notre moyenne mobile simple n'a que 2 paramètres, qui peuvent être modifiés par un utilisateur - c'est une période de moyenne et un décalage horizontal (en barres) de l'indicateur le long des axes temporels. Ces deux paramètres doivent être déclarés comme variables d'entrée de l'indicateur, comme il a été déclaré dans deux autres lignes de code :

//---- indicator input parameters
input int MAPeriod = 13; //averaging period
nput int MAShift = 0; //horizontal shift (in bars)

Notez qu'après la déclaration de ces paramètres d'entrée il y a des commentaires, et ces commentaires seront visibles comme noms de paramètres d'entrée dans la fenêtre « Propriétés » de l'indicateur :


Dans notre cas, ces noms sont beaucoup plus clairs que les noms de variables de l'indicateur. Donc, ces commentaires devraient être simples.

Et la dernière ligne de code qui n'a pas de crochets est la déclaration du tableau dynamique ExtLineBuffer[].

//---- the declaration of the dynamic array
//that will be used further as an indicator's buffer
double ExtLineBuffer[];  

Elle a été déclarée comme variable globale pour plusieurs raisons.

Tout d'abord, ce tableau doit être converti dans le tampon indicateur et il est implémenté dans le bloc de la fonction OnInit(). Deuxièmement, le tampon indicateur lui-même sera utilisé dans la fonction OnCalculate(). Troisièmement, ce tableau stockera les valeurs de l'indicateur, qui seront tracées sous forme de courbe sur le graphique. Du fait qu'il a été déclaré comme variable globale, il est disponible pour tous les blocs de l'indicateur, et il stocke ses valeurs tout le temps jusqu'à ce que l'indicateur soit détaché du graphique.

Le contenu de la fonction OnInit() est présenté par seulement 3 opérateurs, ce sont des fonctions intégrées du terminal client MetaTrader.

L'call de la première fonction assigne au tampon indicateur zéro un tableau dynamique unidimensionnel ExtLineBuffer[]. Deux appels d'une autre fonction avec des valeurs différentes des paramètres d'entrée permettent de déplacer l'indicateur le long de l'axe des prix et permet de spécifier son tracé à partir de la barre avec le numéro MAPeriod.

void OnInit()
  {
//----+
//---- assign the dynamic array ExtLineBuffer with 0th indicator's buffer
   SetIndexBuffer(0,ExtLineBuffer,INDICATOR_DATA);
//---- set plot shift along the horizontal axis by MAShift bars
   PlotIndexSetInteger(0,PLOT_SHIFT,MAShift);
//---- set plot begin from the bar with number MAPeriod
   PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,MAPeriod);
//----+
  }

Le dernier appel de la fonction PlotIndexSetInteger() passe la valeur égale à MAPeriod (via le paramètre begin de la fonction OnCalculate()) à un autre indicateur, s'il est appliqué aux valeurs de notre indicateur. La logique est simple, il n'y a rien à moyenner dans les premières barres de MaPeriod-1, c'est pourquoi le tracé de cet indicateur est inutile. Cependant, cette valeur doit être passée pour décaler l'origine des calculs d'un autre indicateur.

Il ne s'agit pas d'une liste complète de fonctions intégrées, qui sont utilisées dans les indicateurs personnalisés et peuvent être situées dans ce bloc de l'indicateur. Voir la documentation MQL5 pour les détails.

Enfin, considérons le code de la fonction OnCalculate(). Il n'y a pas d'appels personnalisés dans cette fonction comme la fonction OnInit() car ces fonctions sont appelées par le terminal client MetaTrader. Pour cette raison, les paramètres d'entrée de la fonction sont déclarés comme des constantes.

int OnCalculate(
                const int rates_total,    // number of available bars in history at the current tick
                const int prev_calculated,// number of bars, calculated at previous tick
                const int begin,          // index of the first bar
                const double &price[]     // price array for the calculation
                )

Ces paramètres d'entrée ne sont pas modifiables, leurs valeurs sont passées par le terminal client pour une utilisation ultérieure dans le code de cette fonction. Les variables d'entrée de OnCalculate sont décrites dans la documentation de MQL5. La fonction OnCalculate() renvoie ses valeurs pour le terminal client à l'aide de la fonction return(rates_total). Le terminal client reçoit cette valeur du trait courant après l'exécution de OnCalculate() et passe la valeur renvoyée à un autre paramètre prev_calculated. Ainsi, il est toujours possible de déterminer la plage d'indice à barres et d'effectuer les calculs en une seule fois uniquement pour les nouvelles valeurs de l'indicateur, apparues après le trait précédent.

Il est nécessaire de noter que l’agencement des barres dans le terminal client MetaTrader est effectuée de gauche à droite, donc la très ancienne barre (la gauche), présentée au graphique a l'indice 0, la suivante a l'indice 1, etc. Les éléments du tampon ExtLineBuffer[] ont le même ordre.

La structure de code simple à l'intérieur de la fonction OnCalculate de notre indicateur est universelle et typique pour de nombreux indicateurs d'analyse technique. Alors, considérons-le en détail. La logique de la fonction OnCalcualte() est la suivante :

1. Vérifier la présence de barres, nécessaires aux calculs.
2. Déclaration des variables locales.
3. Obtenir l'indice de la barre initiale pour le calcul.
4. La boucle principale du calcul de l'indicateur
5. Retourner la valeur de rates_total au terminal client en utilisant l'opérateur return().

Je pense que le premier terme est clair. Par exemple, si la période de moyenne de la moyenne mobile est égale à 200, mais que le terminal client n'a que 100 barres, il n'est pas nécessaire d'effectuer le calcul car il n'y a aucune barre suffisante pour le calcul. Nous devons donc retourner 0 au terminal client à l'aide de l'opérateur retour.

//---- check for the presence of bars, sufficient for the calculation
   if(rates_total<MAPeriod-1+begin)
      return(0);

Notre indicateur peut être appliqué aux données d'un autre indicateur, qui peut également avoir un nombre minimal de barres pour le calcul.  L'utilisation de la constante begin est nécessaire pour prendre en compte ce fait. Voir l'article Application d’un indicateur à un autre pour plus de détails.

Les variables locales, déclarées dans ce bloc ne sont nécessaires que pour les calculs intermédiaires à l'intérieur de la fonction OnCalculate(). Ces variables sont libérées de la RAM de l'ordinateur après l'appel de la fonction.

//---- declaration of local variables 
   int first,bar,iii;
   double Sum,SMA;

Il faut faire attention à l'indice initial de la principale boucle (variable première). Il nous faut, au premier appel de la fonction, (nous pouvons la déterminer par la valeur du paramètre prev_calculated) effectuer le calcul des valeurs des indicateurs pour toutes les barres. Pour tous les autres traits du terminal client, nous devons effectuer le calcul uniquement pour les nouvelles barres apparues. Cela se fait par 3 lignes de code :

//---- calculation of starting index first of the main loop
   if(prev_calculated==0) // check for the first start of the indicator
      first=MAPeriod-1+begin; // start index for all the bars
   else first=prev_calculated-1; // start index for the new bars

La plage des changements de variable dans l'opérateur de boucle principale du recalcul de l'indicateur a déjà été discutée.

//---- main loop of the calculation
   for(bar=first;bar<rates_total;bar++)

Le traitement des mesures dans la boucle principale s'effectue par ordre croissant (bar++), c'est-à-dire de gauche à droite, de manière naturelle et radicale. Dans notre indicateur, il pourrait être mis en œuvre d'une autre manière (dans l'ordre inverse). Il est préférable d'utiliser l'ordre croissant dans les indicateurs. La variable de la boucle principale est nommée « barre », mais de nombreux programmeurs préfèrent utiliser l’appellation « i ». Je préfère utiliser le terme « barre », car cela rend le code plus clair et lisible.

L'algorithme de moyenne, qui a été implémenté dans la boucle principale, est simple.

     {
      Sum=0.0;
       //---- summation loop for the current bar averaging
      for(iii=0;iii<MAPeriod;iii++)
         Sum+=price[bar-iii]; // Sum = Sum + price[bar - iii]; // eqaual to 
      
      //---- calculate averaged value
      SMA=Sum/MAPeriod;

      //---- set the element of the indicator buffer with the value of SMA we have calculated
      ExtLineBuffer[bar]=SMA;
     }

Dans la deuxième boucle, nous effectuons la sommation cumulative des prix des barres précédentes de la période et la divisons par cette période de moyenne. En conséquence, nous avons la valeur finale de SMA.

Une fois la boucle principale terminée, la fonction OnCalculate renvoie le nombre de barres disponibles à partir de la variable rates_total. Au prochain appel de la fonction OnCalculate(), cette valeur sera passée par le terminal client à la variable prev_calculated. Cette valeur, diminuée de 1, servira d'indice initial pour la boucle principale.

Voici le code source complet de l'indicateur avec des commentaires détaillés pour chaque ligne de code :

//+------------------------------------------------------------------+
//|                                                          SMA.mq5 |
//|                        Copyright 2009, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
//---- the indicator will be plotted in the main window
#property indicator_chart_window
//---- one buffer will be used for the calculations and plot of the indicator
#property indicator_buffers 1
//---- only one graphic plot is used 
#property indicator_plots   1
//---- the indicator should be plotted as a line
#property indicator_type1   DRAW_LINE
//---- the color of the indicator's line is red 
#property indicator_color1  Red 

//---- indicator input parameters
input int MAPeriod = 13; //Averaging period
input int MAShift = 0; //Horizontal shift (in bars)

//---- the declaration of the dynamic array
//that will be used further as an indicator's buffer
double ExtLineBuffer[]; 
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+  
void OnInit()
  {
//----+
//---- assign the dynamic array ExtLineBuffer with 0th indicator's buffer
   SetIndexBuffer(0,ExtLineBuffer,INDICATOR_DATA);
//---- set plot shift along the horizontal axis by MAShift bars
   PlotIndexSetInteger(0,PLOT_SHIFT,MAShift);
//---- set plot begin from the bar with number MAPeriod
   PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,MAPeriod);  
//----+
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(
                const int rates_total,    // number of available bars in history at the current tick
                const int prev_calculated,// number of bars, calculated at previous tick
                const int begin,          // index of the first bar
                const double &price[]     // price array for the calculation
                )
  {
//----+   
   //---- check for the presence of bars, sufficient for the calculation
   if (rates_total < MAPeriod - 1 + begin)
    return(0);
   
   //---- declaration of local variables 
   int first, bar, iii;
   double Sum, SMA;
   
   //---- calculation of starting index first of the main loop
   if(prev_calculated==0) // check for the first start of the indicator
      first=MAPeriod-1+begin; // start index for all the bars
   else first=prev_calculated-1; // start index for the new bars

   //---- main loop of the calculation
   for(bar = first; bar < rates_total; bar++)
    {    
      Sum=0.0;
      //---- summation loop for the current bar averaging
      for(iii=0;iii<MAPeriod;iii++)
         Sum+=price[bar-iii]; // It's equal to: Sum = Sum + price[bar - iii];
         
      //---- calculate averaged value
      SMA=Sum/MAPeriod;

      //---- set the element of the indicator buffer with the value of SMA we have calculated
      ExtLineBuffer[bar]=SMA;
    }
//----+     
   return(rates_total);
  }
//+------------------------------------------------------------------+

Cette forme de code est beaucoup plus facile à comprendre et à lire.

Je voudrais décrire une autre fonctionnalité qui peut être utilisée pour simplifier la compréhension du code. Vous pouvez utiliser des espaces et des lignes vides pour le rendre clair.

Conclusion

Il s'agit de l'interaction entre le code de l'indicateur personnalisé et le terminal client MetaTrader. Bien sûr, le sujet est bien plus large que ce que nous avions envisagé, le but de cet article est d'aider les débutants à comprendre les fondamentaux. Veuillez donc consulter la documentation pour les détails.

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

Fichiers joints |
sma.mq5 (1.78 KB)
sma_.mq5 (3.32 KB)
Traitement des événements de trade dans Expert Advisor à l'aide de la fonction OnTrade() Traitement des événements de trade dans Expert Advisor à l'aide de la fonction OnTrade()
MQL5 a apporté une multitude d'innovations, notamment le travail avec des événements de différents types (événements de minuterie, événements de trade, événements personnalisés, etc.). La capacité de gérer les événements vous permet de créer un tout nouveau type de programmes pour le trading automatique et semi-automatique. Dans cet article, nous considérerons les événements commerciaux et écrirons du code pour la fonction OnTrade(), qui traitera l'événement de trade.
Utilisation des pointeurs d'objet dans MQL5 Utilisation des pointeurs d'objet dans MQL5
Par défaut, tous les objets de MQL5 sont transmis par référence, mais il est possible d'utiliser les pointeurs d'objet. Cependant, il est nécessaire d'effectuer la vérification du pointeur, car l'objet peut ne pas être initialisé. Dans ce cas, le programme MQL5 sera terminé avec une erreur critique et déchargé. Les objets, créés automatiquement, ne provoquent pas une telle erreur, donc dans ce sens, ils sont tout à fait sûrs. Dans cet article, nous allons essayer de comprendre la différence entre la référence d'objet et le pointeur d'objet, et d'examiner comment écrire du code sécurisé qui utilise les pointeurs.
Comment faire appel aux indicateurs dans MQL5 Comment faire appel aux indicateurs dans MQL5
Avec la nouvelle version du langage de programmation MQL disponible, non seulement l'approche du traitement des indicateurs a changé, mais il existe également de nouvelles façons de créer des indicateurs. De plus, vous disposez d'une flexibilité supplémentaire en travaillant avec les tampons d'indicateurs - vous pouvez désormais spécifier la direction d'indexation souhaitée et obtenir exactement autant de valeurs d'indicateurs que vous le souhaitez. Cet article explique les méthodes de base pour faire appel aux indicateurs et récupérer des données à partir des tampons de l'indicateur.
Présentation de MQL5 : Comment écrire un simple Expert Advisor et un indicateur personnalisé Présentation de MQL5 : Comment écrire un simple Expert Advisor et un indicateur personnalisé
Le langage de programmation MetaQuotes 5 (MQL5), inclus dans le terminal client MetaTrader 5, offre de nombreuses nouvelles possibilités et des performances supérieures par rapport au MQL4. Cet article vous aidera à vous familiariser avec ce nouveau langage de programmation. Les exemples simples de rédaction d'un Expert Advisor et d'un Indicateur personnalisé sont présentés dans cet article. Nous considérerons également quelques détails du langage MQL5, qui sont nécessaires pour comprendre ces exemples.