
Fondamenti di programmazione MQL5: Variabili Globali del Terminale
Introduzione
Nell'ambiente MQL4/5 c'è uno strumento interessante - variabili globali del terminale client. Consente di creare aree di archiviazione dati condivise per tutti i programmi del terminale. Inoltre, l’attività di quest'area non si arresta alla chiusura del terminal. Questo articolo suggerisce di utilizzare gli strumenti di programmazione orientata agli oggetti per avere un'idea chiara di quali siano le variabili globali del terminale.
Più avanti nell'articolo, le variabili globali del terminale client verranno chiamate "variabili globali" se non diversamente specificato.
1. Variabili globali, funzioni
Dal punto di vista di un programmatore, una variabile globale è un'area di memoria denominata, disponibile per tutti i programmi di lavoro di un terminale di trading. I programmatori alle prime armi dovrebbero notare che se ci sono più terminali che funzionano contemporaneamente, ognuno di essi avrà il proprio spazio di memoria indipendente per le variabili globali. Non si sovrapporranno.
Gli sviluppatori del linguaggio specificano nella Documentazione che ci sono 11 funzioni utilizzate per lavorare con le variabili globali.
La teoria può essere trovata nella sezione "GlobalVariables" del libro di testo MQL4.
Nelle prossime sezioni utilizzerò gli strumenti di programmazione orientata agli oggetti per l'implementazione di task prefissati.
2. Class CGlobalVar
Guidati dalle idee della programmazione orientata agli oggetti, creiamo la classe CGlobalVar, che sarà direttamente responsabile dell'oggetto di una variabile globale.
//+------------------------------------------------------------------+ //| 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); };
Cosa deve includere una classe? Per un elenco di attributi minimo, sceglierei le seguenti proprietà:
- nome di una variabile;
- valore di una variabile;
- tempo di creazione;
- ora dell'ultima chiamata;
- caratteristica di una variabile temporanea.
Per quanto riguarda i metodi, sono i seguenti:
- Creazione
- eliminazione;
- verificare l'esistenza;
- impostazione di un nuovo valore;
- impostare un nuovo valore per condizione;
- ricevere un nome;
- ricevere un valore;
- ricevendo un flag di variabile temporanea.
Il metodo CGlobalVar::GetValue dovrebbe essere menzionato separatamente. È un metodo modello. Restituisce il tipo di dati per il valore della variabile che un utente imposta come argomento.
Il problema qui è che in MQL una funzione può essere digitata solo tramite parametri. Pertanto è necessario aggiungere un parametro falso.
Creiamo lo script di test Globals_test1.mq5 dove lavoreremo con oggetti del 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 variabile globale viene creata come segue:
gVar1.Create("Gvar1",3.123456789101235,true)
Il primo argomento è il componente di base del nome della variabile futura ("Gvar1"), il secondo argomento è il valore di (3.123456789101235) e il terzo argomento è la caratteristica che mostra che la variabile sarà temporanea (vera).
Il nome della variabile si crea aggiungendo al componente base il nome e il tipo di programma.
Nel mio caso è:
- Gvar1 - il componente di base;
- prog_Globals_test1 - programma in cui è stata creata la variabile (il suo nome è Globals_test1);
- il tipo di programma è - scr (script).
Premendo F3, la seguente voce dovrebbe apparire nell'elenco delle variabili globali nella finestra MetaTrader 5:
Fig.1. Il valore della variabile Test_temp_var1_prog_Globals_test1_scr è pari a 3,18
Al suo lancio e implementazione di successo, le seguenti voci vengono stampate nella rivista "Esperti":
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
Nel giornale vengono stampati diversi tipi di dati del valore della variabile.
Se il terminale MetaTrader 5 viene riavviato, la variabile Gvar1_temp_prog_Globals_test1_scr scompare dall'elenco delle variabili globali. Succede perché la variabile era temporanea e viveva mentre il terminale era aperto.
In MQL4/5, alla ricezione dei dati sulla variabile globale, non c'è modo di scoprire se la variabile è temporanea o meno. Forse il modo più semplice per identificare una variabile temporanea è aggiungere una chiave al nome della variabile. Ad esempio, può essere il suffisso "temp" nel nome della variabile. Tuttavia, la necessità di controllare la creazione del nome della variabile globale è un netto svantaggio di questo approccio, specialmente se tali variabili vengono create da altri programmi che non utilizzano la classe CGlobalVar.
Ad un certo punto volevo sapere quante variabili globali potevano essere create e quanto velocemente.
Ho leggermente modificato lo script precedente e l'ho chiamato Globals_test2.mq5. È stato lanciato con un numero diverso di corse. Ho riavviato il terminale dopo ogni esecuzione per eliminare le variabili.
#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); }
Ecco il risultato (Fig.2)
Fig.2. Tempo speso per creare variabili globali temporanee
Il risultato di un test simile per le variabili globali complete è presentato in Fig.3. La loro creazione non richiede molto più tempo.
Il motivo è che queste variabili vengono salvate sul disco nel file gvariables.dat situato nella cartella Profili.
Fig.3. Tempo speso per creare variabili globali complete
Non credo che sia necessario creare così tante variabili globali. Ho condotto questa valutazione semplicemente per curiosità.
Nel prossimo passaggio lavoreremo con un insieme di variabili globali.
3. CGlobalVarList Class
Per regolare il lavoro con le variabili globali, creeremo una classe lista di variabili globali del tipo CGlobalVarList. Questo tipo di elenco è un discendente della classe di elenco standard CList.
La dichiarazione di classe può essere presentata come:
//+------------------------------------------------------------------+ //| 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 gli oggetti collegati alle variabili globali correnti devono essere inclusi in un elenco di tipo CGlobalVarList, viene utilizzato il metodo 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; }
Questo metodo legge tutte le variabili globali presenti e le include nell'elenco.
L'attributo m_gvars_type controlla il tipo della variabile globale inclusa. È un'enumerazione del 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 };
Supponiamo che prima dell'inizializzazione della lista CGlobalVarList, ci fosse un insieme di variabili globali come presentato in Fig.4.
Fig.4. Insieme approssimativo di variabili globali
Verificheremo se questo set verrà elaborato correttamente dall'elenco. Per effettuare tale verifica verrà creato lo script di test Globals_test3.mq5.
#include "CGlobalVarList.mqh" //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { CGlobalVarList gvarList; gvarList.LoadCurrentGlobals(); PrintFormat("Number of variables in the set: %d",gvarList.Total()); }
Nuove variabili globali (evidenziate in giallo) sono apparse dopo il lancio dello script, cosa che non doveva avvenire (Fig.5).
Fig.5. Numero di variabili globali.
Una stringa è stata stampata come:
2014.10.21 11:35:00.839 Globals_test3 (AUDUSD.e,H1) Number of variables in the list: 10
È successo perché nella dichiarazione del metodo CGlobalVarList::LoadCurrentGlobals c'è un riferimento al metodo CGlobalVar::Create.
Ciò significa che nella stringa viene creata una nuova variabile globale:
if(ptr_gvar.Create(gvar_name,gvar_val))
Inoltre, gli indici delle variabili globali cambiano man mano che vengono visualizzate nuove variabili. Questo è ciò che causa la confusione.
Consiglierei di sostituire il metodo CGlobalVar::Create con uno meno attivo. Un costruttore con parametri deve essere aggiunto alla classe CGlobalVar in modo che la variabile possa essere presa in considerazione nell'elenco.
Dopo la modifica, il metodo CGlobalVarList::LoadCurrentGlobals ha il seguente aspetto:
//+------------------------------------------------------------------+ //| 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; }
Lo script funziona correttamente dopo che il metodo è stato modificato. Viene stampato il seguente record:
2014.10.21 11:38:04.424 Globals_test3 (AUDUSD.e,H1) Number of variables in the list: 6
Quindi aggiungeremo funzionalità che consentono di eliminare e stampare un elenco.
Ora lo script Globals_test3.mq5 ha il seguente aspetto:
//--- #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); } }
Complicheremo il compito creando 10 variabili globali differenziate (Fig.6).
Fig.6. Variabili globali differenziate
Solo le variabili complete saranno incluse nella nostra lista gvarList. Quindi verranno eliminati.
La rivista "Esperto" conterrà quanto segue:
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
L'elenco che include solo le variabili globali complete è stato creato correttamente.
Quindi è stato cancellato e nel terminale sono rimaste solo 5 variabili temporanee (Fig.7).
Fig.7. Variabili globali temporanee
Il compito previsto è stato compiuto.
Nella classe CGlobalVarList sono stati implementati anche i metodi per salvare i dati nel file e scaricare i dati dal file.
4. Applicazione pratica
Come è noto, MQL4/5 è un linguaggio di programmazione specializzato. È stato creato per programmare strategie di trading. Ecco perché qualsiasi strumento del linguaggio è da considerarsi come mezzo per formalizzare una certa idea di trading.
Ci sono abbastanza esempi di collegamento di Expert Advisor con variabili globali sulla piattaforma MQL5. Oggi suggerisco di esaminare da vicino la situazione in cui è richiesto il controllo sull'attuazione del programma.
Supponiamo che ci sia un codice del robot di trading "Globals_test_EA" basato su un approccio a moduli:
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { Comment(""); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { Main(); }
dove appare il modulo principale:
//+------------------------------------------------------------------+ //| 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(); } }
Questo è il modulo principale comprende 3 componenti:
- modulo di apertura;
- modulo di chiusura;
- Modulo Trailing Stop.
Ora abbiamo bisogno di creare variabili globali che controllano le fasi di esecuzione del programma.
Ci sono tre fasi sotto forma di moduli. Per ogni fase vengono utilizzati due punti di controllo. Il primo punto di controllo controlla l'inizio del lavoro del modulo e il secondo controlla la fine del lavoro del modulo.
I punti di controllo sono implementati sotto forma di variabili globali.
Quindi, abbiamo bisogno di sei variabili globali con i seguenti nomi:
//--- global variables: names string gVar_names[6]= { "gvarOpen_start","gvarOpen_finish", "gvarClose_start","gvarClose_finish", "gvarTrail_start","gvarTrail_finish" };
I flag per tutti i moduli sono impostati all'inizio della funzione Main() e cancellati in ogni modulo separato. Inutile dire che stiamo parlando solo di bandiere "proprie". Ad esempio, facciamo riferimento al modulo 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); } }
All'esecuzione del modulo, nella finestra del grafico viene visualizzato un commento che il programma sta lavorando nel blocco Open().
Quindi, se il programma non è stato forzato alla chiusura, il controllo passa alla funzione di impostare/cancellare un flag corrispondente. Nel caso in cui un flag non sia stato cancellato in uno qualsiasi dei punti di controllo, il modulo è considerato non aver terminato il lavoro.
Un modello di fasi di monitoraggio del lavoro del modulo con variabili globali è presentato in Fig. 8.
Fig. 8. Modello per la sequenza di elaborazione dei flag
Ad esempio, l'Expert Advisor "Globals_test_EA" è allegato al grafico e funziona normalmente.
Quando ho cancellato l'Expert Advisor dal grafico, nel journal è apparsa la seguente voce:
2014.10.22 20:14:29.575 Globals_test_EA (EURUSD.e,H1) Program forced to terminate before execution: <<Open_finish>>
Pertanto, la chiusura dell'Expert Advisor è avvenuta in Open(). modulo.
Aprire l'elenco delle variabili globali premendo F3 (Fig.9).
Fig. 9. Variabili globali per Expert Advisor "Globals_test_EA"
Dall'aspetto dell'elenco, solo il flag responsabile dell'inizio del lavoro del modulo Open() è stato azzerato.
Sembra che potenziali guasti possano essere rilevati alla mancata esecuzione dei comandi legati alle posizioni di apertura, chiusura e manutenzione.
Dopo un rilancio del robot sullo stesso grafico, nel journal verranno visualizzate le seguenti informazioni:
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>>
In questo modo riceviamo un avviso su un fallimento delle fasi del programma. Porta a un'altra domanda. Cosa si può fare se quelle fasi falliscono? Questa è una storia diversa.
Conclusione
In questo articolo ho dimostrato le capacità orientate agli oggetti del linguaggio MQL5 per la creazione di oggetti che facilitano il lavoro con le variabili globali del terminale.
Un caso in cui le variabili globali sono state utilizzate come punti di controllo per l'implementazione delle fasi del programma è servito come esempio.
Come sempre sono ben accetti commenti, suggerimenti e critiche costruttive.
Tradotto dal russo da MetaQuotes Ltd.
Articolo originale: https://www.mql5.com/ru/articles/1210





- App di trading gratuite
- Oltre 8.000 segnali per il copy trading
- Notizie economiche per esplorare i mercati finanziari
Accetti la politica del sito e le condizioni d’uso