English Русский 中文 Español Deutsch 日本語 Português 한국어 Italiano Türkçe
Appliquer un indicateur à un autre

Appliquer un indicateur à un autre

MetaTrader 5Exemples | 22 décembre 2021, 16:12
234 0
MetaQuotes
MetaQuotes

Introduction

Considérons une tâche d'amélioration d'un indicateur, qui est appliquée aux valeurs d'un autre indicateur. Dans cet article, nous continuerons à travailler avec True Strength Index (TSI), qui a été créé et pris en compte dans l'article précédent « MQL5 : Créez votre propre indicateur ».

Indicateur personnalisé basé sur les valeurs d'autres indicateurs

Lors de l'écriture d'un indicateur qui utilise la forme abrégée de l'appel de fonction OnCalculate(), vous pourriez manquer le fait qu'un indicateur peut être calculé non seulement par les données de prix, mais également par les données d'un autre indicateur (qu'il s'agisse du type intégré ou personnalisé).

Faisons une expérience simple : attachez l'indicateur RSI intégré avec des paramètres standard à un graphique et faites glisser l'indicateur personnalisé True_Strength_Index_ver2.mq5 vers la fenêtre de l'indicateur RSI. Dans l'onglet Paramètres de la fenêtre qui apparaît, spécifiez que l'indicateur doit être appliqué aux données de l'indicateur précédent (RSI (14)).

Le résultat sera assez différent de ce à quoi nous nous attendions. La ligne supplémentaire de l'indicateur TSI n'a pas paru dans la fenêtre de l'indicateur RSI, et dans la fenêtre de données, vous constaterez que ses valeurs ne sont pas également claires.

Bien que les valeurs de RSI soient définies presque partout, tout l'historique, les valeurs de TSI (appliquées aux données RSI) sont soit totalement absentes (au début) soit toujours égales à -100 :

Un tel comportement est dû au fait que la valeur du paramètre begin n'est utilisée nulle part dans OnCalculate() de notre True_Strength_Index_ver2.mq5. Le paramètre begin spécifie le nombre de valeurs vides dans le paramètre d'entrée price[]. Ces valeurs vides ne peuvent pas être utilisées dans le calcul des valeurs des indicateurs. Rappelons la définition de la première forme de l'appel de fonction OnCalculate().
int OnCalculate (const int rates_total,      // price[] array length
                 const int prev_calculated,  // number of bars calculated after previous call
                 const int begin,            // start index of meaningful data
                 const double& price[]       // array for calculation
   );

Lors de l'application de l'indicateur aux données de prix en spécifiant l'une des constances de prix, le paramètre begin est égal à 0, car il existe un type de prix spécifié pour chaque barre. Par conséquent, le tableau d'entrée price[] a toujours des données correctes à partir de son premier élément price[0]. Mais si nous spécifions les données d'un autre indicateur comme source de calcul, ce n'est plus garanti.

Le paramètre begin de OnCalculate()

Vérifions les valeurs contenues dans le tableau price[], si le calcul est effectué à partir des données d'un autre indicateur. Pour ce faire, dans la fonction OnCalculate(), nous ajouterons du code qui affichera les valeurs que nous voulons vérifier. Maintenant, le début de la fonction OnCalculate() ressemble à ce qui suit :

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate (const int rates_total,    // price[] array length;
                 const int prev_calculated,// number of available bars after previous call;
                 const int begin,          // start index of meaningful data in price[] array 
                 const double &price[])    // data array, that will be used for calculations;
  {
//--- flag for single output of price[] values
   static bool printed=false;
//--- if begin isn't zero, then there are some values that we shouldn't take into account

   if(begin>0 && !printed)
     {
      //--- let's output them
      Print("Data for calculation begin from index equal to ",begin,
            "   price[] array length =",rates_total);

      //--- let's show the values that we shouldn't take into account for calculation
      for(int i=0;i<=begin;i++)
        {
         Print("i =",i,"  value =",price[i]);
        }
      //--- set printed flag to confirm that we have already logged the values
      printed=true;
     }

Encore une fois, glissons-déposons la version modifiée de notre indicateur dans la fenêtre RSI(14) et spécifions les données de l'indicateur précédent pour le calcul. Nous allons maintenant voir les valeurs qui ne sont pas tracées et ne doivent pas être prises en compte pour les calculs, où les valeurs de l'indicateur RSI(14) sont utilisées.


Valeurs vides dans les tampons d'indicateur et DBL_MAX

Les 14 premiers éléments du tableau price[] avec des indices de 0 à 13 inclus ont la même valeur égale à 1.797693134862316e+308. Vous rencontrerez très souvent ce nombre, car c'est la valeur numérique de la constante EMPTY_VALUE intégrée, qui est utilisée pour indiquer les valeurs vides dans un tampon d'indicateur.

Remplir des valeurs vides avec des zéros n'est pas une solution universelle, car cette valeur peut être le résultat d'un calcul par d'autres indicateurs. Pour cette raison, tous les indicateurs intégrés du terminal client renvoient ce nombre pour les valeurs vides. La valeur 1.797693134862316e+308 a été choisie car il s'agit de la valeur maximale possible de type double, et pour plus de commodité, elle est présentée comme constante DBL_MAX dans MQL5.

Pour vérifier si un certain numéro de type double est vide ou non, vous pouvez le comparer avec les constantes EMPTY_VALUE ou DBL_MAX . Les deux variantes sont égales, mais il est préférable d'utiliser la constante EMPTY_VALUE dans votre code pour que ce soit clair.

//+------------------------------------------------------------------+
//| returns true for "empty" values                               |
//+------------------------------------------------------------------+
bool isEmptyValue(double value_to_check)
  {
//--- if the value is equal DBL_MAX, it has an empty value
   if(value_to_check==EMPTY_VALUE) return(true);
//--- it isn't equal DBL_MAX
   return(false);
  }

Le DBL_MAX est un nombre très important et l'indicateur RSI ne peut pas par nature renvoyer de telles valeurs ! Et seul le quinzième élément du tableau (avec l'indice 14) a une valeur raisonnable égale à 50. Ainsi, même si nous ne savons rien de l'indicateur en tant que source de données à calculer, en utilisant le paramètre begin, nous pouvons organiser correctement le traitement des données dans ces cas. Pour être plus précis, nous devons éviter d'utiliser ces valeurs vides dans nos calculs.

Relation entre le paramètre begin et la propriété PLOT_DRAW_BEGIN

Il convient de noter qu'il existe un rapport étroit entre le paramètre begin, qui est transféré à la fonction OnCalculate(), et la propriété PLOT_DRAW_BEGIN, qui définit le nombre de barres initiales sans dessin. Si nous examinons le code source du RSI du paquet standard MetaTrader5, nous verrons le code suivant dans la fonction OnInit() :
//--- sets first bar from what index will be drawn
   PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,ExtPeriodRSI);

Cela signifie que le tracé graphique avec l'indice 0 commence uniquement à partir de la barre dont l'indice est égal à ExtPeriodRSI (c'est une variable d'entrée qui spécifie la période de l'indicateur RSI), et qu'il n'y a pas de tracé des barres antérieures.

En langage MQL5 le calcul d’un certain indicateur A basé sur les données de l'indicateur B est toujours exécuté sur des valeurs tampon nulles de l'indicateur B. Les valeurs tampon nulles de l'indicateur B sont transférées en tant que paramètre d'entrée price[] à la fonction OnCalculate() de l'indicateur A. Intrinsèquement, le tampon zéro est assigné au tracé graphique zéro avec la fonction SetIndexBuffer(). Par conséquent :

Règle de transfert de la propriété PLOT_DRAW_BEGIN vers le paramètre begin: Pour les calculs de l'indicateur personnalisé A basé sur les données de l'autre indicateur (de base) B, la valeur du paramètre d'entrée begin dans la fonction OnCalculate() est toujours égale à la valeur de la propriété PLOT_DRAW_BEGIN du tracé graphique zéro B de l'indicateur de base.

Donc, si nous avons créé l'indicateur RSI (indicateur B) avec la période 14, puis créé notre indicateur personnalisé True Strength Index (indicateur A) en fonction de ses données, alors :

  • L'indicateur RSI (14) est tracé à partir de la 14ème barre à cause de PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,14) ;
  • Le tableau d'entrée price[] dans la fonction OnCalculate() contient les valeurs du tampon zéro de l'indicateur RSI ;
  • La valeur du paramètre d'entrée begin dans la fonction OnCalculate() de l'indicateur TSI est obtenue à partir de la propriété PLOT_DRAW_BEGIN du tracé graphique zéro de l'indicateur RSI.

N'oubliez pas que l'indicateur TSI n'est pas extrait à partir du début du graphique, car la valeur de l'indicateur n'est pas déterminée pour certaines premières barres. Le premier indice de barre, qui sera tracé sous forme de ligne dans l'indicateur TSI, est égal à r+s-1, où :

  • r - période de premier lissage exponentiel des tableaux MTMBuffer[] et AbsMTMBuffer[] dans les tableaux EMA_MTMBuffer[] et EMA_AbsMTMBuffer[] correspondants ;
  • s - période de lissage ultérieur des tableaux EMA_MTMBuffer[] et EMA_AbsMTMBuffer[].

Pour les barres avec des indices inférieurs à r+s-1, il n'y a pas de valeurs pour tracer l'indicateur TSI. Par conséquent, pour les tableaux finaux MA2_MTMBuffer[] et EMA2_AbsMTMBuffer[] utilisés pour calculer l'indicateur TSI, les données ont un décalage supplémentaire et commencent à partir de l'indice r+s-1. Vous pouvez trouver plus d'informations dans « MQL5 : Créez votre propre indicateur » article.

Il existe une instruction dans la fonction OnInit() pour désactiver le dessin des premières barres r+s-1 :

//--- first bar to draw
   PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,r+s-1);

Comme le début des données d'entrée a été déplacé vers l'avant par les barres begin, nous devons en tenir compte et fortifier la position de départ du tracé des données par les barres begin dans la fonction OnCalculate() :

   if(prev_calculated==0)
     { 
      //--- let's increase beginning position of data by begin bars,
      //--- because we use other indicator's data for calculation
      if(begin>0)PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,begin+r+s-1);
     }
Nous prenons maintenant en considération le paramètre begin pour calculer les valeurs de l'indicateur TSI. De plus, le paramètre begin sera correctement transféré si un autre indicateur utilise les valeurs de TSI pour le calcul : beginother_indicator=beginour_indicator+r+s-1. Par conséquent, nous pouvons formuler la règle consistant à imposer un indicateur aux valeurs d'un autre indicateur :

Règle d'imposition des indicateurs : Si un indicateur personnalisé A est tracé à partir de la position Na (les premières valeurs Na ne sont pas tracées) et est basé sur les données d'un autre indicateur B, qui est tracé de la position Nb, l'indicateur qui en résulte A{B} sera tracé à partir de la position Nab=Na+Nb, où A{B} signifie que l'indicateur A est calculé sur des valeurs tampon nulles de l'indicateur B.

Par conséquent, TSI (25,13) {RSI (14)} signifie que l'indicateur TSI (25,13) est constitué des valeurs de l'indicateur RSI (14). À la suite de l'imposition, le début des données est désormais (25+13-1)+14=51. En d'autres termes, le tracé de l'indicateur commencera à partir de la 52ème barre (l'indexation des barres commence par 0).

Ajout de valeurs begin à utiliser dans les calculs d'indicateurs

Maintenant, nous savons exactement que les valeurs significatives du tableau price[] commencent toujours de la position spécifiée par le paramètre begin. Modifions notre code étape par étape. Tout commence d'abord avec le code qui calcule les valeurs des tableaux MTMBuffer[] et AbsMTMBuffer[]. Sans begin, le remplissage du tableau de paramètres a commencé avec l'index 1.

//--- calculate values for mtm and |mtm|
   int start;
   if(prev_calculated==0) start=1;  // start filling MTMBuffer[] and AbsMTMBuffer[] arrays from 1st index 
   else start=prev_calculated-1;    // set start equal to last array index
   for(int i=start;i<rates_total;i++)
     {
      MTMBuffer[i]=price[i]-price[i-1];
      AbsMTMBuffer[i]=fabs(MTMBuffer[i]);
     }

Maintenant, nous allons commencer à partir de la position (begin+1), et le code modifié ressemble à ce qui suit (les changements de code sont indiqués en gras) :

//--- calculate values for mtm and |mtm|
   int start;
   if(prev_calculated==0) start=begin+1;  // start filling MTMBuffer[] and AbsMTMBuffer[] arrays from begin+1 index 
   else start=prev_calculated-1;           // set start equal to the last array index
   for(int i=start;i<rates_total;i++)
     {
      MTMBuffer[i]=price[i]-price[i-1];
      AbsMTMBuffer[i]=fabs(MTMBuffer[i]);
     }

Comme les valeurs de price[0] à price[begin-1] ne peuvent pas être utilisées pour les calculs, nous commençons par price[begin]. Les premières valeurs calculées pour les tableaux MTMBuffer[] et AbsMTMBuffer[] ressembleront à ce qui suit :

      MTMBuffer[begin+1]=price[begin+1]-price[begin];
      AbsMTMBuffer[begin+1]=fabs(MTMBuffer[begin+1]);

Pour cette raison, la variable start dans le cycle for a maintenant la valeur initiale start=begin+1, au lieu de 1.

Compte begin pour les tableaux dépendants

Suivi du lissage exponentiel des tableaux MTMBuffer[] et AbsMTMBuffer[]. La règle est également simple : si la position initiale du tableau de base a augmenté via les barres begin, alors la position initiale de tous les tableaux dépendants doit également être augmentée de barres begin.

//--- calculating the first moving average on arrays
   ExponentialMAOnBuffer(rates_total,prev_calculated,
                         1,               // index of the starting element in array 
                         r,               // period of exponential average
                         MTMBuffer,       // source buffer for average
                         EMA_MTMBuffer);  // target buffer
   ExponentialMAOnBuffer(rates_total,prev_calculated,
                         1,r,AbsMTMBuffer,EMA_AbsMTMBuffer);

Ici, MTMBuffer [] et AbsMTMBuffer [] sont des tableaux de base, et les valeurs calculées dans ces tableaux commencent désormais à partir d'un indice supérieur via begin. Donc, nous allons simplement ajouter ce décalage à la fonction ExponentialMAOnBuffer().

Maintenant, ce bloc ressemble à ceci :

//--- calculating the first moving average on arrays
   ExponentialMAOnBuffer(rates_total,prev_calculated,
                         begin+1,        // index of the starting element in array 
                         r,               // period for exponential average
                         MTMBuffer,       // source buffer for average
                         EMA_MTMBuffer);  // target buffer
   ExponentialMAOnBuffer(rates_total,prev_calculated,
                         begin+1,r,AbsMTMBuffer,EMA_AbsMTMBuffer);

Comme vous pouvez le voir, toute la modification consistait à tenir compte de l'augmentation de la position de données de départ, définie via le paramètre begin. Rien de compliqué. De la même manière, nous allons modifier le deuxième bloc de lissage.

Avant :

//--- calculating the second moving average on arrays
   ExponentialMAOnBuffer(rates_total,prev_calculated,
                         r,s,EMA_MTMBuffer,EMA2_MTMBuffer);
   ExponentialMAOnBuffer(rates_total,prev_calculated,
                         r,s,EMA_AbsMTMBuffer,EMA2_AbsMTMBuffer);

Après :

//--- calculating the second moving average on arrays
   ExponentialMAOnBuffer(rates_total,prev_calculated,
                         begin+r,s,EMA_MTMBuffer,EMA2_MTMBuffer);
   ExponentialMAOnBuffer(rates_total,prev_calculated,
                         begin+r,s,EMA_AbsMTMBuffer,EMA2_AbsMTMBuffer);

De la même manière, nous allons modifier le dernier bloc de calcul.

Avant :

//--- calculating values of our indicator
   if(prev_calculated==0) start=r+s-1; // set initial index for input arrays
   else start=prev_calculated-1;       // set start equal to last array index
   for(int i=start;i<rates_total;i++)
     {
      TSIBuffer[i]=100*EMA2_MTMBuffer[i]/EMA2_AbsMTMBuffer[i];
     }

Après :

//--- calculating values of our indicator
   if(prev_calculated==0) start=begin+r+s-1; // set initial index for input arrays
   else start=prev_calculated-1;              // set start equal to last array index
   for(int i=start;i<rates_total;i++)
     {
      TSIBuffer[i]=100*EMA2_MTMBuffer[i]/EMA2_AbsMTMBuffer[i];
     }

Le réglage de l'indicateur est terminé, et maintenant il ignorera le premières valeurs vides begin du tableau d'entrée price[] dans OnCalculate() et prendra en compte le décalage causé par cette omission. Mais nous devons nous rappeler que certains autres indicateurs peuvent utiliser les valeurs TSI pour les calculs. Pour cette raison, nous allons configurer les valeurs vides de notre indicateur sur EMPTY_VALUE.

L'initialisation des tampons d'indicateurs est-elle nécessaire ?

En MQL5, les tableaux ne sont pas initialisés par défaut avec certaines valeurs définies. La même chose s'applique aux tableaux, qui sont spécifiés par la fonction SetIndexBuffer() pour les tampons d'indicateur. Si un tableau est un tampon d'indicateur, sa taille dépendra de la valeur du paramètre rates_total dans la fonction OnCalculate() ;

Vous pourriez être tenté d'initialiser tous les tampons d'indicateurs avec des valeurs EMPTY_VALUE à l'aide de la fonction ArrayInitialize(), par exemple, immédiatement au début de OnCalculate() :

//--- if it is the first call of OnCalculate() 
   if(prev_calculated==0)
     {
      ArrayInitialize(TSIBuffer,EMPTY_VALUE);
     }

Mais cela n'est pas recommandé pour la raison suivante : Pendant que le terminal client fonctionne, de nouvelles cotations sont reçues pour le symbole, dont les données sont utilisées pour calculer l'indicateur. Après un certain temps, le nombre de barres augmentera, de sorte que le terminal client réservera de la mémoire supplémentaire pour les tampons indicateurs.

Mais les valeurs des nouveaux éléments « attachés » de tableau peuvent avoir n'importe quelle valeur, car lors de la réallocation de mémoire pour un tableau, l'initialisation n'est pas effectuée. L'initialisation initiale peut vous donner une fausse certitude, que tous les éléments du tableau qui n'ont pas été explicitement définis, seront remplis avec les valeurs qui ont été spécifiées lors de l'initialisation. Bien sûr, ce n'est pas vrai, et vous ne devriez jamais penser que la valeur numérique d'une variable ou d'un élément du tableau sera initialisée avec la valeur qui nous est nécessaire.

Vous devez configurer la valeur pour chaque élément du tampon d'indicateur. Si les valeurs de certaines barres ne sont pas définies par l'algorithme de l'indicateur, vous devez les configurer explicitement avec une valeur vide. Par exemple, si une certaine valeur du tampon indicateur est calculée par l'opération de division, dans certains cas, le diviseur peut être égal à zéro.

Nous savons que la division par zéro est une erreur d'exécution critique dans MQL5 et qu'elle conduit à l'arrêt immédiat d'un programme mql5. Au lieu d'éviter la division par zéro en gérant ce cas particulier dans le code, il est nécessaire de configurer la valeur de cet élément tampon. Peut-être qu'il est préférable d'utiliser la valeur que nous avons assignée comme vide pour ce style de tracé.

Par exemple, pour certains styles de tracé, nous avons défini zéro comme valeur vide à l'aide de la fonction PlotIndexSetDouble() :

   PlotIndexSetDouble(plotting_style_index,PLOT_EMPTY_VALUE,0);   

Ensuite, pour toutes les valeurs vides du tampon indicateur dans ce tracé, il est nécessaire de définir explicitement la valeur zéro :

   if(divider==0)
      IndicatorBuffer[i]=0;
   else
      IndicatorBuffer[i]=... 

De plus, si DRAW_BEGIN a été spécifié pour certains tracés, tous les éléments du tampon indicateur avec des indices de 0 à DRAW_BEGIN seront automatiquement remplis de zéros.

Conclusion

Faisons donc un bref résumé. Il y a quelques conditions nécessaires pour que l'indicateur soit correctement calculé sur la base des données d'un autre indicateur (et pour le rendre utilisable dans d'autres mql5-programs)) :

  1. Les valeurs vides dans les indicateurs intégrés sont remplies avec les valeurs de la constante EMPTY_VALUE, qui est exactement égale à la valeur maximale pour le type double (DBL_MAX).
  2. Pour plus de détails sur l'index de début des valeurs significatives d'un indicateur, vous devez analyser le paramètre d'entrée de begin du short formulaire de OnCalculate().
  3. Afin d'interdire le traçage des premières valeurs N pour le style de traçage spécifié, configurez le paramètre DRAW_BEGIN à l'aide du code qui suit
    PlotIndexSetInteger(plotting_style_index,PLOT_DRAW_BEGIN,N);
  4. Si DRAW_BEGIN est spécifié pour certains tracés, tous les éléments du tampon indicateur avec les indices de 0 à DRAW_BEGIN seront remplis automatiquement avec des valeurs vides (la valeur par défaut est EMPTY_VALUE).
  5. Dans la fonction OnCalculer() ajoutez un décalage supplémentaire via les barres begin pour une utilisation correcte des autres données d'indicateur dans votre propre indicateur :
    //--- if it's the first call 
       if(prev_calculated==0)
         { 
          //--- increase position of data beginning by begin bars, 
          //--- because of other indicator's data use      
          if(begin>0)PlotIndexSetInteger(plotting_style_index,PLOT_DRAW_BEGIN,begin+N);
         }
    
  6. Vous pouvez spécifier votre propre valeur vide qui diffère de EMPTY_VALUE dans la fonction OnInit() à l'aide du code suivant :
    PlotIndexSetDouble(plotting_style_index,PLOT_EMPTY_VALUE,your_empty_value);
  7. Ne comptez pas sur une initialisation à usage unique des tampons d’indicateur à l'aide du code suivant :
    ArrayInitialize(buffer_number,value);
        
    Vous devez configurer toutes les valeurs du tampon d'indicateur pour la fonction OnCalculate() explicitement et systématiquement, y compris les valeurs vides .

Bien sûr, dans l’avenir, lorsque vous avez une certaine expérience dans la rédaction des indicateurs, vous rencontrerez des cas qui dépassent le cadre de cet article, mais j’ose croire qu'à ce moment-là votre connaissance de MQL5 vous permettra de les résoudre.

Traduit du russe par MetaQuotes Software Corp.

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

Limitations et vérifications dans Expert Advisors Limitations et vérifications dans Expert Advisors
Est-il permis d’échanger ce symbole lundi ? Y a-t-il assez d’argent pour ouvrir une position ? Quelle est l’ampleur de la perte si le Stop Loss se déclenche ? Comment limiter le nombre d’ordres en attente ? L’opération de trade a-t-elle été exécutée à la barre actuelle ou à la barre précédente ? Si un robot de trading ne peut pas effectuer ce type de vérifications, toute stratégie de trade risque de se transformer en une stratégie de perte. Cet article présente les exemples de vérifications utiles dans n’importe quel Expert Advisor.
MQL5 : Créez votre propre indicateur MQL5 : Créez votre propre indicateur
Qu’est-ce qu’un indicateur? C’est un ensemble de valeurs calculées que nous voulons afficher à l’écran de manière pratique. Les ensembles de valeurs sont représentés dans les programmes sous forme de tableaux. Ainsi, la création d’un indicateur signifie écrire un algorithme qui gère certains tableaux (tableaux de prix) et enregistre les résultats de la manipulation sur d’autres tableaux (valeurs d’indicateur). En décrivant la création de True Strength Index, l’auteur montre comment écrire des indicateurs dans MQL5.
L'ordre de création et de destruction d'objets dans MQL5 L'ordre de création et de destruction d'objets dans MQL5
Chaque objet, qu'il s'agisse d'un objet personnalisé, d'un tableau dynamique ou d'un tableau d'objets, est créé et supprimé dans le programme MQL5 à sa manière. Souvent, certains objets font partie d'autres objets et l'ordre de suppression des objets lors de la désinitialisation devient particulièrement important. Cet article fournit quelques exemples qui couvrent les mécanismes de travail avec des objets.
Embarquement sur de nouveaux rails : Indicateurs personnalisés dans MQL5 Embarquement sur de nouveaux rails : Indicateurs personnalisés dans MQL5
Je ne vais pas énumérer toutes les nouvelles possibilités et fonctionnalités du nouveau terminal et du nouveau langage. Elles sont nombreuses, et certaines nouveautés méritent d'être discutées dans un article séparé. De plus, il n'y a aucun code d’écrit avec une programmation orientée objet, c'est un sujet trop sérieux pour être simplement mentionné dans un contexte comme des avantages supplémentaires pour les développeurs. Dans cet article, nous examinerons les indicateurs, leur structure, leur dessin, leurs types et leurs détails de programmation, par rapport à MQL4. J'espère que cet article sera utile aussi bien aux débutants qu'aux développeurs expérimentés, peut-être que certains d'entre eux trouveront quelques éléments d’apprentissage.