English Русский 中文 Deutsch 日本語 Português 한국어 Français Italiano Türkçe
Principios de programación en MQL5 - Variables globales del terminal

Principios de programación en MQL5 - Variables globales del terminal

MetaTrader 5Ejemplos | 3 noviembre 2014, 14:14
1 462 0
Denis Kirichenko
Denis Kirichenko

Introducción

En el entorno MQL4/5 hay un instrumento interesante, las variables globales del terminal de cliente. Este instrumento permite crear una cierta zona común para el guardado de datos para todos los programas del terminal. Además, la vida de esta zona no se interrumpe al cerrar el terminal. En este artículo, propongo utilizar los medios de la POO, para "aclararnos" con las variables globales del programa.

En el texto del artículo, en caso de que se diga lo contrario, las variables globales del terminal de cliente se llamarán para mayor brevedad "varibales globales".


1. Varibales globales, funciones

Desde el punto de vista del programador, una varibale global es una parte de la memoria con denominación, que está accesible para todos los programas que funcionen en un terminal comercial. Seguramente merezca la pena llamarle la atención a los principiantes sobre el hecho de que si funcionan varios terminales simultáneamente, entonces cada uno de ellos tendrá su espacio independiente en el memoria para las variables globales. Dichos espacios no se cruzarán.

Los desarrolladores del lenguaje, en la documentación indican que hay 11 funciones que procesan las necesidades de las variables globales.

En el manual de MQL4 existe el apartado "Variables GlobalVariables", donde se estudia la teoría.

En los siguientes apartados usaremos los medios de la POO para la ejecución de las tareas marcadas.


2. Clase CGlobalVar

Guiándonos por las ideas de la POO, creamos la clase CGlobalVar, que será directamente responsable del objeto de la variable global.

//+------------------------------------------------------------------+
//| 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);
  };

¿Qué debe incluir dentro de sí una clase? Para conseguir una lista mínima de atributos, yo elegiría las siguientes propiedades:

  • el nombre de la variable;
  • el valor de la variable;
  • la hora de creación;
  • la hora de la última llamada;
  • el signo de la variable temporal.

En lo que respecta a los métodos, estos se muestran de la siguiente forma:

  • creación;
  • eliminación;
  • comprobación de la existencia;
  • establecimiento del nuevo valor;
  • establecimiento del nuevo valor según una condición;
  • obtención del nombre;
  • obtención del del valor;
  • obtención de la etiqueta de la variable temporal.

Merece la pena decir unas palabras sobre el método CGlobalVar::GetValue. Se trata de un método de plantilla. Retornará aquel tipo de datos para el valor de la variable que el usuario establezca en calidad de argumento.

El asunto es que en MQL solo se puede tipificar una función por sus parámetros. Por eso hay que añadir un parámetro falso.

Creamos el script textual Globals_test1.mq5, en el que trabajaremos con los objetos de tipo 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);
     }
  }

Una variable global se crea de la siguiente manera:

gVar1.Create("Gvar1",3.123456789101235,true)

Como primer argumento actúa la parte básica del nombre de la futura variable ("Gvar1"), como segunda, el valor (3.123456789101235), y como tercera el signo que indica que la variable será temporal (true).

El nombre para la variable se crea de esta forma: a la parte básica se añaden el nombre del programa y el tipo de programa.

En mi caso sería:

  1. Gvar1 es la parte básica;
  2. prog_Globals_test1 es el programa donde fue creada la variable (de nombre Globals_test1);
  3. el tipo de programa - scr (script).

En la lista de variables globales, después de pulsar F3 en la ventana del terminal MetaTrader 5, deberá aparecer el siguiente mensaje:

Fig. 1. El valor de la variable Test_temp_var1_prog_Globals_test1_scr es igual a 3.18

Fig. 1. El valor de la variable Test_temp_var1_prog_Globals_test1_scr es igual a 3.18

Al iniciarlo y ejecutarlo con éxito en el registro "Expertos" aparecerán las siguientes líneas:

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

En el registro se muestran para imprimir los diferentes tipos de datos del valor de la variable.

Si reiniciamos el terminal MetaTrader 5, entones la variable Gvar1_temp_prog_Globals_test1_scr desaparecerá del conjunto de variables globales. Esto sucede en vista de que la variable era una variable, es decir, existió hasta que se abrió el propio terminal.

En MQL4/5, al obtener los datos sobre la variable global no se puede saber si la variable es temporal o no. Seguramente, el método más simple para identificar la variable temporal pueda ser una cierta clave en el nombre de la propia variable. Se puede añadir, por ejemplo, el sufijo, "temp" al nombre de la variable. Un defecto de este enfoque es que sería necesario controlar el nombre de la variable global al crearla. Sobre todo si esas variables son creadas con otros programas que no utilicen la clase CGlobalVar.

Me resultó interesante, ¿cuántas variables se pueden crear, y cómo de rápido?

Cambié un poco la versión anterior del script y lo puse en marcha con un número diferente de pasadas, Globals_test2.mq5. Sí, después de cada intento reiniciaba el terminal, para eliminar las variables.

#property script_show_inputs
//---
#include "CGlobalVar.mqh"

input uint InpCnt=10000; // Cantidad de 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 al crear la variable!");
     }
//--- finish value
   uint time=GetTickCount()-start;
//--- to print
   PrintFormat("La creación %d variables globales ha tardado %d msec",InpCnt,time);
  }

Esto es lo que ha resultado (fig.2).

Fig.2. Gasto de tiempo en la creación de variables globales temporales

Fig.2. Gasto de tiempo en la creación de variables globales temporales

Los resultados de un test análogo para la ejecución de variables globales los podemos ver en la fig.3. En su reación se invierte un poco más de tiempo.

Eso sucede así porque estas variables se guardan en el disco, en el archivo gvariables.dat, que se encuentra en la carpeta Profiles.

Fig.3. Gasto temporal en la creación de variables globales plenamente funcionales

Fig.3. Gasto temporal en la creación de variables globales plenamente funcionales

Creo que no es necesario crear tantas variables globales. He realizado los cálculos por curiosidad.

En el siguiente apartado trabajaremos con un conjunto de variables globales.


3. Clase CGlobalVarList

Para que haya orden durante el trabajo con las variables globales, crearemos una clase de lista de las variables globales del tipo CGlobalVarList. Este tipo de lista será heredero de la lista estándar de clase CList.

La determinación de la clase puede mostrarse 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);
  };

Si hay que incluir en una lista de tipo CGlobalVarList objetos relacionados con la variable global actual, entonces se usa el método CGlobalVarList::LoadCurrentGlobals.

//+------------------------------------------------------------------+
//| 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;
  }

Este método calcula todas las variables globales existentes y las incluye en el conjunto de la lista.

El atributo m_gvars_type controla el aspecto de la variable global incliuda. Se trata de una enumeración del tipo ENUM_GVARS_TYPE:

//+------------------------------------------------------------------+
//| Enumeration for gvars type                                       |
//+------------------------------------------------------------------+
enum ENUM_GVARS_TYPE
  {
   GVARS_TYPE_ALL=-1,  // todas las globales
   GVARS_TYPE_FULL=0,  // solo las plenamente funcionales
   GVARS_TYPE_TEMP=1,  // solo las temporales
  };

Supongamos que antes de incializar la lista CGlobalVarList tuviéramos esta serie de variables globales (fig.4)

Fig.4. Listado aproximado de variables globales

Fig.4. Listado aproximado de variables globales

Veamos si es posible procesar este listado con la lista correctamente. Para realizar la comprobación creamos el script textual Globals_test3.mq5.

#include "CGlobalVarList.mqh"
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   CGlobalVarList gvarList;
   gvarList.LoadCurrentGlobals();   
   PrintFormat("Cantidad de variables en la lista: %d",gvarList.Total());
  }

Después de iniciar el script han aparecido nueveas variables globales (las he destacado en amarillo), lo que no se presuponía, en principio (fig.5).

Fig.5. Nuevo listado de variables globales

Fig.5. Nuevo listado de variables globales

Y la línea se ha imprimido así:

2014.10.21 11:35:00.839       Globals_test3 (AUDUSD.e,H1)              Cantidad de variables en la lista: 10

El asunto es que en la determinación del método CGlobalVarList::LoadCurrentGlobals hay una llamada al método CGlobalVar::Create.

Es decir, se crea una nueva variable global en la línea:

if(ptr_gvar.Create(gvar_name,gvar_val))

Además, los índices de las propias variables globales se cambian conforme van apareciendo nuevas variables. De ahí, que se produzca tanta confusión.

En definitiva, hay que sustituir el método CGlobalVar::Create por alguno otro menos activo. Se deberá añadir un constructor con los parámetros a la clase CGlobalVar, para que la variable pueda contarse en la lista.

Después de cambiar el método CGlobalVarList::LoadCurrentGlobals, tiene el siguiente aspecto:

//+------------------------------------------------------------------+
//| 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;
  }

Después de cambiar el método, el script funciona de manera normal. Obtenemos el siguiente mensaje:

2014.10.21 11:38:04.424      Globals_test3 (AUDUSD.e,H1)              Cantidad de variables en la lista: 6

Añadimos la posibilidad de eliminar la impresión de la lista.

Ahora el script Globals_test3.mq5 tiene el aspecto siguiente:

//---
#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("Imprimir lista antes de la eliminación.");
   gvarList.Print(10);
//--- delete gvars
   if(gvarList.KillCurrentGlobals())
     {
      Print("Imprimir lista después de la eliminación.");
      gvarList.Print(10);
     }
  }

Vamos a complicar un poco la tarea. Crearemos 10 variables globales de diferente tipo (fig.6).

Fig.6. Variables globales de diferente tipo

Fig.6. Variables globales de diferente tipo

En nuestra propia lista gvarList , vamos a cargar solo las variables plenamente funcionales. Y luego las eliminaremos físicamente.

En el registro "Expertos", obtendremos la imagen siguiente:

MG      0       11:05:01.113    Globals_test3 (AUDUSD.e,H1)     Imprimir lista antes de la eliminación.
KL      0       11:05:01.613    Globals_test3 (AUDUSD.e,H1)     
OI      0       11:05:01.613    Globals_test3 (AUDUSD.e,H1)     ---===Lista local===---
QS      0       11:05:01.613    Globals_test3 (AUDUSD.e,H1)     Tipo de variables globales: GVARS_TYPE_FULL
RI      0       11:05:01.613    Globals_test3 (AUDUSD.e,H1)     Variables globales en total: 10
EG      0       11:05:01.613    Globals_test3 (AUDUSD.e,H1)     Variables globales en la lista actual: 5
RN      0       11:05:01.613    Globals_test3 (AUDUSD.e,H1)     Gvar #1, nombre - gVar10_prog_test1_scr, valor - 16.6400000000
KP      0       11:05:01.613    Globals_test3 (AUDUSD.e,H1)     Gvar #2, nombre - gVar2_prog_test1_scr, valor - 4.6400000000
GR      0       11:05:01.613    Globals_test3 (AUDUSD.e,H1)     Gvar #3, nombre - gVar4_prog_test1_scr, valor - 7.6400000000
RD      0       11:05:01.613    Globals_test3 (AUDUSD.e,H1)     Gvar #4, nombre - gVar6_prog_test1_scr, valor - 10.6400000000
LJ      0       11:05:01.613    Globals_test3 (AUDUSD.e,H1)     Gvar #5, nombre - gVar8_prog_test1_scr, valor - 13.6400000000
EH      0       11:06:18.675    Globals_test3 (AUDUSD.e,H1)     Imprimir la lista después de la eliminación.
FS      0       11:06:19.003    Globals_test3 (AUDUSD.e,H1)     
JJ      0       11:06:19.003    Globals_test3 (AUDUSD.e,H1)     ---===Lista local===---
HN      0       11:06:19.003    Globals_test3 (AUDUSD.e,H1)     Tipo de variables globales: GVARS_TYPE_FULL
KH      0       11:06:19.003    Globals_test3 (AUDUSD.e,H1)     Variables globales en total: 5
QP      0       11:06:19.003    Globals_test3 (AUDUSD.e,H1)     Variables globales en la lista actual: 0

Se ha creado correctamente una lista que incluye solo las variables globales plenamente funcionales.

A continuación, ha sido vaciada y quedan 5 variables globales en el terminal (fig.7).

Fig.7. Variables globales temporales

Fig.7. Variables globales temporales

Es decir, se ha cumplido con lo pensado.

En la clase CGlobalVarList se han implementado los métodos para guardar datos en el archivo y cargar datos desde el archivo.


4. Sentido práctico

Como ya sabemos, MQL4/5 es un lenguaje especializado de programación. Se creó para programar estrategias comerciales. Por eso, cualquier mecanismo en el lenguaje debe ser estudiado como un medio para formalizar una cierta idea comercial concreta.

Existen muchos ejemplos de "conexión" de asesores por medio de variables globales en un recurso MQL5, por eso se propone estudiar la situación cuando hay que controlar la ejecución de un programa.

Supongamos que tenemos el siguiente código de un robot comercial "Globals_test_EA", que usa el enfoque modular:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   return INIT_SUCCEEDED;
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   Comment("");
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   Main();
  }

donde el módulo principal tiene el aspecto siguiente:

//+------------------------------------------------------------------+
//| Main module                                                      |
//+------------------------------------------------------------------+
void Main(void)
  {
//--- establecer las etiquetas para todos los módulos
   for(int idx=0;idx<GVARS_LIST_SIZE;idx++)
      SetFlag(idx,false);

//--- Comprobando si es posible comerciar y la existencia de conexión
//--- permiso para comerciar
   if(TerminalInfoInteger(TERMINAL_TRADE_ALLOWED))
      //--- conexión al servidor comercial
      if(TerminalInfoInteger(TERMINAL_CONNECTED))
         //--- permiso para comerciar con el asesor iniciado
         if(MQLInfoInteger(MQL_TRADE_ALLOWED))
           {
            //--- 1) módulo de apertura
            Open();
            //--- 2) módulo de cierre
            Close();
            //--- 3) módulo de trail
            Trail();
           }
  }

Es decir, el principal incluye 3 componentes:

  1. el módulo de apertura;
  2. el módulo de cierre;
  3. el módulo de trail;

Ahora se pueden crear variables globales que controlen las etapas de ejecución del programa.

En total habrá 3 etapas configuradas en módulos. Para cada etapa actúan conjuntamente 2 puntos de control. El primero controlará el comienzo del funcionamiento del módulo, y el segundo, la finalización.

Los puntos de control se implementan en forma de variables globales.

En total se necesitan 6 variables con nombre:

//--- variables globales: nombres
string gVar_names[6]=
  {
   "gvarOpen_start","gvarOpen_finish",
   "gvarClose_start","gvarClose_finish",
   "gvarTrail_start","gvarTrail_finish"
  };

Las etiquetas para todos los módulos se establecen al principio de la función Main(), y se desechan en cada módulo por separado. Y, naturalmente, solo las "propias". Por ejemplo, recurrimos al módulo Open():

//+------------------------------------------------------------------+
//| Open module                                                      |
//+------------------------------------------------------------------+
void Open(void)
  {
   Comment(curr_module+__FUNCTION__);
//---
   if(!IsStopped())
     {
      //--- desechar la etiqueta de incio del módulo
      SetFlag(0,true);

      //--- supongamos que el módulo funciona cerca de 1.25 seg
        {
         Sleep(1250);
        }
      //--- desechar la etiqueta de finalización del módulo
      SetFlag(1,true);
     }
  }

Al ejecutar el módulo en la ventana del gráfico, aparecerán comentarios referentes a que el programa está funcionando en el bloque Open().

A continuación, si el programa no se ha finalizado a la fuerza, el control pasa a la función de establecimiento/desechado de la etiqueta correspondiente. En caso de que la etiqueta no se haya podido desechar en cierto punto, consideraremos que el módulo no ha finalizado su trabajo.

De un modo esquemático, mostramos el proceso de seguimiento de las etapas de funcionamiento de los módulos mediante variables globales en la fig.8.

Fig.8. Diagrama de la secuencia de procesado de etiquetas

Fig.8. Diagrama de la secuencia de procesado de etiquetas

Por ejemplo, el asesor "Globals_test_EA" se encuentra en el gráfico y funciona en su modo habitual.

Lo he eliminado del gráfico, recibiendo a continuación este mensaje en el registro:

2014.10.22 20:14:29.575 Globals_test_EA (EURUSD.e,H1)   El programa ha sido finalizado forzosamente antes de su ejecución: <<Open_finish>>

De esta forma, la interrupción del funcionamiento del asesor ha tenido lugar en el módulo Open().

Abrimos las variables globales mediante la tecla F3 (fig.9).

Fig.9. Variables globales para el asesor "Globals_test_EA"

Fig.9. Variables globales para el asesor "Globals_test_EA"

A juzgar por la lista, en el cero se desechó solo la etiqueta responsable del comienzo del funcionamiento del módulo Open().

Resulta que, de manera potencial, se pueden observar problemas al no ejecutarse los comandos relacionados con la apertura de posición, su cierre y seguimiento.

Una nueva iniciación del funcionamiento en el gráfico mostrará en el registro el siguiente mensaje:

RQ      0       20:28:25.135    Globals_test_EA (EURUSD.e,H1)   Valor diferente a cero para: <<Open_finish>>
CL      0       20:28:25.135    Globals_test_EA (EURUSD.e,H1)   Valor diferente a cero para: <<Close_start>>
DH      0       20:28:25.135    Globals_test_EA (EURUSD.e,H1)   Valor diferente a cero para: <<Close_finish>>
ES      0       20:28:25.135    Globals_test_EA (EURUSD.e,H1)   Valor diferente a cero para: <<Trail_start>>
RS      0       20:28:25.135    Globals_test_EA (EURUSD.e,H1)   Valor diferente a cero para: <<Trail_finish>>

De esta forma, obtenemos la advertencia de no haber superado las etapas de funcionamiento del programa. ¿Qué hacemos si no han sido superadas? Eso ya es otra cuestión.


Conclusión

En este artículo he demostrado las posibilidades orientadas a objetos del lenguaje MQL5 en lo referente a la creación de objetos responsables del trabajo con las variables globales del terminal.

Como ejemplo práctico, se ha estudiado el caso en el que las variables globales pueden ser usadas como puntos de control de la ejecución de las etapas del programa.

Como siempre, cualquier observación, propuesta o crítica constructiva será bienvenida.


Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/1210

Archivos adjuntos |
CGlobalVar.mqh (10.26 KB)
Globals_test1.mq5 (1.83 KB)
Globals_test2.mq5 (1.47 KB)
Globals_test3.mq5 (1.29 KB)
Por qué el hosting virtual en Meta Trader 4 y MetaTrader 5 es mejor que los VPS habituales Por qué el hosting virtual en Meta Trader 4 y MetaTrader 5 es mejor que los VPS habituales
La red Virtual Hosting Cloud ha sido especialmente desarrollada para MetaTrader 4 y MetaTrader 5, y posee todas las ventajas de la solución original. ¡Alquile ahora mismo un servidor virtual y ponga a prueba su funcionamiento, le damos 24 horas gratuitas!
Recetas MQL5 - procesamiento del evento BookEvent Recetas MQL5 - procesamiento del evento BookEvent
En el artículo se estudia el evento de la profundidad de mercado BookEvent y su principio de procesamiento. En calidad de ejemplo se crea un programa MQL5, que procesa el estado de la profundidad de mercado. Se usa una aproximación orientada a objetos. Los resultados del procesamiento se muestran en la pantalla en forma de panel y de niveles de profundidad.
Optimizando la optimización: algunas sencillas ideas Optimizando la optimización: algunas sencillas ideas
El proceso de optimización consume muchos recursos del ordenador o del crédito que tengamos en nuestra cuenta de MQL5.community. Este artículo apunta algunas ideas sencillas que pongo en práctica para simplificar o completar el fabuloso sistema optimizador que ofrece MT5, extraídas de mil lecturas en la documentación, en el foro y en artículos.
Análisis regresivo de la influencia de datos macroeconómicos en el cambio de precio de las divisas Análisis regresivo de la influencia de datos macroeconómicos en el cambio de precio de las divisas
En el artículo estudiaremos la cuestión del uso del análisis regresivo múltiple de datos de la estadística macroeconómica y el análisis de la influencia de estos datos en el curso de las divisas, sobre el ejemplo de la pareja EURUSD. La utilización de tal tipo de análisis permite automatizar la realización del análisis fundamental, que se convertirá en algo accesible para prácticamente cualquiera, incluso para un trader principiante.