MQL5 Programming Basics: Global Variables of the Terminal
Introduction
In the MQL4/5 environment there is an interesting instrument - global variables of the client terminal. It allows to create a shared data storage ares for all programs of the terminal. Moreover, the lifetime of this area does not stop with the terminal closure. This article suggests using the Object Oriented Programming tools to get a clear idea what global variables of the terminal are.
Further in the article, global variables of the client terminal will be called "global variables" unless otherwise specified.
1. Global Variables, Functions
From a programmer's point of view, a global variable is a named memory area available for all working programs of a trading terminal. Novice programmers should note that if there are several terminals working simultaneously, each of them will have its own independent memory space for global variables. They are not going to overlap.
The language developers specify in the Documentation that there are 11 functions used for working with global variables.
Theory can be found in the "GlobalVariables" section of the MQL4 textbook.
In the next sections I shall use the Object-oriented programming instruments for the implementation of set tasks.
2. Class CGlobalVar
Guided by the ideas of the Object-oriented programming, let us create the class CGlobalVar, which will be directly responsible for the object of a global variable.
//+------------------------------------------------------------------+ //| 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); };
What does a class have to include? For a minimum attribute list, I would choose the following properties:
- name of a variable;
- value of a variable;
- creation time;
- last call time;
- feature of a temporary variable.
As for the methods, they look as follows:
- creation;
- deletion;
- check for existence;
- setting a new value;
- setting a new value by condition;
- receiving a name;
- receiving a value;
- receiving a temporary variable flag.
The CGlobalVar::GetValue method should be mentioned separately. It is a template method. It returns the data type for the variable value that a user sets as an argument.
The issue here is that in MQL a function can be typed only by parameters. Therefore a fake parameter needs to be added.
Let us create the test script Globals_test1.mq5 where we shall work with objects of the CGlobalVar type.
#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); } }
A global variable is created as follows:
gVar1.Create("Gvar1",3.123456789101235,true)
The first argument is the basic component of the future variable name ("Gvar1"), the second argument is the value of (3.123456789101235) and the third argument is the feature showing that the variable is going to be temporary (true).
The name of the variable is created by adding the name and the type of the program to the basic component.
In my case it is:
- Gvar1 - the basic component;
- prog_Globals_test1 - program where the variable was created (its name is Globals_test1);
- program type is - scr (script).
Upon pressing F3, the following entry should appear in the list of global variables in the MetaTrader 5 window:
Fig.1. Value of the Test_temp_var1_prog_Globals_test1_scr variable is equal to 3.18
On its launch and successful implementation, the following entries get printed in the "Experts" journal:
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
Different data types of the variable value are printed in the journal.
If the MetaTrader 5 terminal gets restarted, then the Gvar1_temp_prog_Globals_test1_scr variable disappears from the list of global variables. It happens because the variable was temporary and it lived while the terminal was open.
In MQL4/5, at receiving data on the global variable, there is no way of finding out if the variable is temporary or not. Maybe the easiest way of identifying a temporary variable is adding a key to the name of the variable. For instance, it can be the suffix "temp" in the name of the variable. However, the necessity to control creating the name of the global variable is a distinct disadvantage of this approach, especially if such variables are created by other programs not using the CGlobalVar class.
At some point I wanted to know how many global variables could be created and how quickly.
I slightly changed the previous script and named it Globals_test2.mq5. It was launched with a different number of runs. I restarted the terminal after every run to delete 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); }
Here is the result (Fig.2).
Fig.2. Time spent on creating temporary global variables
The result of a similar test for full global variables is presented on Fig.3. Their creation does not take a lot longer.
The reason behind it is that these variables are saved to the disk in the gvariables.dat file located in the Profiles folder.
Fig.3. Time spent on creating full global variables
I don't think that there is a need to create so many global variables. I conducted this assessment simply out of curiosity.
In the next passage we are going to work with a set of global variables.
3. CGlobalVarList Class
To regulate the work with global variables, we are going to create a list class of global variables of the CGlobalVarList type. This type of list is a descendant of the standard list class CList.
Class declaration can be presented as:
//+------------------------------------------------------------------+ //| 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); };
If objects connected with current global variables are to be included in a list of the CGlobalVarList type, then the CGlobalVarList::LoadCurrentGlobals method is used.
//+------------------------------------------------------------------+ //| 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; }
This method reads all present global variables and includes them to the list.
The m_gvars_type attribute controls the type of the included global variable. Is it an enumeration of the ENUM_GVARS_TYPE 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 };
Let us assume that before the CGlobalVarList list initialization, there was a set of global variables as presented on Fig.4.
Fig.4. Approximate set of global variables
We are going to check if this set is going to be processed by the list correctly. To carry out such a check, the Globals_test3.mq5 test script will be created.
#include "CGlobalVarList.mqh" //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { CGlobalVarList gvarList; gvarList.LoadCurrentGlobals(); PrintFormat("Number of variables in the set: %d",gvarList.Total()); }
New global variables (highlighted in yellow) appeared after the script launch, which was not supposed to happen (Fig.5).
Fig.5. New set of global variables
A string was printed as:
2014.10.21 11:35:00.839 Globals_test3 (AUDUSD.e,H1) Number of variables in the list: 10
It happened because in the declaration of the CGlobalVarList::LoadCurrentGlobals method there is a reference to the CGlobalVar::Create method.
That means that a new global variable is created in the string:
if(ptr_gvar.Create(gvar_name,gvar_val))
In addition, the indexes of global variables are changing as new variables appear. That is what causes the confusion.
I would recommend to substitute the CGlobalVar::Create method with a less active one. A constructor with parameters has to be added to the CGlobalVar class so the variable can be taken into account in the list.
After the modification the CGlobalVarList::LoadCurrentGlobals method looks like:
//+------------------------------------------------------------------+ //| 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; }
The script works correctly after the method has been modified. The following record gets printed:
2014.10.21 11:38:04.424 Globals_test3 (AUDUSD.e,H1) Number of variables in the list: 6
Then we are going to add features allowing deleting and printing a list.
Now the Globals_test3.mq5 script looks like:
//--- #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); } }
We are going to complicate the task by creating 10 diverse global variables (Fig.6).
Fig.6. Diverse global variables
Only full variables will be included to our list gvarList . Then they will get deleted.
The "Expert" journal will contain the following:
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
The list including only full global variables was created correctly.
Then it was cleared and only 5 temporary variables were left in the terminal (Fig.7).
Fig.7. Temporary global variables
The intended task was accomplished.
In the CGlobalVarList class, methods of saving data to the file and downloading data from the file were also implemented.
4. Practical Application
As is well known, MQL4/5 is a specialized programming language. It was created for programming trading strategies. That is why any tool of the language is to be considered as means for formalizing a certain trading idea.
There are enough examples of linking Expert Advisors with global variables on the MQL5 platform. Today I suggest to take a close look at the situation when control over the program implementation is required.
Let's assume that there is a code of the "Globals_test_EA" trading robot based on a module approach:
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { Comment(""); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { Main(); }
where the main module looks like:
//+------------------------------------------------------------------+ //| 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(); } }
That is the main module includes 3 constituents:
- opening module;
- closing module;
- Trailing Stop module.
Now we need to create global variables controlling stages of program execution.
There are three stages in the form of modules. Two control points are used for each stage. The first control point is controlling the beginning of the module work and the second one is controlling the end of the module work.
Control points are implemented in the form of global variables.
So, we require six global variables with the following names:
//--- global variables: names string gVar_names[6]= { "gvarOpen_start","gvarOpen_finish", "gvarClose_start","gvarClose_finish", "gvarTrail_start","gvarTrail_finish" };
Flags for all modules are set in the beginning of the Main() function and cleared in every separate module. It goes without saying that we are talking about "own" flags only. For example, let's refer to the Open() module:
//+------------------------------------------------------------------+ //| 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); } }
At the module execution, a comment that the program is working in the Open() block appears in the chart window.
Then, if the program was not forced to close, control is passed to the function of setting/clearing a correspondent flag. In case a flag failed to clear in any of the control point, the module is then considered not to have finished work.
A pattern of tracking stages of module work with global variables is presented on Fig. 8.
Fig. 8. Pattern of the processing flag sequence
For example, the "Globals_test_EA" Expert Advisor is attached to the chart and operates normally.
When I deleted the Expert Advisor from the chart, the following entry appeared in the journal:
2014.10.22 20:14:29.575 Globals_test_EA (EURUSD.e,H1) Program forced to terminate before execution: <<Open_finish>>
Therefore terminating the Expert Advisor took place in the Open(). module.
Open the list of global variables by pressing F3 (Fig.9).
Fig. 9. Global variables for the "Globals_test_EA" Expert Advisor
By the look of the list, only the flag responsible for the beginning of the Open() module work was zeroized.
It looks like faults can potentially be detected at command execution failure connected with opening positions, their closure and maintenance.
After a relaunch of the robot on the same chart, the following information will be displayed in the 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>>
This way we receive a warning about a failure of the program stages. It leads to another question. What can be done if those stages failed? That is a different story.
Conclusion
In this article I demonstrated object-oriented capabilities of the MQL5 language for creating objects facilitating work with global variables of the terminal.
A case when global variables were used as control points for implementation of program stages served an example.
As always, comments, suggestions and constructive criticism are welcome.
Translated from Russian by MetaQuotes Ltd.
Original article: https://www.mql5.com/ru/articles/1210
- Free trading apps
- Over 8,000 signals for copying
- Economic news for exploring financial markets
You agree to website policy and terms of use
@Dennis Kirichenko
Hi Dennis,
I have read this article in an attempt to gain a better understanding of global variables of the platform and their functionality. How is creating or changing a global variable going to impact upon the trading environment?
Are you able to explain what the numerical value of the global variables represent?
For quite sometime I have been researching and trying to understand the global variables of the terminal, though as yet I have not understood their usage and effect of them. If possible could you please give me a simple example of how creation of a global variable can have a positive result on the balance of my trading account?
Thank you.
Regards,
Dale.