Princípios de Programação em MQL5: Variáveis Globais do Terminal
Introdução
No ambiente MQL4/5 existe um instrumento interessante - as variáveis globais do terminal do cliente. Ela permite criar um área de armazenamento de dados compartilhados para todos os programas do terminal. Além disso, o tempo de vida desta área não pára com o encerramento do terminal. Este artigo sugere a utilização das ferramentas de Programação Orientada a Objetos para ter uma idéia clara do que se trata as variáveis globais do terminal.
Outras informações sobre o artigo, as variáveis globais do terminal do cliente serão chamadas de "variáveis globais" ao menos que ela seja especificada de outra maneira.
1. Variáveis globais, Funções
Do ponto de vista de um programador, uma variável global é uma área da memória disponível para todos os programas em funcionamento de um terminal de negociação. Programadores iniciantes devem notar que, se existem vários terminais que trabalham simultaneamente, cada um deles terá o seu próprio espaço de memória independente para as variáveis globais. Elas não vão se sobrepor.
Os desenvolvedores da linguagem especificaram na Documentação que existem 11 funções usadas para trabalhar com variáveis globais.
A Teoria pode ser encontrada na seção "GlobalVariables" do livro texto MQL4.
Nas próximas seções, usarei os instrumentos da Programação orientada a objetos para a implementação das tarefas definidas.
2. Classe CGlobalVar
Guiado pelas idéias da Programação orientada a objetos, vamos criar a classe CGlobalVar, Que será diretamente responsável pelo objeto de uma variável 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); };
O que deve ser incluído dentro de uma classe? Para obter uma lista mínima de atributos, eu escolheria as seguintes propriedades:
- nome de uma variável;
- valor de uma variável;
- hora da criação;
- hora da última chamada;
- característica de uma variável temporária.
Quanto aos métodos, eles se parecem da seguinte forma:
- criação;
- eliminação;
- verificar se há existência;
- definir um novo valor;
- definir um novo valor por uma condição;
- receber um nome;
- receber um valor;
- receber uma etiqueta de uma variável temporária.
O método CGlobalVar::GetValue deve ser mencionado separadamente. Ele é um método modelo. Ele retorna o tipo de dados para o valor da variável que o usuário definiu como um argumento.
A questão aqui é que, em MQL uma função pode ser estabelecida apenas por parâmetros. Por isso deve-se adicionar um parâmetro falso.
Vamos criar um script de teste Globals_test1.mq5 onde iremos trabalhar com os objetos do 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); } }
Uma variável global é criada como se segue:
gVar1.Create("Gvar1",3.123456789101235,true)
O primeiro argumento é o componente básico do nome da variável futura ("Gvar1"), o segundo argumento é o valor de (3,123456789101235) e o terceiro argumento é o recurso que mostra que a variável vai ser temporária (true).
O nome da variável é criado pela adição do nome e o tipo de programa ao componente de base.
No meu caso, é:
- Gvar1 - o componente básico;
- prog_Globals_test1 - programa em que a variável foi criada (seu nome é Globals_test1);
- o tipo do programa é - SCR (script).
Ao pressionar F3, a seguinte entrada deve aparecer na lista de variáveis globais na janela do MetaTrader 5:
Fig.1. O valor da variável Test_temp_var1_prog_Globals_test1_scr é igual a 3,18
Em seu lançamento e se sua implementação foi bem-sucedida, as seguintes entradas serão impressas na aba "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
Diferentes tipos de dados do valor da variável são impressas no registro.
Se o terminal MetaTrader 5 é reiniciado, então a variável Gvar1_temp_prog_Globals_test1_scr desaparece da lista de variáveis globais. Isso acontece porque a variável era temporária, ela "viveu" enquanto o terminal estava aberto.
Em MQL4/5, no recebimento de dados sobre a variável global, não há nenhuma maneira de saber se a variável é temporária ou não. Talvez, a maneira mais fácil de identificar uma variável temporária é a adição de uma chave para o nome da variável. Por exemplo, pode ser o sufixo "Temp"No nome da variável. No entanto, a necessidade de controlar o nome da variável global criada é uma desvantagem desta abordagem, especialmente se tais variáveis são criadas por outros programas que não utilizam a classe CGlobalVar.
Em algum momento eu queria saber quantas variáveis globais poderiam ser criadas e com que rapidez.
Eu alterei ligeiramente o script anterior e o chamei de Globals_test2.mq5. Ele foi lançado com um número diferente de passes. Eu reiniciei o terminal após cada rodada para eliminar as variáveis.
#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); }
Aqui está o resultado (Figura 2).
Fig.2. O tempo gasto na criação das variáveis globais temporárias
O resultado de um teste semelhante para as variáveis globais completas são apresentadas na Fig.3. A sua criação não leva muito mais tempo.
A razão por trás disso é que essas variáveis são salvas no disco no arquivo gvariables.dat localizado na pasta Profiles.
Fig.3. O tempo gasto na criação das variáveis globais completas
Eu não acho que há uma necessidade de criar tantas variáveis globais. Eu realizei esta avaliação simplesmente por curiosidade.
Na passagem seguinte, vamos trabalhar com um conjunto de variáveis globais.
3. Classe CGlobalVarList
Para regular o trabalho com variáveis globais, vamos criar uma classe lista de variáveis globais do tipo CGlobalVarList. Esse tipo de lista é uma descendente da classe lista padrão CList.
A declaração de classe pode ser apresentada como:
//+------------------------------------------------------------------+ //| 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); };
Se os objetos relacionados com as variáveis globais atuais são para serem incluídas em uma lista do tipo CGlobalVarList, então o método CGlobalVarList::LoadCurrentGlobals é usado.
//+------------------------------------------------------------------+ //| 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 lê todas as variáveis globais presentes e inclui elas na lista.
O atributo m_gvars_type controla o tipo da variável global incluída. Ela é uma enumeração do tipo 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 };
Vamos supor que, antes da inicialização da lista CGlobalVarList, houve um conjunto de variáveis globais, tal como apresentado na Figura 4.
Fig.4. Conjunto aproximado das variáveis globais
Vamos verificar se este conjunto vai ser processado pela lista corretamente. Para realizar essa verificação, o script de teste Globals_test3.mq5 será criado.
#include "CGlobalVarList.mqh" //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { CGlobalVarList gvarList; gvarList.LoadCurrentGlobals(); PrintFormat("Number of variables in the set: %d",gvarList.Total()); }
Novas variáveis globais (em amarelo) aparecem após o lançamento do script, o que não deveria acontecer (Fig.5).
Fig.5. Novo conjunto de variáveis globais
Uma string foi impressa como:
2014.10.21 11:35:00.839 Globals_test3 (AUDUSD.e,H1) Number of variables in the list: 10
Isso aconteceu porque, na declaração do método CGlobalVarList::LoadCurrentGlobals há uma referência para o método CGlobalVar::create.
Isso significa que uma nova variável global é criada na string:
if(ptr_gvar.Create(gvar_name,gvar_val))
Além disso, os índices das variáveis globais estão a mudar à medida que novas variáveis aparecem. Isso é o que causa a confusão.
Eu recomendaria substituir o método CGlobalVar::create por um menos ativo. Um construtor com parâmetros tem de ser adicionado a classe CGlobalVar de modo que a variável possa ser levada em conta na lista.
Após a modificação do método CGlobalVarList::LoadCurrentGlobals, ele é semelhante como este:
//+------------------------------------------------------------------+ //| 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; }
O script funciona corretamente após o método ser modificado. O registro seguinte será impresso:
2014.10.21 11:38:04.424 Globals_test3 (AUDUSD.e,H1) Number of variables in the list: 6
Em seguida, vamos adicionar funcionalidades que permitem apagar e imprimir uma lista.
Agora o script Globals_test3.mq5 é semelhante a:
//--- #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); } }
Nós estamos iremos complicar a tarefa, criando 10 variáveis globais diversas (fig.6).
Fig.6. Diversas variáveis globais
Apenas variáveis completas serão incluídas na nossa lista gvarList . Então eles vão ser removidos.
A aba "Expert" irá conter o seguinte:
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
A lista inclui apenas as variáveis globais completas que foram criadas corretamente.
Em seguida, ele irá ser liberada e apenas 5 variáveis temporárias serão deixadas no terminal (Fig.7).
Fig.7. Variáveis globais temporárias
A tarefa pretendida foi realizada.
Na classe CGlobalVarList , também foram implementados métodos de gravação de dados para o arquivo e download dos dados do arquivo.
4. Aplicação Prática
Como é bem conhecido, a MQL4/5 é uma linguagem de programação especializada. Ele foi criada para a programação de estratégias de negociação. É por isso que qualquer ferramenta da língua deve ser considerada como um meio de formalizar uma certa ideia de negociação.
Existem exemplos suficientes ligando Expert Advisors com variáveis globais na plataforma MQL5. Hoje eu sugiro dar uma olhada de perto na situação em que o controle sobre a execução do programa é necessário.
Vamos assumir que existe um código de "Globals_test_EA" robô de negociação com base na abordagem de módulo:
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { Comment(""); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { Main(); }
onde o módulo principal é semelhante:
//+------------------------------------------------------------------+ //| 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(); } }
Esse é o módulo principal, que inclui três componentes:
- módulo de abertura;
- módulo de fechamento;
- módulo de stop móvel.
Agora precisamos criar as variáveis globais que controlam as fases de execução do programa.
Existem três fases, sob a forma de módulos. Dois pontos de controle são utilizados para cada fase. O primeiro ponto de controle controla o início do funcionamento do módulo e a segunda é para controlar a extremidade de trabalho do módulo.
Os pontos de controle são implementadas na forma de variáveis globais.
Então, precisamos de seis variáveis globais com os seguintes nomes:
//--- global variables: names string gVar_names[6]= { "gvarOpen_start","gvarOpen_finish", "gvarClose_start","gvarClose_finish", "gvarTrail_start","gvarTrail_finish" };
Flags para todos os módulos são definidos no início da função Main() e apuradas em cada módulo separado. E, é claro, estamos falando sobre as "próprias" flags somente. Por exemplo, vamos no referir ao módulo 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); } }
Na execução do módulo, um comentário que o programa está a funcionar no bloco Open() aparece na janela do gráfico.
Então, se o programa não foi forçado a fechar, o controle é passado para a função de definir/limpar uma flag correspondente. No caso de houver uma falha ao limpar uma flag em qualquer um dos pontos de controle, é considerado que o módulo não terminou o trabalho.
O diagrama de estágios de rastreamento do funcionamento do módulo com variáveis globais é apresentado na Fig.8.
Fig. 8. Diagrama da sequência de processos da flag
Por exemplo, o Expert Advisor "Globals_test_EA" é anexado ao gráfico e opera normalmente.
Quando eu removi o Expert Advisor do gráfico, a seguinte entrada apareceu no registro:
2014.10.22 20:14:29.575 Globals_test_EA (EURUSD.e,H1) Program forced to terminate before execution: <<Open_finish>>
Portanto o encerramento do Expert Advisor pega o lugar do módulo Open()..
Abra a lista de variáveis globais pressionando F3 (Fig.9).
Fig. 9. As variáveis globais para o Expert Advisor "Globals_test_EA"
Pelo parecer da lista, apenas a flag responsável pelo início do funcionamento do módulo Open() foi zerado.
Parece que falhas potenciais podem ser detectadas na falha de execução de comandos relacionados com posições de abertura, seu encerramento e manutenção.
Depois de um relançamento do robô no mesmo gráfico, a seguinte informação será exibida no registro:
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>>
Desta forma, recebemos um aviso sobre uma falha de uma das fases do programa. Isso leva a uma outra questão. O que pode ser feito se esses estágios falharem? Essa é uma história diferente.
Conclusão
Neste artigo eu demonstrei as capacidades orientada a objetos da linguagem MQL5 para criar objetos que facilitam o trabalho com variáveis globais do terminal.
Um caso em que as variáveis globais foram usados como pontos de controle para implementação de estágios do programa, que serviu de exemplo.
Como sempre, comentários, sugestões e críticas construtivas são sempre bem vindas.
Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/1210
- Aplicativos de negociação gratuitos
- 8 000+ sinais para cópia
- Notícias econômicas para análise dos mercados financeiros
Você concorda com a política do site e com os termos de uso