![Grundlagen der Programmierung in MQL5: Globale Variablen des Terminals](https://c.mql5.com/i/articles/overlay.png)
Grundlagen der Programmierung in MQL5: Globale Variablen des Terminals
Einleitung
Es gibt in der MQL4/5-Umgebung ein interessantes Instrument - die globalen Variablen des Client-Terminals. Mit ihm kann man für alle Programme des Terminals einen gemeinsam nutzbaren Datenspeicherungsbereich anlegen. Darüber hinaus endet die Lebensdauer dieses Bereichs nicht, wenn das Terminal geschlossen wird. Dieser Beitrag schlägt die Verwendung von Objekt-orientierten Programmierungs-Tools vor, um eine klare Vorstellung davon zu bekommen, was die globalen Variablen des Terminals eigentlich sind.
Im weiteren Verlauf des Beitrags heißen die globalen Variablen des Client-Terminals immer "globale Variablen", es sei denn anders angegeben.
1. Globale Variablen, Funktionen
Vom Standpunkt eines Programmierers, ist eine globale Variable ein benannter Speicherbereich, auf den alle Arbeitsprogramme des Handelsterminals Zugriff haben. Neulinge unter den Programmierern sollten beachten, dass, wenn mehrere Terminals zeitgleich arbeiten, jedes Arbeitsprogramm seinen eigenen, unabhängigen Speicherbereich für globale Variablen hat. Sie werden sich auch nicht überlappen.
Die Sprachentwickler geben in der Dokumentation an, dass es 11 Funktionen gibt, die für die Arbeit mit globalen Variablen verwendet werden.
Diesbezügliche Theorie findet sich im Abschnitt "GlobaleVariablen" des MQL4-Leitfaden.
Im nächsten Abschnitt verwende ich die Objekt-orientierten Programmierungsinstrumente zur Implementierung der gesetzten Aufgaben.
2. CGlobalVar Klasse
Im Sinne der Gedanken des Objekt-orientierten Programmierens, erzeugen wir die CGlobalVar Klasse, die direkt für das Objekt einer globalen Variable zuständig ist.
//+------------------------------------------------------------------+ //| 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); };
Was muss eine Klasse alles umfassen? Für eine Mindestauflistung an Merkmalen würde ich folgende Eigenschaften auswählen:
- Name einer Variable;
- Wert einer Variable;
- Zeit der Erzeugung;
- Zeit des letzten Aufrufs;
- Merkmal einer temporären Variable.
Was die Methoden angeht, sie sehen folgendermaßen aus:
- Erzeugung;
- Löschung;
- Prüfung auf Vorhandensein;
- Einrichten eines neuen Werts;
- Einrichten eines neuen Werts nach Bedingung;
- Empfang eines Namens;
- Empfang eines Werts;
- Empfang eines temporären Variablen-Markers.
Die CGlobalVar::GetValue Methode verdient es, extra erwähnt zu werden, da sie eine Template-Methode ist. Sie liefert den Datentyp für den Variablenwert, den ein Anwender als Begründung einrichtet.
Das Problem hier ist, dass in MQL eine Funktion nur nach Parametern typenspezifiziert werden kann. Deshalb müssen wir einen fingierten Parameter hinzufügen.
Erzeugen wir also das Test-Script Globals_test1.mq5, in dem wir mir den Objekten des CGlobalVar Typs arbeiten werden.
#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); } }
Und so wird die globale Variable erzeugt:
gVar1.Create("Gvar1",3.123456789101235,true)
Die erste Begründung ist die Basiskomponente des zukünftigen Variablennamen ("Gvar1"), die zweite Begründung ist der Wert von (3,123456789101235) und die dritte ist das Merkmal, das anzeigt, dass die Variable vorläufig sein wird (true).
Der Name der Variable wird durch Hinzufügen des Programmnamens und -typs an die Basiskomponente erzeugt.
In meinem Fall also:
- Gvar1 - die Basiskomponente;
- prog_Globals_test1 - das Programm, in dem die Variable erzeugt wurde (ihr Name ist Globals_test1);
- Programmtyp ist - scr (Script).
Wenn Sie 'F3' drücken, sollte in der Liste mit den globalen Variablen im MetaTrader 5 Fenster der folgende Eintrag erscheinen:
Abb. 1 Wert der Test_temp_var1_prog_Globals_test1_scr Vriable = 3,18
Bei erfolgreichem Start und erfolgreicher Implementierung, werden die folgenden Einträge in das "Experts" Logbuch gedruckt:
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
In das Logbuch werden unterschiedliche Datentypen des Variablenwerts gedruckt.
Wird das MetaTrader 5 Terminal neu gestartet, verschwindet die Gvar1_temp_prog_Globals_test1_scr Variable aus der Liste der globalen Variablen. Und das geschieht deswegen, weil die Variable vorläufig war und nur solange "lebte", wie das Terminal offen war.
Im Moment Erhalts der Daten zur globalen Variable, gibt es in MQL4/5 keine Möglichkeit herauszufinden, ob die Variable vorläufig ist oder nicht. Die vielleicht leichteste Möglichkeit eine vorläufige Variable zu erkennen, ist durch Hinzufügen eines Schlüssels an den Namen der Variable. Das kann z.B. das Suffix "temp" am Variablennamen sein. Die Notwendigkeit, das Erstellen des Namens der globalen Variable zu kontrollieren zu müssen, ist jedoch ein klarer Nachteil dieses Ansatzes, vor allem dann, wenn derartige Variablen von anderen Programmierern angelegt werden, die nicht mit der CGlobalVar Klasse arbeiten.
Irgendwann wollte ich dann wissen, wie viele globale Variablen man erstellen kann und wie rasch.
Dazu habe ich das vorherige Script leicht abgeändert und es in Globals_test2.mq5 umbenannt und es dann mit einer anderen Anzahl von Durchläufen gestartet. Bei jedem Durchlauf habe ich das Terminal neu gestartet, um Variablen zu löschen.
#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); }
Und das ist das Ergebnis (Abb. 2).
Abb. 2 Zur Erstellung vorläufiger globaler Variablen benötigte Zeit
Abb. 3 zeigt das Ergebnis eines ähnlichen Tests für komplette globale Variablen. Und ihre Erstellung hat nicht viel länger gedauert.
Das liegt daran, dass diese Variablen in der gvariables.dat-Datei, die sich im Order Profile befindet, auf der Festplatte gespeichert werden.
Abb. 3 Zur Erstellung kompletter globaler Variablen benötigte Zeit
Ich glaube übrigens nicht, dass man so viele globale Variablen erstellen muss. Ich habe dies einfach ausprobiert und gemacht, weil ich neugierig war.
Im nächsten Abschnitt, beginnen wir mit dem Set an globalen Variablen zu arbeiten.
3. Die CGlobalVarList Klasse
Um die Arbeit mit globalen Variablen regeln zu können, erzeugen wir nun eine Listenklasse von globalen Variablen des Typs CGlobalVarList. Dieser Listentyp ist ein Nachkomme der Standard-Listenklasse CList.
Die Klassendeklarierung kann so dargestellt werden:
//+------------------------------------------------------------------+ //| 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); };
Wenn Objekte, die mit den aktuellen globalen Variablen verknüpft sind, in eine Liste des Typs CGlobalVarList mit eingeschlossen werden sollen, wird dazu die CGlobalVarList::LoadCurrentGlobals Methode verwendet.
//+------------------------------------------------------------------+ //| 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; }
Sie liest alle vorhandenen globalen Variablen und nimmt sie in die Liste auf.
Das m_gvars_type Merkmal kontrolliert den Typ der mit aufgenommenen globalen Variable und ist eine Aufzählung des Typs 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 };
Nehmen wir, dass vor der Initialisierung der CGlobalVarList Liste ein Set globaler Variablen, so wie in Abb. 4 gezeigt, vorhanden waren.
Abb. 4 Ungefähres Set an globalen Variablen
Wir prüfen nun, ob dieses Set korrekt durch die Liste verarbeitet wird. Und dazu müssen wir das Globals_test3.mq5 Test-Script erstellen.
#include "CGlobalVarList.mqh" //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { CGlobalVarList gvarList; gvarList.LoadCurrentGlobals(); PrintFormat("Number of variables in the set: %d",gvarList.Total()); }
Nach dem Start des Scripts tauchten neue globale Variablen auf (in gelb unterlegt), was eigentlich nicht passieren sollte (Abb. 5).
Abb. 5 Neues Set globaler Variablen
Ein String wurde gedruckt als:
2014.10.21 11:35:00.839 Globals_test3 (AUDUSD.e,H1) Number of variables in the list: 10
Dies passierte, weil es in der Deklarierung der CGlobalVarList::LoadCurrentGlobals Methode einen Verweis auf die CGlobalVar::Create Methode gibt.
Das bedeutet, dass im String eine neue globale Variable erzeugt wird:
if(ptr_gvar.Create(gvar_name,gvar_val))
Zusätzlich dazu verändern sich die Indices der globalen Variablen, sobald neue Variablen auftauchen. Und das ist der Grund für die Verwirrung.
Daher empfehle ich, die CGlobalVar::Create Methode durch eine weniger aktive Methode zu ersetzen. Die CGlobalVar Klasse muss dazu durch einen Konstruktor mit Parametern ergänzt werden, damit die Variable in der Liste berücksichtigt werden kann.
Nach der Veränderung sieht die CGlobalVarList::LoadCurrentGlobals Methode so aus:
//+------------------------------------------------------------------+ //| 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; }
Und nach der Veränderung der Methode funktioniert das Script jetzt auch korrekt. Der folgende Eintrag wird gedruckt:
2014.10.21 11:38:04.424 Globals_test3 (AUDUSD.e,H1) Number of variables in the list: 6
Anschließend fügen wir noch Merkmale hinzu, mit denen man eine Liste löschen und drucken kann.
Das Globals_test3.mq5 Script sieht jetzt so aus:
//--- #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); } }
Machen wir unsere Aufgabe noch ein bisschen komplizierter und erzeugen jetzt 10 unterschiedliche, globale Variablen (Abb. 6).
Abb. 6 Unterschiedliche globale Variablen
In unserer gvarList Liste sollen nur komplette Variablen aufgenommen werden, die. später dann gelöscht werden.
Das "Expert" Logbuch enthält dann folgendes:
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
Die Liste, die jetzt nur komplette, globale Variablen enthält, wurde korrekt angelegt.
Dann wurde sie geleert, sodass nur noch 5 vorläufige Variable im Terminal verbleiben (Abb. 7).
Abb. 7 Vorläufige globale Variablen
Wir haben das, was wir uns vorgenommen haben, erreicht.
In der CGlobalVarList Klasse wurden zudem auch Methoden zur Datenspeicherung und zum Download von Daten aus der Datei implementiert.
4. Praktische Anwendung
Wir wir alle wissen, ist MQL4/5 eine spezielle Programmiersprache, die zur Programmierung von Handelsstrategien erfunden wurde. Und daher kann man jedes Tool dieser Sprache als ein Mittel zur Formalisierung einer bestimmten Handelsidee betrachten.
Auf der MQL 5 Plattform gibt es genügend Beispiele für eine Verknüpfung von Expert Advisors mit globalen Variablen. Diesmal schlage ich vor, sich die Situation genauer anzusehen, in der eine Kontrolle der Programmimplementierung erforderlich ist.
Nehmen wir mal an, es gibt einen Code des "Globals_test_EA" Handelsroboters auf Grundlage des modularen Ansatzes:
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { Comment(""); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { Main(); }
wo das Hauptmodul so aussieht:
//+------------------------------------------------------------------+ //| 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(); } }
Dieses Modul umfasst also 3 Bestandteile:
- Eröffnungsmodul;
- Schließungsmodul;
- Trailing Stop-Modul.
Jetzt müssen wir nur noch globale Variablen zur Kontrolle der Etappen der Programmausführung erstellen.
Es gibt drei Phasen in Form von Modulen. In jeder Phase werden zwei Kontrollpunkte verwendet. Der erste Kontrollpunkt kontrolliert den Anfang der Arbeit des Moduls und der zweite das Ende seiner Arbeit.
Kontrollpunkte werden in Form globaler Variablen implementiert.
Wir brauchen daher sechs globale Variablen mit den folgenden Namen:
//--- global variables: names string gVar_names[6]= { "gvarOpen_start","gvarOpen_finish", "gvarClose_start","gvarClose_finish", "gvarTrail_start","gvarTrail_finish" };
Marker für alle Module werden am Anfang der Main() Funktion eingerichtet und in jedem separaten Modul dann geleert. Ich brauche wohl nicht extra erwähnen, dass wir hier ausschließlich von "eigenen" Markern sprechen. Beispiel: das Open() Modul:
//+------------------------------------------------------------------+ //| 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); } }
Bei Ausführung des Moduls, erscheint im Chartfenster ein Kommentar, dass das Programm im Open() Block arbeitet.
Wenn dann kein Schließen des Programms erzwungen wurde, wird die Kontrolle an die Funktion übertragen, die einen entsprechenden Marker setzt/löscht. Sollte ein Marker in einem der Kontrollpunkte nicht gelöscht werden können, wird das so interpretiert, dass das Modul seine Arbeit nicht abgeschlossen hat.
Abb. 8 zeigt ein Muster der Rückverfolgungsphasen der Arbeit des Moduls mit globalen Variablen.
Abb. 8 Muster der Abfolge der verarbeitenden Marker
So ist z.B. der "Globals_test_EA" Expert Advisor an den Chart angehängt und funktioniert normal.
Als ich jedoch den Expert Advisor vom Chart gelöscht habe, zeigte mir das Logbuch den folgenden Eintrag:
2014.10.22 20:14:29.575 Globals_test_EA (EURUSD.e,H1) Program forced to terminate before execution: <<Open_finish>>
Das heißt also, dass die Beendigung des Expert Advisors im Open(). Modul stattfand.
Durch Drücken von "F3" (Abb. 9) öffnen Sie die Liste der globalen Variablen.
Abb. 9 Globale Variablen für den "Globals_test_EA" Expert Advisor
Wenn wir uns diese Liste ansehen, erkennen wir, dass der Marker, der für den Anfang der Arbeit des Open() Moduls verantwortlich war, genullt wurde.
Es hat den Anschein, als könnten Fehler im Zusammenhang mit der Eröffnung, dem Schließen und der Beibehaltung von Positions möglicherweise bereits bei einer gescheiterten Ausführung des Befehls entdeckt werden.
Nach einem Neustart des Roboters auf dem gleichen Chart, wird im Logbuch die folgende Information angezeigt:
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>>
Auf diese Weise erhalten wir eine Warnung über einen Misserfolg der Programmphasen - was uns zu einer anderen Frage führt: Was kann man machen, wenn die Phasen fehlschlagen? Doch das ist eine ganz andere Geschichte.
Fazit
In diesem Beitrag habe ich die Objekt-orientierten Fähigkeiten der MQL5-Sprache zur Erzeugung von Objekten aufzuzeigen versucht, die die Arbeit mit den globalen Variablen des Terminals immens erleichtern.
Als Beispiel diente der Fall, in dem globale Variablen als Kontrollpunkte zur Implementierung der Programmphasen hergenommen werden.
Ich freue mich wie immer über Kommentare, Vorschläge und konstruktive Kritik.
Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/1210
![Liquid-Chart](https://c.mql5.com/2/11/800px-Wiki.png)
![Statistik-Cookbook für Händler: Hypothesen](https://c.mql5.com/2/12/Trader_Statistics_Recipes_MetaTrader5_Alglib_MQL5__1.png)
![Auf Fraktalen basiertes Zeichnen von Trendlinien mithilfe von MQL4 und MQL5](https://c.mql5.com/2/18/TrendLines_Fractals_Based.png)
![Programmierung von EA-Modi mit Hilfe des Objekt-orientierten Ansatzes](https://c.mql5.com/2/12/Expert_Advisor_modes_programming_img.png)
![MQL5 - Sprache von Handelsstrategien, eingebaut ins Kundenterminal MetaTrader 5](https://c.mql5.com/i/registerlandings/logo-2.png)
- Freie Handelsapplikationen
- Über 8.000 Signale zum Kopieren
- Wirtschaftsnachrichten für die Lage an den Finanzmärkte
Sie stimmen der Website-Richtlinie und den Nutzungsbedingungen zu.