Download MetaTrader 5

MQL5 Programming Basics: Global Variables of the Terminal

19 November 2014, 10:48
Dennis Kirichenko
0
3 590

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:

  1. Gvar1 - the basic component;
  2. prog_Globals_test1 - program where the variable was created (its name is Globals_test1);
  3. 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

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

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

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

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

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

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

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:

  1. opening module;
  2. closing module;
  3. 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

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

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 Software Corp.
Original article: https://www.mql5.com/ru/articles/1210

Attached files |
globals_test1.mq5 (3.67 KB)
globals_test2.mq5 (2.92 KB)
globals_test3.mq5 (2.59 KB)
cglobalvar.mqh (20.52 KB)
cglobalvarlist.mqh (18.74 KB)
globals_test_ea.mq5 (13.01 KB)
Step on New Rails: Custom Indicators in MQL5 Step on New Rails: Custom Indicators in MQL5

I will not list all of the new possibilities and features of the new terminal and language. They are numerous, and some novelties are worth the discussion in a separate article. Also there is no code here, written with object-oriented programming, it is a too serous topic to be simply mentioned in a context as additional advantages for developers. In this article we will consider the indicators, their structure, drawing, types and their programming details, as compared to MQL4. I hope that this article will be useful both for beginners and experienced developers, maybe some of them will find something new.

Here Comes the New MetaTrader 5 and MQL5 Here Comes the New MetaTrader 5 and MQL5

This is just a brief review of MetaTrader 5. I can't describe all the system's new features for such a short time period - the testing started on 2009.09.09. This is a symbolical date, and I am sure it will be a lucky number. A few days have passed since I got the beta version of the MetaTrader 5 terminal and MQL5. I haven't managed to try all its features, but I am already impressed.

Using text files for storing input parameters of Expert Advisors, indicators and scripts Using text files for storing input parameters of Expert Advisors, indicators and scripts

The article describes the application of text files for storing dynamic objects, arrays and other variables used as properties of Expert Advisors, indicators and scripts. The files serve as a convenient addition to the functionality of standard tools offered by MQL languages.

How to create an indicator of non-standard charts for MetaTrader Market How to create an indicator of non-standard charts for MetaTrader Market

Through offline charts, programming in MQL4, and reasonable willingness, you can get a variety of chart types: "Point & Figure", "Renko", "Kagi", "Range bars", equivolume charts, etc. In this article, we will show how this can be achieved without using DLL, and therefore such "two-for-one" indicators can be published and purchased from the Market.