I hesitate between using the Global Variables and an ini file.

 
Hello,

I want to save data between two starts of the same EA.
I hesitate between using the Global Variables and an ini file.
What solution do you use in general?
Documentation on MQL5: Global Variables of the Terminal
Documentation on MQL5: Global Variables of the Terminal
  • www.mql5.com
Global variables are kept in the client terminal for 4 weeks since the last access, then they will be deleted automatically. An access to a global variable is not only setting of a new value, but reading of the global variable value, as well.
 
Pierre Rougier:
Hello,

I want to save data between two starts of the same EA.
I hesitate between using the Global Variables and an ini file.
What solution do you use in general?

The easiest is using GlobalVariables if you need to save numerical values or short text strings

 
Fernando Morales:

The easiest is using GlobalVariables if you need to save numerical values or short text strings

I guess that Terminal Global Variables could only handle Double content...

 
I want to save data between two starts of the same EA.
I hesitate between using the Global Variables and an ini file.
What solution do you use in general?

If I wanted to transfer the state of my EA to a different machine, I would use some sort of file.

I might not use an .ini file, though. It depends upon the nature of the data being saved. INI files are useful for key,value pairs, but they're not as useful for arrays of data. For arrays of data I would use some sort of CSV. I notice in the example for the INI library that he stores order data in the .ini file—I would never do that.

As it pointed out above, GlobalVariables could be a simple way to save certain data. I personally wouldn't save EA state data there. I think of Terminal Global Variables as "shared memory" between EA's, indicators, and scripts. So to me, logically, it shouldn't go there. That's not to say it isn't possible, though.

As for what I currently use: I don't currently save state data. When my EA starts up, it pulls deal data using HistoryDealsTotal(), HistorySelect(), etc. Apart from that, my EA starts with a clean slate every time.

 

It depends on the application.

If i need to check if something already exists, for example a magic number, or i want to quickly reload some saved values i definitely use Global Vars.

I would probably not use it when i want to save larger amounts of data, for example when i generate CSV or HTML that's all written to file.

So i would say it depends on the amount of data you want to save.

 
Minions Labs:

I guess that Terminal Global Variables could only handle Double content...

I have on occasions used Terminal GVs to store strings as part of the GV's name

 
It really depends on the scenario. I agree that CSV is the appropriate way to save state for simple static values, but if you need to save/load dynamic state then you could make your own expert class that inherits CObject and implement the Save/Load methods which ultimately serialize state to a binary file. Alternatively, you could use the JAson module and serialize state to a JSON file. 
 
Thanks to all.

Pierre
 

Global variables can only handle doubles, and imho, the internal handling of global vars is based on SQL and not proper implemented. Since I dropped extensive usage of global variables, I have no more timeouts and no more crashes of MT4/MT5 at all.

Ini files, in view of speed, require a lot of string operations and by this lot of memory allocation - not a good idea. 

I implemented a class for variable exchange, doubles and strings, in four different ways:

1. Exchange data between indicators/EA within a chart

2. Within the same MT4 instance

3. Between all MT4 instances

4. Between any MT4 and MT5 instance

The class also handles the lifetime of a variable automatically. 

This is the description header. If you´re interested in the whole code, drop me a private message. The code is a part of a commercial product and may be used for free by anyone, no matter if you purchased the product or not, but I am not allowed to mention the name here. It´s not fully described in the documentation, but I am sure it´s not that hard to figure out how it works. One example:

For writing, just use:

__LocalVars.SetString("MyStrVar","Sample text");

__LocalVars.SetDouble("MyDblVar",1.234);

For reading:

string text = __LocalVars.GetString("MyStrVar");

double n = __LocalVars.GetDouble("MyDblVar");

Props:

datetime d = __LocalVars.GetTC("MyStrVar");

bool b = __LocalVars.IsExist("MyStrVar");


Actually that´s it. 


/*
   Functions
   ---------
   
   bool     InitByChart (string prefix=NULL, CHART id=NULL, bool commonfolder=false)
   bool     Init (string filename, bool commonfolder)

   bool     SetDouble (string name, double value, bool permanent=false)
   bool     SetString (string name, string value, bool permanent=false)
   double   GetDouble(string name, bool reload=true)
   string   GetString(string name, bool reload=true)

   bool     IsDefined(string name, bool reload=true)
   bool     Delete(string name, bool reload=true)
   bool     DeleteAll(string prefix=NULL, bool reload=true)

   bool     Update()
   datetime GetTC(string name)

   Prop-Functions
   --------------
   bool     AutoUpdate(bool flag)      - default ON
   bool     AutoUpdate()       
   bool     Display(bool flag)         - default OFF
   bool     Display()
   bool     Prefix(string value=NULL)  - If NULL, a unique prefix is used
   string   Prefix()
   long     GetUniqueId                - Returns a unique identifier


   Predefined objects
   ------------------
   
   __ChartVars     - database with variables in the sandbox of the chart
   __LocalVars     - database with variables limited to the current instance of MT4/MT5
   __GlobalVars    - global data, valid for all instances of either MT4 or MT5
   __GlobalVarsMTX - global data, valid for all instances of any MT4 and MT5


   Sample without initialization / direct usage
   -----------------
   __ChartVars.SetDouble("Value",1)
   double v=__ChartVars.GetDouble("Value");
   

   Architecture of var database
   ----------------------------
   
   int               - number of records (m_cnt)
   SDBVarsData[]     - main records 0-max (m_data[])
   int[]             - string index table (m_sx[])
   string[]          - string records 0-max


   Comments
   --------
   
   The focus is performance. Therefore the main table in SDBVarsData[] is always compressed. 
   When a record is removed, the remaining records are shifted and on every access, the
   main table is loaded at once into memory. 
   
   But, shifting strings would decrease speed due to too many disk accesses. Therefore
   the strings are allocated via a string-index table. This way, a record of the main table
   which points to a string, only has the index to the string and when it is shifted during
   compression, the string itself remains at the same physical adress. 
   
   

*/
 

Here's a simple and efficient way to save/load variables. 


#property strict

#include <Arrays\List.mqh>
//+------------------------------------------------------------------+
class VariableData : public CObject
{
protected:
   string      m_name;
   string      m_value;
public:
               VariableData(){}
               VariableData(string name):m_name(name){}
   template<typename T>
   void        operator = (T value){ m_value=(string)value;}
   template<typename T>
   T           value()  { return (T)m_value; }
   string      name()   { return m_name;     }
   virtual bool Save(const int file_handle) override{
      if(file_handle==INVALID_HANDLE)
         return(false);
      int len_name = StringLen(m_name);
      int len_value = StringLen(m_value);
      if(   FileWriteInteger(file_handle,len_name) != sizeof(int)
         || FileWriteString(file_handle, m_name, len_name) == 0
         || FileWriteInteger(file_handle,len_value) != sizeof(int)
         || FileWriteString(file_handle, m_value, len_value) == 0
      )
         return false;
      return true;
   }
   virtual bool Load(const int file_handle) override{
      if(file_handle==INVALID_HANDLE)
         return(false);
      m_name = FileReadString(file_handle, FileReadInteger(file_handle));
      m_value = FileReadString(file_handle, FileReadInteger(file_handle));
      return true;
   }
};
//+------------------------------------------------------------------+
class VariableList : public CList
{
   public: virtual CObject *CreateElement(void) { return new VariableData(); }
};
//+------------------------------------------------------------------+
class PersistentVariables : public CObject
{
   VariableList      m_list;
   string            m_file_name;
public:
   PersistentVariables(string file_name):m_file_name(file_name){}
   VariableData *operator[](string name){
      VariableData *vd = m_list.GetFirstNode();
      for(;CheckPointer(vd); vd=vd.Next())
         if(vd.name() == name)
            return vd;
      vd = new VariableData(name);
      m_list.Add(vd);
      return vd;
   }
   bool load(){
      int h = FileOpen(m_file_name, FILE_READ|FILE_BIN);
      m_list.Clear();
      bool res = m_list.Load(h);
      FileClose(h);
      return res;
   }
   bool save(){
      int h = FileOpen(m_file_name, FILE_WRITE|FILE_BIN);
      bool res = m_list.Save(h);
      FileClose(h);
      return res;
   }
};
//+------------------------------------------------------------------+
void OnStart()
{
   PersistentVariables vars("saved_variables.bin");
   int test_int        = 444;
   double test_double  = 44.4;
   string test_string  = "Hello world!";
   vars["test_int"]    = test_int;
   vars["test_double"] = test_double;
   vars["test_string"] = test_string;
   if(vars.save())
      Print("vars saved without errors.");
   
   if(vars.load())
      Print("vars loaded without errors.");
      
   test_int    = vars["test_int"].value<int>();
   test_double = vars["test_double"].value<double>();
   test_string = vars["test_string"].value<string>();
   printf("results: int=%d, double=%.2f, string=%s",
      test_int, test_double, test_string);
}
 
nicholi shen:

Here's a simple and efficient way to save/load variables. 


Thank you so much.

Reason: