English Русский 中文 Español Deutsch 日本語 Português 한국어 Italiano Türkçe
L'ordre de création et de destruction d'objets dans MQL5

L'ordre de création et de destruction d'objets dans MQL5

MetaTrader 5Exemples | 22 décembre 2021, 16:13
102 0
MetaQuotes
MetaQuotes

De quoi parle l'article ?

Les programmes MQL5 sont écrits dans des concepts de programmation orientée objet (POO), et cela ouvre non seulement de nouvelles possibilités pour créer des bibliothèques personnalisées, mais vous permet également d'utiliser des classes complètes et testées d'autres développeurs. Dans la bibliothèque standard, qui est incluse dans le terminal client MetaTrader 5, il existe des centaines de classes qui contiennent des milliers de méthodes.

Pour tirer pleinement parti de la POO, nous devons clarifier certains détails sur la création et la suppression d'objets dans les programmes MQL5. La création et la suppression d'objets sont brièvement décrites dans Documentation, et cet article illustrera ce sujet dans des exemples.

Initialisation et désinitialisation des variables globales

L’initialisation des variables globales se fait juste après le démarrage du programme MQL5 et avant tout appel de fonction. Lors de l'initialisation, des valeurs initiales sont assignées aux variables de types simples, et le constructeur des objets est appelé, s'il y est déclaré. 

A titre d'exemple, déclarons deux classes CObjectA et CObjectB. Chaque classe a un constructeur et un destructeur, contenant une simple fonction Print(). Déclarons les variables de ces types de classe globalement et exécutons le script.

//+------------------------------------------------------------------+
//|                                         GlobalVar_TestScript.mq5 |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
class CObjectA
  {
public:
                     CObjectA(){Print(__FUNCTION__," Constructor");}
                    ~CObjectA(){Print(__FUNCTION__," Destructor");}
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class CObjectB
  {
public:
                     CObjectB(){Print(__FUNCTION__," Constructor");}
                    ~CObjectB(){Print(__FUNCTION__," Destructor");}
  };
//--- declaring the objects globally
CObjectA first;
CObjectB second;
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   Print(__FUNCTION__);
  }

Le résultat du script est affiché dans le journal Experts :

GlobalVar_TestScript (EURUSD,H1)    13:05:07    CObjectA::ObjectA  Constructeur
GlobalVar_TestScript (EURUSD,H1)    13:05:07    CObjectB::ObjectB  Constructeur
GlobalVar_TestScript (EURUSD,H1)    13:05:07    OnStart
GlobalVar_TestScript (EURUSD,H1)    13:05:07    CObjectB::~ObjectB  Destructeur
GlobalVar_TestScript (EURUSD,H1)    13:05:07    CObjectA::~ObjectA  Destructeur

D'après le journal, il est clair que l'ordre d'initialisation correspond à l'ordre de déclaration des variables dans le script GlobalVar_TestScript.mq5 et que la désinitialisation est effectuée dans l'ordre inverse avant le déploiement du programme MQL5.

Initialisation et désinitialisation des variables locales

Les variables locales sont désinitialisées à la fin du bloc de programme dans lequel elles ont été déclarées et dans l'ordre inverse de leur déclaration. Le bloc de programme est un opérateur composé qui peut faire partie de l'opérateur switch, des opérateurs de boucle (for, while et do-while), du corps de la fonction ou une partie de l’ opérateur if-else.

Les variables locales ne sont initialisées que si elles sont utilisées dans le programme. Si une variable est déclarée, mais que le bloc de code dans lequel elle est déclarée n'est pas exécuté, alors cette variable n'est pas créée et donc elle n'est pas initialisée. 

Pour illustrer cela, revenons à nos CObjectA and CObjectB classes, et créons la nouvelle classe CObjectС. Les classes sont toujours déclarées globalement, mais les variables de ces classes sont désormais déclarées localement dans la fonction OnStart().

Déclarons la variable de la CObjectA classclasse explicitement dans la première ligne de fonction, mais les objets des classes CObjectB et CObjectС  seront déclarés dans des blocs séparés, qui seront exécutés en fonction de la valeur de la variable d’entrée execute. Dans MetaEditor, les variables d'entrée des programmes MQL5 sont surlignées en marron.

//+------------------------------------------------------------------+
//|                                          LocalVar_TestScript.mq5 |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property script_show_inputs
//--- input parameters
input bool     execute=false;
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class CObjectA
  {
public:
                     CObjectA(){Print(__FUNCTION__," Constructor");}
                    ~CObjectA(){Print(__FUNCTION__," Destructor");}
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class CObjectB
  {
public:
                     CObjectB(){Print(__FUNCTION__," Constructor");}
                    ~CObjectB(){Print(__FUNCTION__," Destructor");}
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class CObjectC
  {
public:
                     CObjectC(){Print(__FUNCTION__," Constructor");}
                    ~CObjectC(){Print(__FUNCTION__," Destructor");}
  };
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   CObjectA objA;
//--- this block will NOT be executed if execute==false
   if(execute)
     {
      CObjectB objB;
     }
//--- this block WILL be executed if execute==false
   if(!execute)
     {
      CObjectC objC;
     }
  }
//+------------------------------------------------------------------+

Le résultat est :

LocalVar_TestScript (GBPUSD,H1)    18:29:00    CObjectA::CObjectA  Constructeur
LocalVar_TestScript (GBPUSD,H1)    18:29:00    CObjectC::CObjectC  Constructeur
LocalVar_TestScript (GBPUSD,H1)    18:29:00    CObjectC::~CObjectC  Destructeur
LocalVar_TestScript (GBPUSD,H1)    18:29:00    CObjectA::~CObjectA  Destructeur

L'objet de la classe CObjectA  sera toujours initialisé en premier automatiquement, quelle que soit la valeur du paramètre d'entrée d'exécution. Ensuite, l'objet objB ou l’ objet objC est automatiquement initialisé - cela dépend du bloc exécuté en fonction de la valeur du paramètre d'entrée d'exécution. Par défaut, ce paramètre a une valeur false, et dans ce cas après l'initialisation de la variable objA vient l'initialisation de la variable objC. Ceci est évident dans l'exécution du constructeur et du destructeur.

Mais quel que soit l'ordre d'initialisation (quel que soit le paramètre d'exécution), la désinitialisation des variables de type complexe se fait dans l'ordre inverse de leur initialisation. Cela s'applique à la fois aux objets locaux et aux objets de classe globaux créés automatiquement. Dans ce cas, il n'y a pas de différence entre eux.

Initialisation et désinitialisation des objets créés dynamiquement

Dans MQL5, les objets composés sont initialisés automatiquement, mais si vous souhaitez contrôler manuellement le processus de création d'objets, vous devez utiliser des pointeurs d’objet. Une variable déclarée en tant que pointeur d'objet d'une classe ne contient pas l'objet lui-même et il n'y a pas d'initialisation automatique de cet objet.

Les pointeurs peuvent être déclarés localement et/ou globalement, et en même temps ils peuvent être initialisés avec une valeur vide NULLE de type hérité. La création d'objet est effectuée uniquement lorsque l'opérateur new est appliqué au pointeur d'objet et ne dépend pas de la déclaration du pointeur d'objet.

Les objets créés dynamiquement sont supprimés à l'aide de l'opérateur delete, nous devons donc le gérer. A titre d'exemple, déclarons globalement deux variables : une de type CObjectA et une de type CObjectB, et une autre variable de type CObjetC avec pointeur d'objet.

//+------------------------------------------------------------------+
//|                                       GlobalVar_TestScript_2.mq5 |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
class CObjectA
  {
public:
                     CObjectA(){Print(__FUNCTION__," Constructor");}
                    ~CObjectA(){Print(__FUNCTION__," Destructor");}
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class CObjectB
  {
public:
                     CObjectB(){Print(__FUNCTION__," Constructor");}
                    ~CObjectB(){Print(__FUNCTION__," Destructor");}
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class CObjectC
  {
public:
                     CObjectC(){Print(__FUNCTION__," Constructor");}
                    ~CObjectC(){Print(__FUNCTION__," Destructor");}
  };
CObjectC *pObjectC;
CObjectA first;
CObjectB second;
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   pObjectC=new CObjectC;
   Print(__FUNCTION__);
   delete(pObjectC);
  }
//+------------------------------------------------------------------+

Malgré le fait que le pointeur d'objet créé dynamiquement pObjectC soit déclaré avant les variables statiques premier et second, cet objet même n'est initialisé que lorsqu'il est créé par l'opérateur new. Dans cet exemple, l'opérateur new se trouve dans la fonction OnStart().

GlobalVar_TestScript_2 (EURUSD,H1)    15:03:21    CObjectA::CObjectA  Constructeur
GlobalVar_TestScript_2 (EURUSD,H1)    15:03:21    CObjectB::CObjectB  Constructeur
GlobalVar_TestScript_2 (EURUSD,H1)    15:03:21    CObjectC::CObjectC  Constructeur
GlobalVar_TestScript_2 (EURUSD,H1)    15:03:21    OnStart
GlobalVar_TestScript_2 (EURUSD,H1)    15:03:21    CObjectC::~CObjectC  Destructeur
GlobalVar_TestScript_2 (EURUSD,H1)    15:03:21    CObjectB::~CObjectB  Destructeur
GlobalVar_TestScript_2 (EURUSD,H1)    15:03:21    CObjectA::~CObjectA  Destructeur

Lorsque l'exécution du programme dans la fonction OnStart() atteint l'opérateur

   pObjectC=new CObjectC;

l'objet est initialisé et le constructeur de cet objet est appelé. Ensuite, le programme exécute cette chaîne

   Print(__FUNCTION__);

qui génère le texte suivant dans le Journal :

GlobalVar_TestScript_2 (EURUSD,H1)    15:03:21    OnStart

puis l'objet créé dynamiquement est supprimé en appelant l'opérateur delete :

   delete(pObjectC);

Ainsi, les objets sont initialisés dynamiquement lors de leur création par l'opérateur new et sont supprimés par l'opérateur.delete. 

Exigence obligatoire : tous les objets créés à l'aide de l'expression object_pointer=new Class_Name doivent toujours être supprimés à l'aide de l'opérateur delete(object_pointer). Si, pour une raison quelconque, l'objet créé dynamiquement (après la fin du bloc où il a été initialisé) n'a pas été supprimé à l'aide de l'opérateur delete, un message correspondant sera affiché dans le journal des experts.


Suppression d’objets créés dynamiquement

Comme il a été mentionné précédemment, chaque objet créé dynamiquement est initialisé à l'aide de l'opérateur new et doit toujours être supprimé à l'aide de l'opérateur delete. Mais n'oubliez pas que l'opérateur new crée un objet et renvoie un pointeur vers cet objet.  L'objet créé n’es lui-même pas dans la variable, contenant le pointeur d'objet. Vous pouvez déclarer plusieurs pointeurs et les assigner au même pointeur d'objet.

//+------------------------------------------------------------------+
//|                                        LocalVar_TestScript_1.mq5 |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property link      "http://www.mql5.com"
#property version   "1.00"
//+------------------------------------------------------------------+
//|  simple class                                                    |
//+------------------------------------------------------------------+
class CItem
  {
public:
                     CItem(){Print(__FUNCTION__," Constructor");}
                    ~CItem(){Print(__FUNCTION__," Destructor");}
  };
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- declaring the first object pointer array 
   CItem* array1[5];
//--- declaring the first object pointer array 
   CItem* array2[5];
//--- filling arrays in the loop
   for(int i=0;i<5;i++)
     {
      //--- creating a pointer for the first array using new operator
      array1[i]=new CItem;
      //--- creating a pointer for the second array via copy from the first array
      array2[i]=array1[i];
     }
   // We "forgot" to delete objects before exiting the function. See "Experts" tab.
  }
//+------------------------------------------------------------------+

La sortie indique qu'il reste plusieurs objets non supprimés. Mais il n'y aura que 5 objets non supprimés au lieu de 10, comme vous pouvez le penser, car l’opérateur new n'a créé que 5 objets.

(GBPUSD,H1) 12:14:04 CItem::CItem Constructeur
(GBPUSD,H1) 12:14:04 CItem::CItem Constructeur
(GBPUSD,H1) 12:14:04 CItem::CItem Constructeur
(GBPUSD,H1) 12:14:04 CItem::CItem Constructeur
(GBPUSD,H1) 12:14:04 CItem::CItem Constructeur
(GBPUSD,H1) 12:14:04 5 objets non supprimés restants

Même si le destructeur de l'objet créé dynamiquement n'est pas appelé (l'objet n'est pas supprimé à l'aide de l'opérateur delete), la mémoire sera toujours effacée. Mais dans le journal « Experts », il est dit que l'objet n'a pas été supprimé. Cela peut vous aider à découvrir la gestion incorrecte des objets et à corriger l'erreur.

Dans l'exemple suivant, essayons de supprimer les pointeurs dans chacun des deux tableaux de pointeurs tableau1 et tableau2.

//+------------------------------------------------------------------+
//|                                        LocalVar_TestScript_2.mq5 |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property link      "http://www.mql5.com"
#property version   "1.00"
//+------------------------------------------------------------------+
//|  simple class                                                   |
//+------------------------------------------------------------------+
class CItem
  {
public:
                     CItem(){Print(__FUNCTION__," Constructor");}
                    ~CItem(){Print(__FUNCTION__," Destructor");}
  };
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- declaring the first object pointer array
   CItem* array1[5];
//--- declaring the second object pointer array
   CItem* array2[5];
//--- filling arrays in the loop
   for(int i=0;i<5;i++)
     {
      //--- creating a pointer for the first array using new operator
      array1[i]=new CItem;
      //--- creating a pointer for the second array via copy from the first array
      array2[i]=array1[i];
     }
//--- deleting object using pointers of second array
   for(int i=0;i<5;i++) delete(array2[i]);
//--- let's try to delete objects using pointers of first array
   for(int i=0;i<5;i++) delete(array2[i]);
// in Experts tab there are messages about trying to delete invalid pointer
  }
//+------------------------------------------------------------------+

Le résultat dans l'onglet Experts est maintenant différent.

(GBPUSD,H1) 15:02:48 CItem::CItem Constructeur
(GBPUSD,H1) 15:02:48 CItem::CItem Constructeur
(GBPUSD,H1) 15:02:48 CItem::CItem Constructeur
(GBPUSD,H1) 15:02:48 CItem::CItem Constructeur
(GBPUSD,H1) 15:02:48 CItem::CItem Constructeur
(GBPUSD,H1) 15:02:48 CItem :: ~ CItem Destructeur
(GBPUSD,H1) 15:02:48 CItem :: ~ CItem Destructeur
(GBPUSD,H1) 15:02:48 CItem :: ~ CItem Destructeur
(GBPUSD,H1) 15:02:48 CItem :: ~ CItem Destructeur
(GBPUSD,H1) 15:02:48 CItem :: ~ CItem Destructeur
(GBPUSD,H1) 15:02:48 supprimer le pointeur non valide
(GBPUSD,H1) 15:02:48 supprimer le pointeur non valide
(GBPUSD,H1) 15:02:48 supprimer le pointeur non valide
(GBPUSD,H1) 15:02:48 supprimer le pointeur non valide
(GBPUSD,H1) 15:02:48 supprimer le pointeur non valide

Les objets créés par CItem ont été supprimés avec succès dans la première boucle for(), mais d'autres tentatives de suppression d'objets qui n'existent pas, dans la deuxième boucle ont provoqué des messages sur des pointeurs non valides. L'objet créé dynamiquement doit être supprimé une fois, et avant l'utilisation de tout pointeur d'objet, il doit être vérifié avec la fonction CheckPointer().

Vérification du pointeur à l'aide de la fonction CheckPointer()

CheckPointer() est utilisé pour vérifier les pointeurs et permet d'identifier le type de pointeur. Lorsque vous travaillez avec des objets créés dynamiquement, il y a deux possibilités : 

  • restauration à la fin du bloc d'exécution
  • tentative de suppression d'un objet déjà supprimé 

Prenons un autre exemple, qui illustre l'interrelation des objets. Créons deux classes : la première classe CItemArray contient le tableau de pointeurs d'une autre classe CItem.

//+------------------------------------------------------------------+
//|                                        LocalVar_TestScript_3.mq5 |
//|                        Copyright 2009, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property link      "http://www.mql5.com"
#property version   "1.00"
//+------------------------------------------------------------------+
//|  simple class                                                    |
//+------------------------------------------------------------------+
class CItem
  {
public:
                     CItem(){Print(__FUNCTION__," Constructor");}
                    ~CItem(){Print(__FUNCTION__," Destructor");}
  };
//+------------------------------------------------------------------+
//| class, containing pointer array of CItem class                   |
//+------------------------------------------------------------------+
class CItemArray
  {
private:
   CItem            *m_array[];
public:
                     CItemArray(){Print(__FUNCTION__," Constructor");}
                    ~CItemArray(){Print(__FUNCTION__," Destructor");Destroy();}
   void               SetArray(CItem &array[]);
protected:
   void               Destroy();
  };
//+------------------------------------------------------------------+
//|  filling pointers array                                          |
//+------------------------------------------------------------------+
CItemArray::SetArray(CItem &array[])
  {
   int size=ArraySize(array);
   ArrayResize(m_array,size);
   for(int i=0;i<size;i++)m_array[i]=GetPointer(array[i]);
  }
//+------------------------------------------------------------------+
//|  releasing                                                       |
//+------------------------------------------------------------------+
CItemArray::Destroy(void)
  {
   for(int i=0;i<ArraySize(m_array);i++)
     {
      if(CheckPointer(m_array[i])!=POINTER_INVALID)
        {
         if(CheckPointer(m_array[i])==POINTER_DYNAMIC) delete(m_array[i]);
        }
      else Print("Invalid pointer to delete");
     }
  }

Les classes elles-mêmes ne contiennent aucune erreur, mais leur utilisation peut apporter des surprises. La première variante du script :

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   CItemArray items_array;
   CItem array[5];
   items_array.SetArray(array);
  }

L'exécution de cette variante de script affichera les messages suivants :

(GBPUSD,H1) 16:06:17 CItemArray::CItemArray Constructeur
(GBPUSD,H1) 16:06:17 CItem::CItem Constructeur
(GBPUSD,H1) 16:06:17 CItem::CItem Constructeur
(GBPUSD,H1) 16:06:17 CItem::CItem Constructeur
(GBPUSD,H1) 16:06:17 CItem::CItem Constructeur
(GBPUSD,H1) 16:06:17 CItem::CItem Constructeur
(GBPUSD,H1) 16:06:17 CItem :: ~ CItem Destructeur
(GBPUSD,H1) 16:06:17 CItem :: ~ CItem Destructeur
(GBPUSD,H1) 16:06:17 CItem :: ~ CItem Destructeur
(GBPUSD,H1) 16:06:17 CItem :: ~ CItem Destructeur
(GBPUSD,H1) 16:06:17 CItem :: ~ CItem Destructeur
(GBPUSD,H1) 16:06:17 CItemArray::~CItemArray Destructeur
(GBPUSD,H1) 16:06:17 Pointeur à supprimer non valide
(GBPUSD,H1) 16:06:17 Pointeur à supprimer non valide
(GBPUSD,H1) 16:06:17 Pointeur à supprimer non valide
(GBPUSD,H1) 16:06:17 Pointeur à supprimer non valide

Comme la déclaration de la variable de classe CItemArray vient en premier, elle est initialisée en premier et le destructeur de classe est appelé. Ensuite, tableau[5] est déclaré, contenant des pointeurs d'objet de CItem class. C'est pourquoi nous voyons cinq messages concernant l'initialisation de chaque objet.

Dans la dernière ligne de ce script simple, les pointeurs du tableau array[5] sont copiés dans le tableau de pointeurs d'objets internes nommé items_array (voir « LocalVar_TestScript_4.mq5 »).

   items_array.SetArray(array);

Pour l'instant, le script arrête de s'exécuter et les objets créés automatiquement sont automatiquement supprimés. Le premier objet à supprimer est celui qui a été initialisé en dernier - c’est array [5], le tableau des pointeurs. Cinq dossiers du Journal concernant l'appel du destructeur de la classe CItem le confirment. Vient ensuite le message concernant l'appel du destructeur pour l'objet items_array, car il a été initialisé juste avant la variable array[5]. 

Mais le destructeur de classe CArrayItem appelle la fonction protégée Destroy(), qui essaie de supprimer les objets CItem via des pointeurs dans m_array[] via l'opérateur delete. Le pointeur est vérifié en premier et s'il n'est pas valide, les objets ne sont pas supprimés et le message « pointeur non valide à supprimer » s’affiche 

Il y a 5 dossiers de ce type dans le Journal, c'est-à-dire que tous les pointeurs du tableau m_array[] ne sont pas valides. Cela s'est produit parce que les objets de ces pointeurs ont déjà été désinitialisés lors de la désinitialisation array[] du tableau.

Ajustons notre script, en échangeant les déclarations des variables items_array and items_array[].

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   CItem array[5];
   CItemArray items_array;
   items_array.SetArray(array);
  }

Le script corrigé ne produit pas d'erreurs. Tout d'abord, la variable items_array a été désinitialisée, car elle a été déclarée en dernier. Lors de sa désinitialisation, le destructeur de classe ~CItemArray() a été appelé, qui, à son tour, a appelé la fonction Destroy(). 

Dans cet ordre de déclaration, le tableau items_array est supprimé avant le tableau array[5]. Dans la fonction Destroy(), qui est appelée à partir du destructeur items_array , les objets pointeurs existent toujours, donc aucune erreur ne se produit.

La suppression correcte des objets créés dynamiquement peut également être vue dans l'exemple de la fonction GetPointer(). Dans cet exemple, la fonction Destroy() est explicitement appelée pour garantir le bon ordre de la suppression d’objets.

Conclusion

Comme vous pouvez le voir, la création et la suppression d'objets se font simplement. Il suffit de passer en revue tous les exemples de cet article et vous pouvez créer vos propres variantes d'interrelations entre les objets créés automatiquement et dynamiquement.

Vous devez toujours vérifier dans vos classes la suppression correcte des objets et concevoir correctement vos destructeurs, afin qu'il n'y ait pas d'erreurs lors de l'accès à des pointeurs non valides. N'oubliez pas que si vous utilisez des objets créés dynamiquement à l'aide de l'opérateur new, vous devez correctement supprimer ces objets à l'aide de l'opérateur delete.

De cet article, vous n'avez appris que l'ordre de création et de suppression des objets dans MQL5. L'organisation d'un travail sécurisé avec des pointeurs d'objet dépasse le cadre de cet article.

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

MQL5 pour les débutants : Guide d'utilisation des indicateurs techniques dans Expert Advisors MQL5 pour les débutants : Guide d'utilisation des indicateurs techniques dans Expert Advisors
Afin d’obtenir les valeurs d’un indicateur intégré ou personnalisé dans un Expert Advisor, son descripteur doit d’abord être créé à l’aide de la fonction correspondante. Les exemples de l’article montrent comment utiliser tel ou tel indicateur technique lors de la création de vos propres programmes. L’article décrit les indicateurs générés dans le langage MQL5. Il est destiné à ceux qui n’ont pas beaucoup d’expérience dans le développement de stratégies de trading et offre des moyens simples et clairs de travailler avec des indicateurs en utilisant la bibliothèque de fonctions offerte.
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.
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.
Appliquer un indicateur à un autre Appliquer un indicateur à un autre
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é). Vous souhaitez améliorer un indicateur pour sa bonne application aux données de l'autre indicateur ? Dans cet article, nous passerons en revue toutes les étapes requises pour une telle modification.