
Les bases de la programmation MQL5 : Variables globales du terminal
Introduction
Dans l'environnement MQL4/5, il existe un instrument intéressant - les variables globales du terminal client. Il permet de créer des zones de stockage de données partagées pour tous les programmes du terminal. De plus, la durée de vie de cette zone ne s'arrête pas à la fermeture du terminal. Cet article suggère d'utiliser les outils de Programmation orientée objet pour avoir une idée claire de ce que sont les variables globales du terminal.
Plus loin dans l'article, les variables globales du terminal client seront appelées «variables globales» sauf indication contraire.
1. Variables globales, fonctions
Du point de vue d'un programmeur, une variable globale est une zone de mémoire nommée disponible pour tous les programmes de travail d'un terminal de trading. Les programmeurs novices doivent noter que s'il y a plusieurs terminaux fonctionnant simultanément, chacun d'eux aura son propre espace mémoire indépendant pour les variables globales. Ils ne vont pas se chevaucher.
Les développeurs de langage précisent dans la documentation qu'il y a 11 fonctions utilisées pour travailler avec des variables globales.
La théorie peut être trouvée dans la «GlobalVariables» du manuel MQL4.
Dans les sections suivantes, j'utiliserai les instruments de programmation orientés objets pour la mise en œuvre de tâches définies.
2. Classe CGlobalVar
Guidés par les idées de la programmation orientée objet, créons la classe CGlobalVar, qui sera directement responsable de l'objet d'une variable globale.
//+------------------------------------------------------------------+ //| Class CGlobalVar | //+------------------------------------------------------------------+ class CGlobalVar : public CObject { //--- === Data members === --- private: string m_name; double m_value; //--- datetime m_create_time; datetime m_last_time; //--- flag for temporary var bool m_is_temp; //--- === Methods === --- public: //--- constructor/destructor void CGlobalVar(void); void CGlobalVar(const string _var_name,const double _var_val, const datetime _create_time); void ~CGlobalVar(void){}; //--- create/delete bool Create(const string _var_name,const double _var_val=0.0, const bool _is_temp=false); bool Delete(void); //--- exist bool IsGlobalVar(const string _var_name,bool _to_print=false); //--- set methods bool Value(const double _var_val); bool ValueOnCondition(const double _var_new_val,const double _var_check_val); //--- get methods string Name(void) const; datetime CreateTime(void) const; datetime LastTime(void); template<typename T> T GetValue(T _type) const; bool IsTemporary(void) const; //--- private: string FormName(const string _base_name,const bool _is_temp=false); };
Que doit contenir une classe ? Pour une liste d'attributs minimum, je choisirais les propriétés suivantes :
- nom d'une variable ;
- valeur d'une variable;
- temps de création;
- heure du dernier appel ;
- caractéristique d'une variable temporaire.
Quant aux méthodes, elles se présentent comme suit :
- création ;
- Suppression ;
- vérifier l'existence ;
- définir une nouvelle valeur ;
- définir une nouvelle valeur par condition ;
- recevoir un nom ;
- recevoir une valeur ;
- recevoir un indicateur de variable temporaire.
La méthode CGlobalVar::GetValue doit être mentionnée séparément. C'est une méthode modèle. Il renvoie le type de données pour la valeur de la variable qu'un utilisateur définit comme argument.
Le problème ici est que dans MQL, une fonction ne peut être saisie que par des paramètres. Par conséquent, un faux paramètre doit être ajouté.
Créons le script de test Globals_test1.mq5 où nous travaillerons avec des objets de type CGlobalVar.
#include "CGlobalVar.mqh" //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { CGlobalVar gVar1; //--- create a temporary global var if(gVar1.Create("Gvar1",3.123456789101235,true)) { Print("\n---=== A new global var ===---"); PrintFormat("Name: \"%s\"",gVar1.Name()); PrintFormat("Is temporary: %d",gVar1.IsTemporary()); //--- Get the value //--- double type double d=0.0; double dRes=gVar1.GetValue(d); PrintFormat("Double value: %0.15f",dRes); //--- float type float f=0.0; float fRes=gVar1.GetValue(f); PrintFormat("Float value: %0.7f",fRes); //--- string type string s=NULL; string sRes=gVar1.GetValue(s); PrintFormat("String value: %s",sRes); //--- Set a new value double new_val=3.191; if(gVar1.Value(new_val)) PrintFormat("New value is set: %f",new_val); //--- Set a new value on condition new_val=3.18; if(gVar1.ValueOnCondition(3.18,3.191)) PrintFormat("New value on conditionis set: %f",new_val); } }
Une variable globale est créée comme suit :
gVar1.Create("Gvar1",3.123456789101235,true)
Le premier argument est le composant de base du futur nom de variable («Gvar1»), le deuxième argument est la valeur de (3.123456789101235) et le troisième argument est la caractéristique indiquant que la variable va être temporaire (vrai).
Le nom de la variable est créé en ajoutant le nom et le type du programme au composant de base.
Dans mon cas c'est :
- Gvar1 - le composant de base ;
- prog_Globals_test1 - programme où la variable a été créée (son nom est Globals_test1) ;
- le type de programme est - scr (script).
En appuyant sur F3, l'entrée suivante devrait apparaître dans la liste des variables globales dans la fenêtre MetaTrader 5 :
Fig.1. La valeur de la variable Test_temp_var1_prog_Globals_test1_scr est égale à 3.18
Lors de son lancement et de sa mise en œuvre réussie, les entrées suivantes sont imprimées dans le journal « Experts » :
KP 0 10:20:20.736 Globals_test1 (AUDUSD.e,H1) ---=== A new global var ===--- EH 0 10:20:21.095 Globals_test1 (AUDUSD.e,H1) Name: "Gvar1_temp_prog_Globals_test1_scr" LF 0 10:20:21.876 Globals_test1 (AUDUSD.e,H1) Is temporary: 1 MO 0 10:20:31.470 Globals_test1 (AUDUSD.e,H1) Double value: 3.123456789101235 KG 0 10:20:31.470 Globals_test1 (AUDUSD.e,H1) Float value: 3.1234567 OP 0 10:20:31.470 Globals_test1 (AUDUSD.e,H1) String value: 3.123456789101235 RH 0 10:20:31.470 Globals_test1 (AUDUSD.e,H1) New value is set: 3.191000 DJ 0 10:20:31.470 Globals_test1 (AUDUSD.e,H1) New value on conditionis set: 3.180000
Différents types de données de la valeur de la variable sont imprimés dans le journal.
Si le terminal MetaTrader 5 est redémarré, la variable Gvar1_temp_prog_Globals_test1_scr disparaît de la liste des variables globales. Cela se produit parce que la variable était temporaire et qu'elle vivait pendant que le terminal était ouvert.
En MQL4/5, lors de la réception des données sur la variable globale, il n'y a aucun moyen de savoir si la variable est temporaire ou non. Le moyen le plus simple d'identifier une variable temporaire est peut-être d'ajouter une clé au nom de la variable. Par exemple, il peut s'agir du suffixe «temp» dans le nom de la variable. Cependant, la nécessité de contrôler la création du nom de la variable globale est un inconvénient distinct de cette approche, surtout si de telles variables sont créées par d'autres programmes n'utilisant pas la classe CGlobalVar.
À un moment donné, j'ai voulu savoir combien de variables globales pouvaient être créées et à quelle vitesse.
J'ai légèrement modifié le script précédent et je l'ai nommé Globals_test2.mq5. Il a été lancé avec un nombre différent de pistes. J'ai redémarré le terminal après chaque exécution pour supprimer les variables.
#property script_show_inputs //--- #include "CGlobalVar.mqh" input uint InpCnt=10000; // Number of variables //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- start value uint start=GetTickCount(); //--- for(uint idx=0;idx<InpCnt;idx++) { CGlobalVar gVar; //--- Create a temporary global var if(!gVar.Create("Test_var"+IntegerToString(idx+1),idx+0.15,true)) Alert("Error creating a global variable!"); } //--- finish value uint time=GetTickCount()-start; //--- to print PrintFormat("Creation of %d global variables took %d ms",InpCnt,time); }
Voici le résultat (Fig.2).
Fig.2. Temps passé à créer des variables globales temporaires
Le résultat d'un test similaire pour les variables globales complètes est présenté sur la Fig.3. Leur création ne prend pas beaucoup plus de temps.
La raison en est que ces variables sont enregistrées sur le disque dans le fichier gvariables.dat situé dans le dossier Profiles.
Fig.3. Temps passé à créer des variables globales complètes
Je ne pense pas qu'il soit nécessaire de créer autant de variables globales. J'ai fait cette évaluation par simple curiosité.
Dans le prochain passage, nous allons travailler avec un ensemble de variables globales.
3. Classe CGlobalVarList
Pour réguler le travail avec les variables globales, nous allons créer une classe liste de variables globales de type CGlobalVarList. Ce type de liste est un descendant de la classe de liste standard CList..
La déclaration de classe peut être présentée comme :
//+------------------------------------------------------------------+ //| Class CGlobalVarList | //+------------------------------------------------------------------+ class CGlobalVarList : public CList { //--- === Data members === --- private: ENUM_GVARS_TYPE m_gvars_type; //--- === Methods === --- public: //--- constructor/destructor void CGlobalVarList(void); void ~CGlobalVarList(void){}; //--- load/unload bool LoadCurrentGlobals(void); bool KillCurrentGlobals(void); //--- working with files virtual bool Save(const int _file_ha); virtual bool Load(const int _file_ha); //--- service void Print(const int _digs); void SetGvarType(const ENUM_GVARS_TYPE _gvar_type); //--- private: bool CheckGlobalVar(const string _var_name); };
Si des objets connectés avec des variables globales courantes doivent être inclus dans une liste de type CGlobalVarList, alors la méthode CGlobalVarList::LoadCurrentGlobals est utilisée.
//+------------------------------------------------------------------+ //| Load current global vars | //+------------------------------------------------------------------+ bool CGlobalVarList::LoadCurrentGlobals(void) { ENUM_GVARS_TYPE curr_gvar_type=this.m_gvars_type; int gvars_cnt=GlobalVariablesTotal(); //--- for(int idx=0;idx<gvars_cnt;idx++) { string gvar_name=GlobalVariableName(idx); if(this.CheckGlobalVar(gvar_name)) continue; //--- gvar properties double gvar_val=GlobalVariableGet(gvar_name); datetime gvar_time=GlobalVariableTime(gvar_name); CGlobalVar *ptr_gvar=new CGlobalVar(gvar_name,gvar_val,gvar_time); //--- control gvar type if(CheckPointer(ptr_gvar)==POINTER_DYNAMIC) { if(curr_gvar_type>GVARS_TYPE_ALL) { bool is_temp=ptr_gvar.IsTemporary(); //--- only full-fledged if(curr_gvar_type==GVARS_TYPE_FULL) {if(is_temp)continue;} //--- only temporary else if(curr_gvar_type==GVARS_TYPE_TEMP) {if(!is_temp)continue;} } //--- try to add if(this.Add(ptr_gvar)>-1) continue; } //--- return false; } //--- return true; }
Cette méthode lit toutes les variables globales présentes et les inclut dans la liste.
L'attribut m_gvars_type contrôle le type de la variable globale incluse. Est-ce une énumération de type ENUM_GVARS_TYPE :
//+------------------------------------------------------------------+ //| Enumeration for gvars type | //+------------------------------------------------------------------+ enum ENUM_GVARS_TYPE { GVARS_TYPE_ALL=-1, // all global GVARS_TYPE_FULL=0, // only full GVARS_TYPE_TEMP=1, // only temporary };
Supposons qu'avant l'initialisation de la liste CGlobalVarList, il y avait un ensemble de variables globales comme présenté sur la Fig.4.
Fig.4. Ensemble approximatif de variables globales
Nous allons vérifier si cet ensemble va être correctement traité par la liste. Pour effectuer une telle vérification, le script de test Globals_test3.mq5 sera créé.
#include "CGlobalVarList.mqh" //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { CGlobalVarList gvarList; gvarList.LoadCurrentGlobals(); PrintFormat("Number of variables in the set: %d",gvarList.Total()); }
De nouvelles variables globales (surlignées en jaune) sont apparues après le lancement du script, ce qui n'était pas censé se produire (Fig.5).
Fig.5. Nouvel ensemble de variables globales
Une chaîne a été imprimée sous la forme :
2014.10.21 11:35:00.839 Globals_test3 (AUDUSD.e,H1) Number of variables in the list: 10
C'est arrivé parce que dans la déclaration de la méthode CGlobalVarList::LoadCurrentGlobals, il y a une référence à la méthode CGlobalVar::Create.
Cela signifie qu'une nouvelle variable globale est créée dans la chaîne :
if(ptr_gvar.Create(gvar_name,gvar_val))
De plus, les index des variables globales changent à mesure que de nouvelles variables apparaissent. C'est ce qui crée la confusion.
Je recommanderais de remplacer la méthode CGlobalVar::Create par une méthode moins active. Un constructeur avec des paramètres doit être ajouté à la classe CGlobalVar pour que la variable puisse être prise en compte dans la liste.
Après la modification, la méthode CGlobalVarList::LoadCurrentGlobals ressemble à :
//+------------------------------------------------------------------+ //| Load current global vars | //+------------------------------------------------------------------+ bool CGlobalVarList::LoadCurrentGlobals(void) { int gvars_cnt=GlobalVariablesTotal(); //--- for(int idx=0;idx<gvars_cnt;idx++) { string gvar_name=GlobalVariableName(idx); double gvar_val=GlobalVariableGet(gvar_name); datetime gvar_time=GlobalVariableTime(gvar_name); CGlobalVar *ptr_gvar=new CGlobalVar(gvar_name,gvar_val,gvar_time); if(CheckPointer(ptr_gvar)==POINTER_DYNAMIC) if(this.Add(ptr_gvar)>-1) continue; //--- return false; } //--- return true; }
Le script fonctionne correctement une fois la méthode modifiée. L'enregistrement suivant est imprimé :
2014.10.21 11:38:04.424 Globals_test3 (AUDUSD.e,H1) Number of variables in the list: 6
Ensuite, nous allons ajouter des fonctionnalités permettant de supprimer et d'imprimer une liste.
Le script Globals_test3.mq5 ressemble maintenant à :
//--- #include "CGlobalVarList.mqh" //--- input ENUM_GVARS_TYPE InpGvarType=GVARS_TYPE_FULL; // Set gvar type //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { CGlobalVarList gvarList; //--- delete gvars gvarList.SetGvarType(InpGvarType); //--- load current gvars gvarList.LoadCurrentGlobals(); Print("Print the list before deletion."); gvarList.Print(10); //--- delete gvars if(gvarList.KillCurrentGlobals()) { Print("Print the screen after deletion."); gvarList.Print(10); } }
Nous allons compliquer la tâche en créant 10 variables globales différentes (Fig.6).
Fig.6. Diverses variables globales
Seules les variables complètes seront incluses dans notre liste gvarList . Ensuite, ils seront supprimés.
Le journal « Expert » contiendra les éléments suivants :
MG 0 11:05:01.113 Globals_test3 (AUDUSD.e,H1) Print the list before deletion. KL 0 11:05:01.613 Globals_test3 (AUDUSD.e,H1) OI 0 11:05:01.613 Globals_test3 (AUDUSD.e,H1) ---===Local list===--- QS 0 11:05:01.613 Globals_test3 (AUDUSD.e,H1) Global variable type: GVARS_TYPE_FULL RI 0 11:05:01.613 Globals_test3 (AUDUSD.e,H1) Total number of global variables: 10 EG 0 11:05:01.613 Globals_test3 (AUDUSD.e,H1) Number of global variables in current list: 5 RN 0 11:05:01.613 Globals_test3 (AUDUSD.e,H1) Gvar #1, name - gVar10_prog_test1_scr, value - 16.6400000000 KP 0 11:05:01.613 Globals_test3 (AUDUSD.e,H1) Gvar #2, name - gVar2_prog_test1_scr, value - 4.6400000000 GR 0 11:05:01.613 Globals_test3 (AUDUSD.e,H1) Gvar #3, name - gVar4_prog_test1_scr, value - 7.6400000000 RD 0 11:05:01.613 Globals_test3 (AUDUSD.e,H1) Gvar #4, name - gVar6_prog_test1_scr, value - 10.6400000000 LJ 0 11:05:01.613 Globals_test3 (AUDUSD.e,H1) Gvar #5, name - gVar8_prog_test1_scr, value - 13.6400000000 EH 0 11:06:18.675 Globals_test3 (AUDUSD.e,H1) Print the list after deletion. FS 0 11:06:19.003 Globals_test3 (AUDUSD.e,H1) JJ 0 11:06:19.003 Globals_test3 (AUDUSD.e,H1) ---===Local list===--- HN 0 11:06:19.003 Globals_test3 (AUDUSD.e,H1) Global variable type: GVARS_TYPE_FULL KH 0 11:06:19.003 Globals_test3 (AUDUSD.e,H1) Total number of global variables: 5 QP 0 11:06:19.003 Globals_test3 (AUDUSD.e,H1) Number of global variables in the current list: 0
La liste comprenant uniquement des variables globales complètes a été créée correctement.
Ensuite, il a été effacé et il ne restait plus que 5 variables temporaires dans le terminal (Fig.7).
Fig.7. Variables globales temporaires
La tâche prévue a été accomplie.
Dans la classeCGlobalVarList , des méthodes d'enregistrement de données dans le fichier et de téléchargement de données à partir du fichier ont également été implémentées.
4. Application pratique
Comme on le sait, MQL4/5 est un langage de programmation spécialisé. Il a été créé pour programmer des stratégies de trading. C'est pourquoi tout outil du langage est à considérer comme un moyen de formaliser une certaine idée de trading.
Il existe suffisamment d'exemples de liaison d'Expert Advisors avec des variables globales sur la plate-forme MQL5. Aujourd'hui, je suggère d'examiner de près la situation lorsqu'un contrôle sur la mise en œuvre du programme est requis.
Supposons qu'il existe un code du robot de trading «Globals_test_EA» basé sur une approche par module :
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { Comment(""); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { Main(); }
où le module principal ressemble à :
//+------------------------------------------------------------------+ //| Main module | //+------------------------------------------------------------------+ void Main(void) { //--- set flags for all modules for(int idx=0;idx<GVARS_LIST_SIZE;idx++) SetFlag(idx,false); //--- Check the trade possibility and connectivity //--- permission to trade if(TerminalInfoInteger(TERMINAL_TRADE_ALLOWED)) //--- connection to the trading server if(TerminalInfoInteger(TERMINAL_CONNECTED)) //--- permission to trade for the launched EA if(MQLInfoInteger(MQL_TRADE_ALLOWED)) { //--- 1) opening module Open(); //--- 2) closing module Close(); //--- 3) Trailing Stop module Trail(); } }
C'est-à-dire que le module principal comprend 3 constituants :
- module d'ouverture;
- module de fermeture;
- Module d'arrêt suiveur.
Nous devons maintenant créer des variables globales contrôlant les étapes d'exécution du programme.
Il y a trois étapes sous forme de modules. Deux points de contrôle sont utilisés pour chaque étape. Le premier point contrôle le début du travail du module et le second contrôle la fin du travail du module.
Les points de contrôle sont implémentés sous forme de variables globales.
Nous avons donc besoin de six variables globales portant les noms suivants :
//--- global variables: names string gVar_names[6]= { "gvarOpen_start","gvarOpen_finish", "gvarClose_start","gvarClose_finish", "gvarTrail_start","gvarTrail_finish" };
Les drapeaux de tous les modules sont définis au début de la fonction Main() et effacés dans chaque module séparé. Il va sans dire que nous ne parlons que de «propres» drapeaux. Par exemple, référons-nous au module Open() :
//+------------------------------------------------------------------+ //| Open module | //+------------------------------------------------------------------+ void Open(void) { Comment(curr_module+__FUNCTION__); //--- if(!IsStopped()) { //--- clear the module start flag SetFlag(0,true); //--- assume that the module operates for approximately 1.25 s { Sleep(1250); } //--- clear the module finish flag SetFlag(1,true); } }
A l'exécution du module, un commentaire indiquant que le programme travaille dans le bloc Open() apparaît dans la fenêtre graphe.
Ensuite, si la fermeture du programme n'a pas été forcée, le contrôle est passé à la fonction de mise/effacement d'un drapeau correspondant. Au cas où un drapeau ne s'effacerait pas dans l'un des points de contrôle, le module est alors considéré comme n'ayant pas terminé le travail.
Un schéma des étapes de suivi du travail de module avec des variables globales est présenté sur la figure 8.
Fig. 8. Modèle de la séquence d'indicateurs de traitement
Par exemple, l'Expert Advisor «Globals_test_EA» est attaché au graphique et fonctionne normalement.
Lorsque j'ai supprimé l'Expert Advisor du graphique, l'entrée suivante est apparue dans le journal :
2014.10.22 20:14:29.575 Globals_test_EA (EURUSD.e,H1) Program forced to terminate before execution: <<Open_finish>>
Par conséquent, la fin de l'Expert Advisor a eu lieu dans Open(). module.
Ouvrez la liste des variables globales en appuyant sur F3 (Fig.9).
Fig. 9. Variables globales pour l'Expert Advisor «Globals_test_EA»
D'après l'apparence de la liste, seul le drapeau responsable du début du travail du module Open() a été mis à zéro.
Il semble que des défauts puissent potentiellement être détectés lors d'échecs d'exécution de commandes liés aux positions d'ouverture, à leur fermeture et à leur maintenance.
Après une relance du robot sur la même carte, les informations suivantes seront affichées dans le journal :
RQ 0 20:28:25.135 Globals_test_EA (EURUSD.e,H1) Non-zero value for: <<Open_finish>> CL 0 20:28:25.135 Globals_test_EA (EURUSD.e,H1) Non-zero value for: <<Close_start>> DH 0 20:28:25.135 Globals_test_EA (EURUSD.e,H1) Non-zero value for: <<Close_finish>> ES 0 20:28:25.135 Globals_test_EA (EURUSD.e,H1) Non-zero value for: <<Trail_start>> RS 0 20:28:25.135 Globals_test_EA (EURUSD.e,H1) Non-zero value for: <<Trail_finish>>
De cette façon, nous recevons un avertissement en cas d'échec des étapes du programme. Cela conduit à une autre question. Que faire si ces étapes échouent ? C'est une autre histoire.
Conclusion
Dans cet article, j'ai démontré les capacités orientées objet du langage MQL5 pour créer des objets facilitant le travail avec les variables globales du terminal.
Un cas où des variables globales ont été utilisées comme points de contrôle pour la mise en œuvre des étapes du programme a servi d'exemple.
Comme toujours, les commentaires, suggestions et critiques constructives sont les bienvenues.
Traduit du russe par MetaQuotes Ltd.
Article original : https://www.mql5.com/ru/articles/1210





- Applications de trading gratuites
- Plus de 8 000 signaux à copier
- Actualités économiques pour explorer les marchés financiers
Vous acceptez la politique du site Web et les conditions d'utilisation