Von der Grundstufe bis zur Mittelstufe: Variablen (III)
Einführung
Das hier vorgestellte Material dient ausschließlich zu Bildungszwecken. Die Anwendung sollte unter keinen Umständen zu einem anderen Zweck als zum Erlernen und Beherrschen der vorgestellten Konzepte verwendet werden.
Im vorherigen Artikel „Von der Grundstufe bis zur Mittelstufe: Variablen (II)“ haben wir besprochen und erklärt, wie statische Variablen in unserem Code verwendet werden. Solche Variablen ermöglichen uns, die unnötige Verwendung globaler Variablen zu vermeiden. Damit sind wir mit den Hauptvariablen fertig. Es bleibt jedoch die Frage nach dem Datentyp, den jede Variable enthalten kann, die für dieses Problem immer noch eine gewisse Relevanz hat. Auf diesen Aspekt werde ich im Rahmen der Variablenthematik nicht näher eingehen. Wir werden in einem separaten Thema darüber sprechen.
Wenn wir jedoch bereits über lokale und globale Variablen gesprochen haben, darüber, wie und warum eine Variable als Konstante deklariert wird und sogar darüber, wie statische Variablen verwendet werden, was gibt es zu diesem Thema noch zu sagen? Obwohl viele Leute das nicht glauben, gibt es einen speziellen Variablentyp, der oft als Konstante betrachtet werden kann, aber dennoch ein spezieller Variablentyp ist. Ich spreche von Funktionen. Eine Funktion ist ein spezieller Variablentyp, auch wenn viele Programmierer das grundsätzlich nicht glauben.
Beginnen wir ein neues Thema, in dem wir versuchen zu verstehen, dass eine Funktion eine spezielle Variable ist.
Spezielle Variablen: Funktionen
Wenn wir über Funktionen sprechen, denken Leute mit grundlegenden Programmierkenntnissen zuerst an die Verwendung eines zusätzlichen Aufrufs, um eine Prozedur in unserer Anwendung auszuführen. Diese Definition ist jedoch nicht ganz ausreichend, und zwar aufgrund der Tatsache, dass es Unterprogramme gibt, die keine Funktionen, sondern Prozeduren sind. Es gibt einen kleinen konzeptionellen Unterschied zwischen diesen beiden Typen. Der Hauptunterschied besteht darin, dass eine Funktion einen Wert oder genauer gesagt einen Datentyp zurückgibt. Im Gegensatz zu einer Prozedur, die nach der Ausführung keine Daten zurückgibt. Beide Typen können jedoch durch ihre Argumente Werte ändern und zurückgeben.
Machen Sie sich darüber jetzt keine Gedanken, ich gebe nur einen kurzen Überblick über das, was später ausführlicher besprochen wird. Aber diese kleine Unterscheidung ist notwendig, damit Sie verstehen, warum ich sage, dass eine Funktion ein spezieller Variablentyp ist.
Es ist wichtig, hier eine Parallele zwischen verschiedenen Sprachen zu ziehen. Denn je nachdem, welche Sprache wir in Zukunft lernen möchten, ist das hier besprochene Konzept möglicherweise nicht anwendbar. Dies liegt daran, dass jede Sprache Aufgaben auf ihre eigene Weise erfüllt.
In Skriptsprachen wie JavaScript und Python werden Funktionen normalerweise als eine Form konstanter Variablen implementiert. In C und C++ kann eine Funktion jedoch entweder als konstante Variable oder als Variable fungieren, die den Wert einer statischen Variable innerhalb der Funktion ändern kann, ohne dass Argumente übergeben werden müssen. Für manche mag das ungewöhnlich oder sogar kontraintuitiv klingen, da es so aussieht, als ob es nicht möglich sein sollte. In C und C++ ermöglichen Zeiger diese Funktionalität jedoch. Durch die Verwendung eines Zeigers zum Verweisen auf eine statische Variable innerhalb einer Funktion kann externer Code, der die Funktion aufruft, den Wert dieser statischen Variable ändern.
Diese Eigenschaft macht die Programmierung in C und C++ sowohl anspruchsvoll als auch leistungsstark. Zwar bringt es potenzielle Risiken mit sich, bietet Entwicklern jedoch auch ein hohes Maß an Flexibilität. In reinem MQL5 befinden wir uns irgendwo zwischen der Funktionsverarbeitung von JavaScript/Python und den Möglichkeiten von C und C++. Zwar verfügen wir nicht über das gleiche Maß an Flexibilität wie in C und C++, MQL5 bietet jedoch eine sicherere Codierungsumgebung.
Um über die mitunter recht trockene Theorie hinauszugehen, wollen wir uns einige einfache Beispiele ansehen, wie Funktionen als konstante Variablen verwendet werden können. Ein gängiges Szenario wäre die Verwendung einer Funktion zum Definieren eines Werts – mit dem Unterschied, dass wir statt der direkten Zuweisung eines Werts einen aussagekräftigeren und repräsentativeren Namen verwenden.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. Print(One_Radian()); 07. } 08. //+------------------------------------------------------------------+ 09. double One_Radian() 10. { 11. return 180. / M_PI; 12. } 13. //+------------------------------------------------------------------+
Code 01
In Codebeispiel 01 haben wir ein sehr einfaches Beispiel. Wenn dieser Code ausgeführt wird, sehen Sie das unten gezeigte Ergebnis.

Abbildung 01
An diesem Punkt fragen Sie sich vielleicht: Warum machen Sie die Dinge so? Wäre es nicht einfacher, die Berechnung aus Zeile 11 direkt in Zeile 06 zu platzieren und das Ergebnis auszudrucken? Ja, das wäre es. Wenn das jedoch Ihr Gedanke war, bedeutet das, dass Sie nicht ganz verstanden haben, was ich mit diesem Beispiel verdeutlichen möchte. Der Zweck besteht hier NICHT darin, die Berechnung selbst durchzuführen. Das Ziel besteht darin, eine Konstante zu generieren, die global im gesamten Code verwendet werden kann. Wenn Sie den Code jedes Mal neu eingeben müssen, wenn Sie den Wert einer Konstante benötigen, um diese zu erstellen, wäre der Arbeitsaufwand beim Verbessern oder sogar Korrigieren des Codes enorm. Wenn jedoch alles in einer Funktion mit einem aussagekräftigen Namen enthalten ist, die die Konstante generiert, ist der Vorgang viel einfacher, schneller und reibungsloser, was Sie als Programmierer effizienter macht.
Stellen Sie sich die Entwicklung eines 3D-Programms vor, bei dem Sie Werte von Grad in Radiant umrechnen müssen – und zwar nicht nur in einer Codezeile, sondern über Hunderte von Zeilen hinweg, die über die gesamte Codebasis verstreut sind. Lassen Sie mich Sie fragen: Was wäre einfacher: die Berechnung jedes Mal einzugeben oder alles in eine Funktion wie die im Codebeispiel 01 gezeigte zu platzieren?
Nun, das mag eine Anwendung sein, aber wenn Sie sie trivial oder sinnlos finden, wie wäre es dann, wenn wir mithilfe des Wissens, das wir in den vorherigen Artikeln gewonnen haben, etwas Ausgefeilteres erstellen? Dies kann wie folgt erfolgen:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. Counting(); 07. Print(Counting()); 08. Counting(); 09. Counting(); 10. Print(Counting()); 11. } 12. //+------------------------------------------------------------------+ 13. ulong Counting(void) 14. { 15. static ulong Tic_Tac = 0; 16. 17. Tic_Tac = Tic_Tac + 1; 18. 19. return Tic_Tac; 20. } 21. //+------------------------------------------------------------------+
Code 02
Wenn Codebeispiel 02 ausgeführt wird, erhalten Sie ein Ergebnis ähnlich dem unten gezeigten:

Abbildung 02
Mit anderen Worten: Wenn Ihnen das erste Beispiel nicht besonders passend erschien, dann vielleicht das zweite. Dies liegt daran, dass wir möglicherweise zählen müssen, wie oft ein Ereignis in einer bestimmten Anwendung aufgetreten ist. Viele würden eine globale Variable verwenden, um solche Ereignisse zu verfolgen. Allerdings hat die Verwendung globaler Variablen, wie bereits in früheren Artikeln erläutert, auch Nachteile. Durch die Verwendung einer Funktion mit ähnlichem Zweck beseitigen wir die Unannehmlichkeiten globaler Variablen und gewährleisten gleichzeitig die Stabilität und Sicherheit unseres Codes. Gleichzeitig können wir die Logik viel einfacher, schneller und praktischer ändern.
Vordefinierte Variablen
Zusätzlich zu den Themen, die wir bereits zu Variablen und Konstanten behandelt haben, gibt es noch weitere wichtige Typen in MQL5, die erwähnt werden müssen. Der Grund hierfür ist, dass sie integraler Bestandteil von Programmen sind, die die Funktionen der Sprache in großem Umfang nutzen.
Hier werden wir kurz auf diese speziellen Variablentypen eingehen. Es ist wichtig, dass Sie die Existenz dieser Variablen verstehen und dass Sie sie für bestimmte Aufgaben verwenden können.
Weitere Details zu diesen Variablen finden Sie in der Dokumentation unter Vordefinierte Variablen. Wir werden uns hier jedoch nicht auf ein bestimmtes konzentrieren. Wichtig ist zu verstehen, dass es sich dabei zwar um sogenannte vordefinierte Variablen handelt, jedoch nicht um typische Variablen. In unserem Code werden sie als Konstanten behandelt. Für MetaTrader 5 handelt es sich jedoch tatsächlich um Variablen. Obwohl sie in unserem Code als Konstanten behandelt werden, gibt es einige Fälle, in denen wir bestimmte Funktionen oder Prozeduren in MQL5 verwenden können, um den Wert dieser Variablen zu ändern.
Es ist sehr wichtig, lieber Leser, dass Sie dies verstehen. Wenn Sie versuchen, den Wert einer dieser Variablen direkt zu ändern, tritt ein Fehler auf und Ihr Code wird nicht kompiliert. Mit „direkt ändern“ meine ich den Versuch, etwas wie das unten gezeigte Beispiel zu tun:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. Print(_LastError); 07. _LastError = 2; 08. Print(_LastError); 09. } 10. //+------------------------------------------------------------------+
Code 03
Wenn Sie mit MQL5 noch nicht vertraut sind, achten Sie genau auf meine folgende Erklärung. In Zeile 6 des Codebeispiels 03 weisen wir das Terminal an, den aktuellen Wert auszudrucken, der in der vordefinierten Variable _LastError gespeichert ist. Da es sich hierbei um eine vordefinierte Variable in MQL5 handelt, muss sie nicht deklariert werden. Der Compiler kann es automatisch erkennen.
Wenn der Compiler jedoch auf Zeile 7 im Codebeispiel 03 stößt, wird ein Fehler ausgelöst, wie Sie im folgenden Bild sehen können:

Abbildung 03
Warum ist das so? Der Grund dafür ist, dass diese vordefinierten Variablen in MQL5 auf Programmierebene als Konstanten behandelt werden. Auf der Codeausführungsebene in MetaTrader 5 werden diese Variablen jedoch nicht als Konstanten betrachtet.
Dies kann zunächst verwirrend und schwer verständlich erscheinen, insbesondere für Programmieranfänger. Bevor Sie sich jedoch näher mit vordefinierten Variablen befassen, lieber Leser, müssen Sie Folgendes verstehen: Da diese Variablen in jedem MQL5-Code vorhanden sind, sollten Sie NICHT versuchen, eine andere Variable mit demselben Namen wie diese vordefinierten Variablen zu erstellen. Dies würde einen Verstoß gegen die von der Plattform auferlegten Sicherheitsprotokolle darstellen.
Obwohl der Versuch eines direkten Zugriffs, wie im Codebeispiel 03 gezeigt, zu einem Fehler führt, bietet die MQL5-Sprache selbst Möglichkeiten, einige dieser vordefinierten Variablen für allgemeine Zwecke zu ändern. Es gibt keine spezielle Regel, die vorschreibt, welchen Wert Sie diesen Variablen zuweisen sollten. Für jede Änderung sollte jedoch, wenn möglich, ein berechtigter Grund vorliegen. Sie sollten den Wert dieser Variablen nicht nur ändern, weil es technisch möglich ist, es sei denn, Sie studieren die Sprache selbst, wie wir es hier tun.
Mithilfe der Funktion SetUserError können wir beispielsweise einen Wert für die vordefinierte Variable _LastError festlegen. Aber nicht irgendein Wert. Der Wert, den wir zuweisen können, ist begrenzt, da eine Liste von Fehlercodes vorhanden ist, die für die Verwendung durch MQL5 selbst reserviert sind. Für viele kann dies entmutigend wirken, da sie es möglicherweise als Einschränkung betrachten. Dies ist jedoch nicht unbedingt der Fall.
Um dies in der Praxis zu sehen, können Sie den unten gezeigten Code verwenden:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. Print(_LastError); 07. SetUserError(2); 08. Print(_LastError); 09. } 10. //+------------------------------------------------------------------+
Code 04
Dieser Code wird tatsächlich kompiliert und kann ausgeführt werden. Wenn Sie es jedoch in MetaTrader 5 ausführen, sehen Sie etwas Ähnliches wie im Bild unten.

Abbildung 04
Was ist das für eine seltsame Nummer? Warten Sie eine Minute. In Zeile 7 von Codebeispiel 04 weisen wir denselben Wert zu wie in Zeile 7 von Codebeispiel 03. Okay, Codebeispiel 03 wurde aus den besprochenen Gründen nicht kompiliert, aber trotzdem entspricht dieser Wert in Abbildung 04 nicht meinen Erwartungen.
Tatsächlich, lieber Leser, ist der angezeigte Wert der, den Sie der Variable _LastError zuweisen wollten, er wurde jedoch verschoben. Dadurch wird verhindert, dass es zu Konflikten mit vordefinierten Fehlerwerten kommt. Um den genauen Wert anzuzeigen, den Sie _LastError zuweisen wollten, ist eine kleine Anpassung erforderlich. Doch bevor wir uns diese Anpassung ansehen, müssen wir zunächst etwas anderes verstehen. Normalerweise ziehen es viele Programmierer vor, den Namen einer vordefinierten Variable nicht direkt im Code zu verwenden. Im Allgemeinen ist es ratsamer, eine Funktion zu verwenden, die den Wert einer vordefinierten Variablen zurückgibt. Obwohl Sie nichts daran hindert, den Code wie oben gezeigt zu verwenden, besteht der üblichere (und im Allgemeinen bevorzugte) Ansatz darin, eine Codestruktur wie die unten gezeigte zu verwenden:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. Print(GetLastError()); 07. SetUserError(2); 08. Print(GetLastError()); 09. } 10. //+------------------------------------------------------------------+
Code 05
Beachten Sie, dass der Code im Wesentlichen dasselbe tut. Für viele ist Code 05 jedoch besser lesbar als Code 04. Auf diese Weise teilen wir anderen Programmierern, die den Code lesen, mit, dass wir auf den Wert einer vordefinierten Variablen zugreifen. Und ist Ihnen aufgefallen, dass das, was im vorherigen Thema besprochen wurde, auch hier anwendbar ist? Der Zugriff auf vordefinierte Variablen zum späteren Lesen kann auf beliebige Art und Weise erfolgen. Die Methode zur Änderung ihrer Werte bleibt jedoch immer dieselbe. Um beispielsweise einen beliebigen Wert aus der Variable _LastError zu entfernen, DÜRFEN wir NICHT das in Zeile 7 der obigen Codes gezeigte Verfahren verwenden. Wenn wir dies tun, weisen wir einen neuen Fehlerwert zu. Die richtige Möglichkeit zum Löschen oder Entfernen von Fehlern aus _LastError besteht in der Verwendung eines anderen in MQL5 bereitgestellten Verfahrens. Er ist unten aufgeführt:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. Print(GetLastError()); 07. SetUserError(2); 08. Print(GetLastError()); 09. SetUserError(0); 10. Print(GetLastError()); 11. ResetLastError(); 12. Print(GetLastError()); 13. } 14. //+------------------------------------------------------------------+
Code 06
Wenn Sie dies zum ersten Mal sehen, passen Sie bitte gut auf, lieber Leser. Wenn dieser Code ausgeführt wird, ist das Ergebnis wie unten gezeigt.

Abbildung 05
Beachten Sie Folgendes: Wenn Zeile 6 ausgeführt wird, wird die erste Zeile, die Sie in Abbildung 05 sehen, gedruckt. Sie zeigt an, dass in der Variable _LastError kein Fehler oder Wert vorliegt. Sobald Zeile 7 ausgeführt wird, weisen wir der Variablen _LastError einen Wert zu, wie zuvor gesehen. Viele Anfänger versuchen jedoch, mit Zeile 9 die Variable _LastError zu löschen. Aber wenn wir dies tun und das Ergebnis ausdrucken, erhalten wir einen Wert ungleich Null. Warum? Der Grund ist derselbe, den ich bereits erwähnt habe. Wenn wir SetUserError verwenden, um einen Wert in _LastError festzulegen, wird der Wert verschoben. Aus diesem Grund führt der Versuch, mit dieser Funktion einen beliebigen Wert festzulegen, nicht zum erwarteten Ergebnis. Da unser Ziel hier jedoch darin besteht, den Wert Null in der Variable _LastError festzulegen, ist die korrekte Vorgehensweise hierfür die Verwendung von Zeile 11. Bei der Ausführung der Prozedur in Zeile 11 wird _LastError tatsächlich der Wert Null zugewiesen.
Dies ist auch für alle anderen vordefinierten Variablen zu beachten. Natürlich werde ich nicht jeden einzelnen Punkt einzeln durchgehen, da Sie in der Dokumentation nachschlagen können, um die korrekten Verfahren (sofern vorhanden) zum Festlegen der Werte für diese Variablen zu verstehen. Das Lesen dieser Werte kann jedoch, wie am Anfang des Themas gezeigt, direkt erfolgen, d. h. durch Verwendung des Namens der vordefinierten Variable selbst.
Aber warten Sie einen Moment. Ich habe die hier erläuterten Punkte verfolgt und verstanden, begreife jedoch immer noch nicht ganz das Konzept des Wertausgleichs, das Sie im Verlauf des Themas mehrfach erwähnt haben. Könnten Sie das besser erklären? Auf diese Weise kann ich möglicherweise die Werte verwenden, die ich der Variable „_LastError“ zugewiesen habe, anstatt diese seltsamen Zahlen zu sehen, wenn ich den Variablenwert im Terminal ausdrucke.
Tatsächlich handelt es sich hierbei um eine berechtigte Anfrage, die einer ausführlichen Erläuterung bedarf. Lassen Sie uns dies in einem neuen Thema behandeln, da wir auch andere verwandte Konzepte ansprechen werden.
Enumerationen und Konstantendefinitionen
Einer der schwierigsten Aspekte, der in jeder Programmiersprache definiert werden muss, ist meiner Meinung nach die Existenz von Definitionen und Enumerationen in der Sprache. Verstehen Sie mich nicht falsch, lieber Leser. Beide sind weit verbreitet und eine große Hilfe beim Programmieren. Allerdings lässt sich allein anhand der Dokumentation nur schwer feststellen, was eine Enumeration und was eine Definition ist. Dies kann man nämlich nur anhand der Dokumentation herausfinden, in der angegeben ist, welche Art der Modellierung verwendet wird.
Ohne jetzt gleich in die Details dieser Konzepte einzutauchen (da wir sie später behandeln werden), können wir damit beginnen, zu verstehen, was jeder Wert darstellt. Noch wichtiger ist, dass wir uns auf das Verständnis einiger Elemente konzentrieren, die häufig in Code vorkommen, beispielsweise Texte, die auf den ersten Blick möglicherweise nicht viel Sinn ergeben. Ein Beispiel hierfür ist im folgenden Code zu sehen:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. int OnInit() 05. { 06. return INIT_SUCCEEDED; 07. }; 08. //+------------------------------------------------------------------+ 09. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 10. { 11. return rates_total; 12. }; 13. //+------------------------------------------------------------------+
Code 07
In Zeile 6 haben wir etwas, das für viele möglicherweise nicht viel Sinn ergibt, obwohl es in fast jedem Indikator-Code vorkommt. Darauf werden wir, liebe Leser, später noch genauer eingehen. Aber ich zeige Ihnen diesen Code (Code 07) nur, um etwas zu veranschaulichen, was Ihnen in vielen Codes häufig begegnen wird. Obwohl das reservierte Wort „return“ eine Variable oder einen Wert erwartet, verwenden wir hier einen String. Aber was bedeutet diese Zeichenfolge und warum verwenden wir sie anstelle einer anderen? In Wirklichkeit könnten wir etwas anderes gebrauchen. Aber lassen Sie uns einen Schritt nach dem anderen machen.
Wenn Sie sich Konstanten, Enumeratoren und Strukturen ansehen, werden Sie feststellen, dass in MQL5 viele Konstanten und Enumerationen definiert sind. Diese Enumerationen und Konstanten, wie die, die Sie in Zeile 6 von Code 07 sehen können, erhalten in der Programmierung Namen. Diese Entitäten werden als Aliase bezeichnet. Und ja, die Schreibweise ist exakt wie gezeigt, da in Programmiersprachen keine Akzente verwendet werden können. Das Ziel besteht darin, eine für uns besser einprägsame Darstellung zu erstellen oder den Code leichter lesbar zu machen. Nicht nur für die Person, die es geschrieben hat, sondern auch für andere Programmierer.
Um dies deutlicher zu machen, denken Sie, lieber Leser, über Folgendes nach. Angenommen, Sie programmieren etwas und möchten einen wahren Wert zurückgeben, um anzuzeigen, dass Ihr Code eine Operation erfolgreich ausgeführt hat. Wie würden Sie das machen?
Sie könnten einen Wert größer als Null verwenden oder sogar das Wort „true“ schreiben. Theoretisch würde beides funktionieren. Das heißt, der Wert in Zeile 6 von Code 07 könnte wie unten gezeigt geschrieben werden.
//+------------------------------------------------------------------+ #property copyright "Daniel Jose" //+------------------------------------------------------------------+ int OnInit() { return true; }; //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) { return rates_total; }; //+------------------------------------------------------------------+ void OnDeinit(const int reason) { Print(reason); }; //+------------------------------------------------------------------+
Kennziffer 08
Wenn Sie dies jedoch tun, schlägt Ihr Code fehl. Aber warum wird es scheitern? Das macht keinen Sinn, denn mit dem Wert „true“ signalisieren wir ja, dass OnInit erfolgreich abgeschlossen wurde. Ja, lieber Leser, wenn wir „true“ zurückgeben, signalisieren wir tatsächlich, dass etwas ohne Fehler ausgeführt wurde. Trotzdem schlägt Ihr Code fehl und wird sofort nach der Ausführung von OnInit beendet. Der Grund hierfür liegt in dem Wert, der dem Alias INIT_SUCCEEDED zugeordnet ist.
Es ist nicht so, dass Sie dort keinen anderen Wert verwenden können, aber Sie müssen verstehen, dass INIT_SUCCEEDED einfach eine besser lesbare Darstellung eines zugrunde liegenden Werts ist. Wenn Sie die Dokumentation überprüfen, werden Sie sehen, dass INIT_SUCCEEDED tatsächlich 0 entspricht. In der Booleschen Logik steht 0 für falsch. Wenn Sie also stattdessen „true“ zurückgeben, schlägt Code 08 mit Sicherheit fehl, auch wenn er keine tatsächlichen Fehler enthält.
Wichtiges Detail: Wenn Sie Code 08 ausführen, wird unmittelbar nach der Ausführung die folgende Meldung im Terminal angezeigt.

Abbildung 06
Ich verstehe, dass das im Moment vielleicht nicht viel Sinn ergibt, aber ich versichere Ihnen, dass es mit der Zeit Sinn ergeben wird. Anstelle von INIT_SUCCEEDED könnten Sie in Zeile 6 von Code 08 den Wert 0 oder false verwenden. Dies würde perfekt funktionieren und den Indikator weiterlaufen lassen, bis Sie ihn aus dem Chart entfernen. Dies ist jedoch nicht die einzige Möglichkeit, mit dieser Situation umzugehen. Sie können auch einen Ansatz wie den unten gezeigten verwenden:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. int OnInit() 05. { 06. return ERR_SUCCESS; 07. }; 08. //+------------------------------------------------------------------+ 09. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 10. { 11. return rates_total; 12. }; 13. //+------------------------------------------------------------------+ 14. void OnDeinit(const int reason) 15. { 16. Print(reason); 17. }; 18. //+------------------------------------------------------------------+
Kennziffer 09
Im Fall des Codes 09 erhalten Sie, genau wie bei der Verwendung des Wertes 0 oder false in Zeile 6 des Codes 08, das unten dargestellte Ergebnis, sobald der Indikator aus dem Chart entfernt wird.

Abbildung 07
Beachten Sie, dass es keine strenge Regel gibt, die vorschreibt, wie ein Code geschrieben werden muss – nur Richtlinien, die Sie verstehen sollten, um sicherzustellen, dass alles richtig funktioniert. Nun nehmen wir an, Sie sind ein äußerst gewissenhafter Programmierer, der in seinen Anwendungen keine Fehler toleriert. In diesem Fall könnten Sie den unten gezeigten Code verwenden:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. int OnInit() 05. { 06. return GetLastError(); 07. }; 08. //+------------------------------------------------------------------+ 09. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 10. { 11. return rates_total; 12. }; 13. //+------------------------------------------------------------------+ 14. void OnDeinit(const int reason) 15. { 16. Print(reason); 17. }; 18. //+------------------------------------------------------------------+
Code 10
Dies wäre ein Extremfall, denn wenn zu irgendeinem Zeitpunkt während der Ausführung von OnInit ein Fehler auftritt, unterscheidet sich der zurückgegebene Wert von Null oder den zuvor erwähnten Bezeichnungen, die einen Nullwert anzeigen. Dies liegt daran, dass GetLastError, das wir zuvor besprochen haben, zeigen würde, dass _LastError einen Wert enthält. Sie müssen sich jedoch darüber im Klaren sein, dass der Fehler überall im Code auftreten kann, unabhängig davon, ob er Teil Ihrer Programmierung ist oder nicht. Manchmal entstehen Fehler nicht aufgrund eines Fehlers im Code selbst, sondern aufgrund von Interaktionen zwischen Werten. Dies kann aus verschiedenen Gründen dazu führen, dass _LastError einen Wert ungleich Null enthält. Aus diesem Grund gilt diese Art der Fehlerbehandlung als extrem. Es ist selten, Code zu sehen, der diesen Ansatz tatsächlich verwendet. Wer jedoch damit experimentieren möchte, wird feststellen, dass unerwartete und manchmal auch amüsante Dinge passieren können.
Auf diese Weise gewinnen Sie an Reife und entwickeln ein tieferes Verständnis für den Umgang mit unerwarteten Situationen. Aber gehen Sie vorsichtig und mit Geduld vor, lieber Leser. Da wir uns bisher nicht mit der Filterung von Fehlern befasst haben, kann selbst das kleinste Detail Sie völlig verwirren und Sie fragen sich, warum eine Anwendung ohne ersichtlichen Grund manchmal funktioniert und manchmal nicht.
Als letztes Thema dieses Artikels wollen wir nun sehen, wie sich die Verwendung intern definierter Konstanten und Enumerationen in MQL5 auf unsere Arbeit auswirken kann. Wie bereits erwähnt, können wir mithilfe der Funktion SetUserError der Systemvariablen _LastError einen beliebigen Wert zuweisen. Was aber, wenn wir genau wissen möchten, welcher Wert von SetUserError festgelegt wurde? Das ist eigentlich ganz einfach. In der MQL5-Dokumentation für SetUserError wird erläutert, wie Sie dies anpassen. Ein Teil des Textes ist unten wiedergegeben:
Setzt die vordefinierte Variable _LastError auf einen Wert gleich ERR_USER_ERROR_FIRST + user_error.
Mit anderen Worten: Wenn wir ERR_USER_ERROR_FIRST vom in _LastError gefundenen Wert abziehen, können wir den genauen Wert bestimmen, der von SetUserError gesetzt wurde. Hier ist eine Beispielimplementierung im folgenden Code:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. Print(GetLastError()); 07. SetUserError(2); 08. Print(GetLastError()); 09. Print(GetLastError() - ERR_USER_ERROR_FIRST); 10. } 11. //+------------------------------------------------------------------+
Code 11
Nach der Ausführung von Code 11 können wir Folgendes auf dem Terminal sehen:

Abbildung 08
Beachten Sie, dass drei Werte angezeigt werden. Für uns sind jedoch die zweite und dritte Stelle interessant, da hier die Wertekorrektur gerade deshalb durchgeführt wird, um festzustellen, welcher Wert von der Prozedur SetUserError zur Anzeige des Fehlers zugewiesen wurde.
Abschließende Überlegungen
Ich habe eine kleine Anmerkung zu dem, was hier gezeigt wird. Sie könnten meinen, dass Sie, wenn Sie in SetUserError einen negativen Wert verwenden, den von MQL5 definierten Fehlerwertebereich erreichen können, um etwas Besonderes zu melden. Negative Werte haben jedoch KEINE Auswirkung. Dies hängt mit dem Datentyp zusammen, der von der Prozedur SetUserError erwartet wird.
Diese Situationen werden im nächsten Artikel betrachtet und erläutert. Einige der hier angegebenen Codes sind im Anhang zu finden. Verwenden Sie sie, um alles, was in diesem Artikel vorgestellt wird, selbständig zu studieren. Viel Spaß beim Lernen und bis zum nächsten Artikel!
Übersetzt aus dem Portugiesischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/pt/articles/15304
Warnung: Alle Rechte sind von MetaQuotes Ltd. vorbehalten. Kopieren oder Vervielfältigen untersagt.
Dieser Artikel wurde von einem Nutzer der Website verfasst und gibt dessen persönliche Meinung wieder. MetaQuotes Ltd übernimmt keine Verantwortung für die Richtigkeit der dargestellten Informationen oder für Folgen, die sich aus der Anwendung der beschriebenen Lösungen, Strategien oder Empfehlungen ergeben.
Neuronale Netze im Handel: Verwenden von Sprachmodellen für die Zeitreihenprognose
Neuronale Netze im Handel: Leichtgewichtige Modelle für die Zeitreihenprognose
Entwicklung eines Replay-Systems (Teil 59): Eine neue Zukunft
Künstlicher Bienenstock-Algorithmus (ABHA): Tests und Ergebnisse
- 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.
Sehen Sie sich den neuen Artikel an: Von der Grundstufe zur Mittelstufe: Variablen (III).
Autor: CODE X
Nachdem ich soeben den Titel "Spezielle Variablen" gelesen habe: Funktionen" gelesen habe, dachte ich, dass der Artikel den speziellen Typ "Funktionszeiger" behandeln würde.
Dies wird später in einem anderen Artikel behandelt, der speziell für diesen Zweck erstellt wurde. Denn um zu verstehen, warum man Zeiger auf Funktionen verwendet, muss man zunächst eine andere Art von Konzept verstehen, nämlich den Umgang mit Ereignissen 🙂👍.