Von der Grundstufe bis zur Mittelstufe: Indikator (II)
Im vorherigen Artikel „Von der Grundstufe zur Mittelstufe: Indikator (I)“ haben wir eine praktische Diskussion darüber begonnen, wie man mit minimalen Kenntnissen einen sehr einfachen und leichtgewichtigen Indikator erstellen kann. Auch wenn viele denken, dass zur Erzielung von Ergebnissen umfangreiche Programmierkenntnisse erforderlich sind, stellt sich heraus, dass auch ein Anfänger mit etwas Mühe etwas relativ Einfaches und Funktionelles erstellen kann. Und zwar auf recht elegante Weise, da der Großteil der Arbeit vom MetaTrader 5 übernommen wird. Es liegt an uns, richtig auf die Ereignisse zu reagieren. Dann weiß MetaTrader 5, was er mit den restlichen Informationen, die wir während dieser Ereignisse verarbeiten, tun soll.
Sie werden vielleicht denken: „Aber der Code, den wir erstellt haben, ähnelt keinem Indikator, den ich je gesehen habe. Und obwohl es uns gelingt, etwas auf dem Chart zu zeichnen, ist es nicht ganz das, was ich erwartet habe – ich hatte gehofft, ich könnte viel mehr tun.“
Nun gut, liebe Leserin, lieber Leser, Sie mögen denken, dass dies alles nicht sehr beeindruckend aussieht, aber Sie müssen bedenken, dass Wissen nicht über Nacht erworben wird: Es muss kultiviert werden. Das Ziel dieser Artikelserie ist genau das: Wissen zu kultivieren und zu verbreiten. Es geht nicht darum, dem Grundsatz zu folgen: „Tu dies, weil es nur so funktioniert“. Schauen wir also, wie weit wir im letzten Artikel gekommen sind.
Wie man einen gleitenden Durchschnitt mit Periodenlänge X implementiert
Im vorherigen Artikel haben wir uns angesehen, wie man Informationen in einem Chart darstellt. Wir haben dies auf eine einfache, leicht verständliche Weise getan. Das Ziel des Artikels war es jedoch, zu zeigen, dass es eine bessere Möglichkeit gibt, die in MetaTrader 5 verfügbaren Ressourcen zu nutzen.
Was im vorigen Artikel gezeigt wurde, kann jedoch auch auf andere Weise erreicht werden. Aber ich denke, viele würden gerne sehen, wie ein allgemeineres System des gleitenden Durchschnitts umgesetzt werden könnte. Mit anderen Worten, es wäre interessant, gleitende Durchschnitte zu erstellen, die beispielsweise dem berühmten exponentiellen gleitenden Durchschnitt mit einer Periodenlänge von 9 ähneln, der gerade deshalb so bekannt ist, weil er ein sehr profitables Handelsmodell darstellt, auch wenn er trotz seiner extremen Einfachheit schwer zu verstehen ist.
Vielleicht denken Sie jetzt darüber nach, wie Sie den langweiligen Code, der im vorigen Artikel implementiert wurde, in etwas Interessanteres verwandeln können. Dies zu erreichen, könnte eine viel größere Herausforderung sein. Ich denke, Sie werden etwas mit viel mehr Codezeilen erstellen müssen, nicht so wie im vorherigen Artikel gezeigt.
Nun, Herausforderung angenommen: Heute werden wir einen Indikator für den gleitenden Durchschnitt mit einem Minimum an Code erstellen – je weniger Code, desto besser. Doch während dies für viele eine Herausforderung ist, finde ich es langweilig und eintönig. Bevor wir sehen, wie das geht, wollen wir den Code aus dem vorherigen Artikel noch einmal durchgehen:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #property indicator_type1 DRAW_LINE 05. #property indicator_color1 clrBlack 06. //+----------------+ 07. #property indicator_buffers 1 08. #property indicator_plots 1 09. //+----------------+ 10. double gl_buffer[]; 11. //+------------------------------------------------------------------+ 12. int OnInit() 13. { 14. SetIndexBuffer(0, gl_buffer, INDICATOR_DATA); 15. 16. return INIT_SUCCEEDED; 17. }; 18. //+------------------------------------------------------------------+ 19. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 20. { 21. for (int c = prev_calculated; c < rates_total; c++) 22. gl_buffer[c] = price[c]; 23. 24. return rates_total; 25. }; 26. //+------------------------------------------------------------------+
Code 01
Ich denke, Sie haben bereits mit dem Code 01 geübt, denn er ist sehr einfach und interessant und ermöglicht es uns, Dinge zu verstehen, die sonst schwer zu begreifen wären. Nun zur Sache: Wie können wir diesen Code dazu bringen, einen gleitenden Durchschnitt zu berechnen und darzustellen? Um dies zu erreichen, müssen wir zunächst einige Entscheidungen treffen. Sie sind nicht obligatorisch, aber nützlich, um zu üben, wie wir über den Code, den wir implementieren wollen, nachdenken.
Die erste Entscheidung, die wir treffen müssen, ist die Art der Berechnung, die wir verwenden wollen. Ja, es gibt Unterschiede zwischen den Berechnungen des gleitenden Durchschnitts. Es gibt gewichtete und ungewichtete gleitende Durchschnitte. Diese Gewichtung kann auf der Grundlage von Volumen, Preis oder Zeit erfolgen. Es ist sehr wichtig, dies zu verstehen und zu wissen, wie man sich entscheidet. Bei einem exponentiellen gleitenden Durchschnitt wird er nach der Zeit oder nach der Periodenlänge gewichtet, die er enthält. Ist der VWAP ein nach Volumen gewichteter Kurswert oder umgekehrt? Wer kennt die Antwort? In jedem Fall wird die Zeit nicht berücksichtigt. Bei einem arithmetischen gleitenden Durchschnitt gibt es keine solche Gewichtung, und der Wert wird nur unter Berücksichtigung der Daten selbst, nicht aber ihrer Veränderung im Verhältnis zu anderen Daten berechnet. Der Einfachheit halber nennen wir diese Durchschnitte „exponentieller gleitender Durchschnitt“ und „einfacher (simple) gleitender Durchschnitt“.
Es ist die Beziehung zwischen einer Information und einer anderen, die die Gewichtungsfaktoren bestimmt. Wir werden uns jedoch nicht in langweilige, mathematische Details vertiefen, da dies in diesem Fall unnötig ist. Aber wir müssen zumindest die Formel für die Art der Berechnung kennen, die wir durchführen wollen. Wie könnten wir sonst etwas umsetzen, ohne zu verstehen, wie es berechnet wird? Da der arithmetische gleitende Durchschnitt sehr einfach zu berechnen ist (man muss nur Werte über eine bestimmte Anzahl von Perioden addieren und subtrahieren), werden wir einen etwas komplexeren Fall betrachten: den exponentiellen Durchschnitt, den viele für einen solchen halten.
Die Formel, die wir verwenden werden, lautet wie folgt:

Abbildung 01
Abbildung 01 zeigt den Ausdruck für die Berechnung eines exponentiellen Durchschnitts mit einer beliebigen Anzahl von Perioden. In dieser Formel steht P für den aktuellen Kurswert. Der Wert M ist der bisherige exponentielle Durchschnittswert. N ist die Anzahl der für die Mittelwertbildung verwendeten Perioden. Mit diesen Informationen können wir mit der Umsetzung der Berechnung fortfahren. Zunächst möchte ich ein kleines Detail klären, das ich kürzlich erwähnt habe: Das arithmetische Mittel wird berechnet, indem X-Werte addiert und das Ergebnis durch das verwendete X geteilt wird.
Wenn wir jedoch zur Berechnung von X + 1, d. h. der nächsten Periode, übergehen, müssen wir nur den Wert des ersten Eintrags von der Summe abziehen und den Wert des neuen Eintrags addieren. Dann teilen wir das Ergebnis durch X. Deshalb habe ich gesagt, dass das arithmetische Mittel langweilig ist, da wir nur addieren und subtrahieren müssen. Man geht jedoch davon aus, dass die exponentielle Mittelwertbildung sehr rechenaufwändig ist. Sie werden jedoch feststellen, dass es fast dasselbe ist wie das arithmetische Mittel.
Sehen wir uns nun an, was dieser Ausdruck in Bild 01 bedeutet: Um den aktuellen EMA (Exponential Moving Average) zu berechnen, benötigen wir den vorherigen Durchschnittswert. Hmm... es scheint, dass wir ein Problem haben, denn wenn wir die Berechnung beginnen, haben wir diesen Wert noch nicht. Deshalb brauchen wir eine kleine mathematische Abhilfe. Die Lösung besteht einfach darin, M auf Null zu setzen. Jetzt können wir die Berechnung durchführen. Dazu reicht es aus, sich den Code 01 anzusehen und festzustellen, wo er geändert werden muss. In diesem Fall muss nur die Zeile 22 geändert werden. Aber um Missverständnisse zu vermeiden und um Ihnen zu zeigen, dass es nicht nötig ist, den Code zu verkomplizieren, werde ich den vollständigen Code präsentieren:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #property indicator_type1 DRAW_LINE 05. #property indicator_color1 clrBlack 06. //+----------------+ 07. #property indicator_buffers 1 08. #property indicator_plots 1 09. //+----------------+ 10. double gl_buffer[]; 11. //+------------------------------------------------------------------+ 12. int OnInit() 13. { 14. SetIndexBuffer(0, gl_buffer, INDICATOR_DATA); 15. 16. return INIT_SUCCEEDED; 17. }; 18. //+------------------------------------------------------------------+ 19. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 20. { 21. for (int c = prev_calculated; c < rates_total; c++) 22. gl_buffer[c] = (price[c] - (c ? gl_buffer[c - 1] : 0)) * (2. / (1.0 + 9)) + (c ? gl_buffer[c - 1] : 0); 23. 24. return rates_total; 25. }; 26. //+------------------------------------------------------------------+
Code 02
Bitte beachten Sie, dass wir nur die Zeile 22 im Code geändert haben. SONST WURDE NICHTS GEÄNDERT. Bei der Ausführung des Codes sehen wir jedoch Folgendes:

Abbildung 02
Das fragen Sie sich wahrscheinlich: „Aber sehen wir in diesem Bild 02 wirklich einen neunperiodigen exponentiellen gleitenden Durchschnitt? Es ist kaum zu glauben, dass so wenig nötig ist, um einen exponentiellen gleitenden Durchschnitt zu platzieren“. Um dies zu überprüfen, verwenden wir den Indikator, der standardmäßig mit dem MetaTrader 5 geliefert wird, um zu sehen, ob wir alles richtig machen. Sie ist in der folgenden Animation dargestellt:

Animation 01
In dieser Animation sehen wir im Hintergrund den Durchschnitt von Bild 02 und im Vordergrund den Verifikationsindikator. Bitte beachten Sie die von uns verwendeten Werte. Je nach diesen Werten wird der Indikator im Chart platziert, wie in der Animation gezeigt. Alles ist perfekt. Der Code 02 berechnet einen exponentiellen Durchschnitt über neun Perioden. Aber wie ist das möglich?
Wenn Sie die in den Artikeln vorgestellten Konzepte studiert haben, werden Sie verstehen, dass wir einfach den in Abbildung 01 dargestellten Ausdruck verwenden. Wie bereits erwähnt, ist jedoch eine kleine Anpassung erforderlich, da wir den ersten Wert in der Berechnungskette auf Null setzen müssen. Daher mag dieser Ausdruck auf den ersten Blick etwas verwirrend erscheinen. Wenn Sie möchten, können Sie den Code in Zeile 22 jedoch ändern, um ihn kompakter zu gestalten. Der Calculate-Ereignishandler sieht also wie folgt aus:
. . . 18. //+------------------------------------------------------------------+ 19. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 20. { 21. #define def_N 9 22. 23. for (int c = prev_calculated; c < rates_total; c++) 24. { 25. double M = (c ? gl_buffer[c - 1] : 0); 26. 27. gl_buffer[c] = (price[c] - M) * (2. / (1.0 + def_N)) + M; 28. } 29. 30. return rates_total; 31. 32. #undef def_N 33. }; 34. //+------------------------------------------------------------------+
Fragment 01
Fragment 01 tut dasselbe wie Code 02, nur mit ein paar Zeilen mehr. Ausgezeichnet, aber bevor wir fortfahren, muss ich Sie warnen, dass es ein kleines Problem in diesem Code gibt. Ich habe jedoch nicht die Absicht, Ihnen zu erklären, worin dieses Problem besteht, sondern möchte Sie nur ein wenig neugierig machen. Auf diese Weise werden Sie sicher versuchen, sich mit diesem Thema auseinanderzusetzen, und können sich eine klarere Meinung über bestimmte Aspekte des Programmierens bilden, wie z. B. die Tatsache, dass wir Dinge nicht immer auf eine bestimmte Art und Weise tun sollten, nur weil ein mathematischer Ausdruck uns dies vorschreibt.
Um das Problem zu beheben, muss der Code wie unten gezeigt geändert werden:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #property indicator_type1 DRAW_LINE 05. #property indicator_color1 clrBlack 06. //+----------------+ 07. #property indicator_buffers 1 08. #property indicator_plots 1 09. //+----------------+ 10. double gl_buffer[]; 11. //+------------------------------------------------------------------+ 12. int OnInit() 13. { 14. SetIndexBuffer(0, gl_buffer, INDICATOR_DATA); 15. 16. return INIT_SUCCEEDED; 17. }; 18. //+------------------------------------------------------------------+ 19. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 20. { 21. for (int c = (prev_calculated > 0 ? prev_calculated - 1 : 0); c < rates_total; c++) 22. gl_buffer[c] = (c ? ((price[c] - gl_buffer[c - 1]) * 2. / (1 + 9)) + gl_buffer[c - 1] : price[c] * 2. / (1 + 9)); 23. 24. return rates_total; 25. }; 26. //+------------------------------------------------------------------+
Code 03
Keine Sorge, im Anhang finden Sie beide Codes, sodass Sie experimentieren und versuchen können, das Problem zu verstehen, das bei Betrachtung des Codes sinnlos erscheinen mag. Wie auch immer, ich denke, wir können jetzt über etwas anderes reden. Das werden Sie sicher denken: „Lieber Kollege, was ist, wenn ich einen exponentiellen gleitenden Durchschnitt mit einer anderen Periodenlänge verwenden möchte? Muss ich den Code jedes Mal neu kompilieren, wenn ich einen anderen Wert für die Periodenlänge verwenden möchte? Das sieht nicht sehr vielversprechend aus“. Die Art der Umsetzung, die wir bis jetzt gesehen haben, betrifft nur ein ganz bestimmtes Modell. Wenn Sie jedoch nicht jeden implementierten Punkt erfassen, wird es für Sie schwierig sein, neu entstehende Konzepte zu verstehen.
Aber nach allem, was hier erklärt wurde, denke ich, dass Sie bereit sind, die nächste Stufe der Umsetzung zu erreichen. Um jedoch nicht alles durcheinander zu bringen, sollten wir das Thema wechseln.
Interaktion mit dem Nutzer
Es ist äußerst selten, dass jemand einen Code wie den im vorherigen Thema gezeigten implementieren möchte, eben weil wir die Anzahl der Berechnungsperioden nicht ändern können. Es gibt zwar Fälle, wie z. B. bei der Berechnung des VWAP, in denen der auf dem Chart dargestellte Durchschnitt nicht von der Zeit abhängt. Das wäre jedoch ein ziemlich spezieller Fall, was nichts an seinen Vorzügen oder der Notwendigkeit ändert, zu erklären und zu verstehen, was in diesem Thema diskutiert wird.
Als Nächstes werden wir uns ansehen, wie wir es dem Nutzer ermöglichen können, einige Berechnungsparameter zu ändern, ohne den Code neu kompilieren zu müssen. Dazu müssen wir dem MQL5-Compiler mitteilen, dass wir dem Nutzer oder anderen Anwendungen erlauben wollen, die in unserem Code implementierten internen Werte zu kommunizieren. Mit anderen Worten: Wir werden eine Art Konstante haben, die für den Nutzer eine Variable ist. Aus der Sicht unserer Anwendung wird das, was wir erstellen wollen, jedoch als Konstante betrachtet. Ja, es ist möglich, diese Änderungen direkt über andere Anwendungen vorzunehmen, wie wir später sehen werden, obwohl diese Ressource im Prinzip für eine direktere Interaktion mit dem Nutzer gedacht ist.
Zunächst müssen wir den Code 03 anpassen, denn in seiner jetzigen Form führt er zu einiger Verwirrung. Zu didaktischen Zwecken (und nicht zur Leistungssteigerung) werden wir den Code daher wie folgt ändern:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #property indicator_type1 DRAW_LINE 05. #property indicator_color1 clrBlack 06. //+----------------+ 07. #property indicator_buffers 1 08. #property indicator_plots 1 09. //+----------------+ 10. double gl_buffer[]; 11. //+------------------------------------------------------------------+ 12. int OnInit() 13. { 14. SetIndexBuffer(0, gl_buffer, INDICATOR_DATA); 15. 16. return INIT_SUCCEEDED; 17. }; 18. //+------------------------------------------------------------------+ 19. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 20. { 21. double k = 2. / (1 + 9); 22. 23. for (int c = (prev_calculated > 0 ? prev_calculated - 1 : 0); c < rates_total; c++) 24. gl_buffer[c] = (c ? ((price[c] - gl_buffer[c - 1]) * k) + gl_buffer[c - 1] : price[c] * k); 25. 26. return rates_total; 27. }; 28. //+------------------------------------------------------------------+
Code 04
Bitte beachten Sie, dass die Änderungen recht einfach sind, da nur Zeile 21 hinzugefügt wurde, die eine Konstante zur Anpassung der exponentiellen Durchschnittsberechnung erstellt. Nun ein wichtiger Punkt: Der Wert, den wir hier ändern müssen, ist genau die „9“, die wir in Zeile 21 von Code 04 sehen. Fügen Sie dazu einfach eine neue Zeile in Code 04 ein. Auf diese Weise können wir angeben, welchen Zeitraum wir für die Berechnung des Durchschnitts verwenden wollen. Diese Änderung ist im Folgenden zu sehen:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #property indicator_type1 DRAW_LINE 05. #property indicator_color1 clrBlack 06. //+----------------+ 07. #property indicator_buffers 1 08. #property indicator_plots 1 09. //+----------------+ 10. input uchar user01 = 9; 11. //+----------------+ 12. double gl_buffer[]; 13. //+------------------------------------------------------------------+ 14. int OnInit() 15. { 16. SetIndexBuffer(0, gl_buffer, INDICATOR_DATA); 17. 18. return INIT_SUCCEEDED; 19. }; 20. //+------------------------------------------------------------------+ 21. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 22. { 23. double k = 2. / (1 + user01); 24. 25. for (int c = (prev_calculated > 0 ? prev_calculated - 1 : 0); c < rates_total; c++) 26. gl_buffer[c] = (c ? ((price[c] - gl_buffer[c - 1]) * k) + gl_buffer[c - 1] : price[c] * k); 27. 28. return rates_total; 29. }; 30. //+------------------------------------------------------------------+
Code 05
Und das war's. Nun kann der Nutzer die Periode des zu verwendenden exponentiellen gleitenden Durchschnitts angeben. Wir müssen jedoch noch einen kleinen Punkt erklären. Wenn wir diesen Indikator auf dem Chart platzieren, sehen wir folgendes:

Abbildung 03
Bitte beachten Sie, dass in diesem Bild eine neue Registerkarte hervorgehoben wird. Diese Registerkarte gab es vorher nicht, aber sie wurde gerade wegen der Zeile 10 in Code 05 erstellt. Das heißt, dies ist ein Punkt der Interaktion mit dem Nutzer, der es ihm ermöglicht, einige Konstanten zu konfigurieren, die wir im Code verwenden werden. Bis jetzt ist alles großartig. Wenn Sie jedoch die neue Registerkarte auswählen, sehen Sie das folgende Bild:

Abbildung 04
Und hier liegt das Problem. Achten Sie auf die in Abbildung 04 hervorgehobenen Informationen. Für uns, die wir diese Anwendung entwickeln, ist es völlig klar, was dieser Wert bewirkt. Wenn wir jedoch mehr Werte hinzufügen, wird es etwas verwirrend, und selbst die Person, die den Code implementiert hat, kann es schwierig finden, zu verstehen, was die einzelnen Werte bedeuten. Und hier müssen wir uns auf ein weiteres kleines Detail konzentrieren. Diese Information wird auf eine eher ungewöhnliche Art und Weise an den Compiler weitergegeben – zumindest kam mir das anfangs etwas seltsam vor, obwohl ich mich später daran gewöhnt habe.
Wie in Abbildung 04 zu sehen ist, entspricht die hervorgehobene Zeile derjenigen, die den Namen für die Konstante in Zeile 10 von Code 05 angibt. Und ja, lieber Leser, in Zeile 10 deklarieren wir keine Variable, sondern eine Konstante. In einfachen Fällen können wir unserem Code und dem Nutzer der Anwendung einen repräsentativen Namen geben. Dies ist jedoch nicht immer anwendbar, da in den meisten Fällen die Anzeige eines kurzen Textes für den Nutzer am besten geeignet ist. Wie platziert man also Text an dieser prominenten Stelle in der Abbildung 04?
Dies ist der ungewöhnlichste Teil. Hierfür müssen wir einen Kommentar vom Typ String hinzufügen. Da ein solcher Kommentar nach der Deklaration der Konstanten stehen muss, müssen wir etwa wie folgt vorgehen:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #property indicator_type1 DRAW_LINE 05. #property indicator_color1 clrBlack 06. //+----------------+ 07. #property indicator_buffers 1 08. #property indicator_plots 1 09. //+----------------+ 10. input uchar user01 = 9; //Exponential average periods 11. //+----------------+ . . .
Code 06
Schauen Sie sich Code 06 genau an, insbesondere Zeile 10. Was dieser Zeile hinzugefügt wurde, nennen wir einen String-Kommentar. Wenn wir dies in einer Konstante tun, die auf der Registerkarte in Abbildung 04 angezeigt wird, weisen wir den Compiler an, diesen Text als Zeichenfolge zu verwenden, die dem Nutzer angezeigt wird. Um dies zu verdeutlichen und sicherzustellen, dass Sie es wirklich verstehen, sehen Sie sich an, was passiert, wenn wir dieselbe Registerkarte wie in Abbildung 04 anzeigen, aber das verwenden, was in Code 06 angezeigt wird. Dies ist auf dem nachstehenden Bild zu sehen:

Abbildung 05
Dank der Abbildung 05 weiß jeder, was in der Anwendung konfiguriert wird, was die Nutzung stark vereinfacht.
Haben Sie bemerkt, wie einfach es ist, einen Indikator zu erstellen? Wir brauchen nur sehr wenig, damit alles funktioniert und die Elemente perfekt zu einer bestimmten Art von Interesse passen. Um den Indikator nützlicher zu machen und ihn besser in MetaTrader 5 zu integrieren sowie für andere Zwecke zu nutzen, müssen noch einige Dinge getan werden. Aber wir werden diese nach und nach behandeln, damit Sie verstehen, wie alles mit den Funktionen der MQL5-Standardbibliothek in einem Indikator zusammenpasst. Wie Sie sehen können, benötigen wir mehrere Funktionen, damit der Indikator funktioniert. Der Indikator ist jedoch noch nicht wirklich vielseitig.
Also gut, das war der erste Teil. Jetzt werden wir uns ansehen, wie man die zweite Version von OnCalculate verwendet. Die erste Version erfüllt bestimmte Kriterien, aber wir benötigen möglicherweise weitere Informationen. Deshalb brauchen wir eine andere Version. Um jedoch nicht alles miteinander zu vermischen, werden wir dies in einem anderen Thema behandeln.
Wie man die zweite Version von OnCalculate nutzt
In früheren Artikeln haben wir erwähnt, dass die Funktion OnCalculate die einzige überladene Funktion in der MQL5-Standardbibliothek ist. Denn in manchen Fällen können wir eine Version verwenden, in anderen eine andere. Die Wahl der Version wirkt sich jedoch nicht nur darauf aus, was auf der Nutzeroberfläche angezeigt wird, sondern auch darauf, wie Sie bei der Implementierung vorgehen müssen. Ohne die Unterstützung von MetaTrader 5 müssen wir bei der Implementierung vorsichtiger sein. Denn im Gegensatz zur ersten Version liefert die zweite Version mehr Daten, die verwendet werden können, und je nachdem, wie sie verwendet werden, kann das eine oder andere Ergebnis erzielt werden.
Da die zweite Version von OnCalculate recht komplex ist, werden wir nur untersuchen, wie der Code für denselben Indikator in dieser erweiterten Version aussehen würde. Es ist wichtig zu verstehen, dass es einen Unterschied gibt, ob man die eine oder die andere Version verwendet, aber die Unterschiede werden von Ihnen, den Programmierern, geschaffen. Derselbe Code 06, der im vorherigen Thema besprochen wurde, würde also bei Verwendung der zweiten Version von OnCalculate wie folgt aussehen:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #property indicator_type1 DRAW_LINE 05. #property indicator_color1 clrBlack 06. //+----------------+ 07. #property indicator_buffers 1 08. #property indicator_plots 1 09. //+----------------+ 10. input uchar user01 = 9; //Exponential average periods 11. //+----------------+ 12. double gl_buffer[]; 13. //+------------------------------------------------------------------+ 14. int OnInit() 15. { 16. SetIndexBuffer(0, gl_buffer, INDICATOR_DATA); 17. 18. return INIT_SUCCEEDED; 19. }; 20. //+------------------------------------------------------------------+ 21. int OnCalculate(const int rates_total, const int prev_calculated, const datetime &Time[], const double &Open[], const double &High[], const double &Low[], const double &Close[], const long &TickVolume[], const long &Volume[], const int &Spread[]) 22. { 23. double k = 2. / (1 + user01); 24. 25. for (int c = (prev_calculated > 0 ? prev_calculated - 1 : 0); c < rates_total; c++) 26. gl_buffer[c] = (c ? ((Close[c] - gl_buffer[c - 1]) * k) + gl_buffer[c - 1] : Close[c] * k); 27. 28. return rates_total; 29. }; 30. //+------------------------------------------------------------------+
Code 07
Bitte beachten Sie einen Punkt. Im Gegensatz zum vorherigen Fall, in dem wir wählen konnten, welche Art von Informationen für die Berechnung des Durchschnitts verwendet werden sollte, ist dies bei Code 07 nicht mehr möglich. Der Grund dafür ist, dass wir die Berechnung auf einen der Werte „fixieren“ – in diesem Fall den Schlusswert des Balkens. Alle anderen Informationen werden ignoriert, da sie nicht verwendet werden. Aber das ist nicht die einzige Änderung – erinnern Sie sich, dass wir gesagt haben, dass sich auch die Nutzeroberfläche ändern wird? Wenn wir den in Code 07 vorgestellten Indikator im Chart platzieren, werden wir feststellen, dass eine der Registerkarten fehlt, wie im folgenden Bild zu sehen ist:

Abbildung 06
In Abbildung 06 sehen wir, dass eine der früheren Registerkarten jetzt fehlt. Der Grund dafür ist, dass die Berechnung des Indikators auf eine bestimmte Art von Wert festgelegt ist. Daher kann der Nutzer die Quelle nicht mehr wie bisher ändern. Dies schließt jedoch die Verwendung anderer Berechnungsarten nicht aus – wir können dem Nutzer die Möglichkeit geben, die Art der Eingabewerte zu wählen, die in der Berechnung verwendet werden sollen. Dazu genügt es, neue Eingaben zu machen.
Dieser Teil ist sehr interessant. Da dies recht einfach ist, wird im folgenden Artikel erklärt, wie man es macht. Dann können wir auf interessante und kreative Art und Weise verschiedene Lösungen entwickeln.
Um dem Nutzer Zugang zu anderen Arten von Eingabewerten zu geben, können wir verschiedene Methoden verwenden. Ich bevorzuge die Verwendung von Enumerationen. Enumerationen sind einfach zu implementieren und zu ändern, weshalb ich sie mag. Im Wesentlichen benötigen wir eine kleine Abfolge von Schritten, um alles hinreichend elegant und intuitiv zu gestalten, sowohl für Sie, der es implementieren wird, als auch für die Nutzer, die Ihre Anwendung nutzen werden.
Der erste Schritt besteht darin, eine Enumeration zu erstellen. Der Einfachheit und Klarheit halber werden wir eine sehr einfache Version erstellen. Daher werden wir den Code 07 wie folgt ändern:
. . . 09. //+----------------+ 10. enum Enum_TypeInput { 11. HIGH, 12. OPEN, 13. CLOSE, 14. LOW 15. }; 16. //+----------------+ 17. input uchar user01 = 9; //Exponential average periods 18. input uchar user02 = HIGH; //Data type for calculation 19. //+----------------+ . . .
Code 08
Nach der Ausführung von Code 07 mit den in Code 08 angegebenen Änderungen ergibt sich folgendes Bild:

Abbildung 07
„Wow! Aber das ist nicht das, was ich tun wollte. Ich wollte dem Nutzer die in der Enumeration in Zeile 10 definierten Daten oder Typen anzeigen, damit er auf der Grundlage dieses Enumerationstyps eine Auswahl treffen kann. Warum hat es nicht geklappt?“ Nun, der Grund liegt im erwarteten Datentyp in Zeile 18. Bitte beachten Sie: Wir haben eine Enumeration deklariert, und das ist in Ordnung; das Problem ist, dass die Enumeration in Ganzzahlwerte umgewandelt wird. Der Compiler hat nicht verstanden, was wir vorhatten zu tun. Um dies zu beheben, ändern wir einfach den Typ des erwarteten Wertes in eine Konstante. Wir werden auch den Typ von HIGH auf CLOSE ändern, was in den meisten Fällen der Fall sein wird. Daher wird der Code 08 wie folgt geändert:
. . . 16. //+----------------+ 17. input uchar user01 = 9; //Exponential average periods 18. input Enum_TypeInput user02 = CLOSE; //Data type for calculation 19. //+----------------+ . . .
Code 09
Nach Anwendung dieser Änderung von Code 08 auf 09 sehen wir das folgende Ergebnis, wenn wir den Code im Terminal ausführen, siehe unten:

Abbildung 08
Sehen Sie, wie das funktioniert. Aber es geht nicht nur darum. Da der Compiler verstanden hat, was wir tun wollten, können wir auch Elemente mit demselben Text aus der in Zeile 10 von Code 08 deklarierten Enumeration auswählen. Beachten Sie jedoch Folgendes: Anders als in früheren Jahren, als wir uns auf die Unterstützung des MetaTrader 5 verlassen haben, sind wir jetzt auf uns allein gestellt. Mit anderen Worten: Es reicht nicht aus, alles wie oben beschrieben zu konfigurieren, damit es sofort funktioniert. Wir müssen die Ereignisbehandlung ändern. In diesem Fall gehen wir zum nächsten Schritt über, der darin besteht, dass unser Code die Auswahl des Nutzers verwendet. Dazu ändern wir den Code einfach wie folgt:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #property indicator_type1 DRAW_LINE 05. #property indicator_color1 clrBlack 06. //+----------------+ 07. #property indicator_buffers 1 08. #property indicator_plots 1 09. //+----------------+ 10. enum Enum_TypeInput { 11. HIGH, 12. OPEN, 13. CLOSE, 14. LOW 15. }; 16. //+----------------+ 17. input uchar user01 = 9; //Exponential average periods 18. input Enum_TypeInput user02 = CLOSE; //Data type for calculation 19. //+----------------+ 20. double gl_buffer[]; 21. //+------------------------------------------------------------------+ 22. int OnInit() 23. { 24. SetIndexBuffer(0, gl_buffer, INDICATOR_DATA); 25. 26. return INIT_SUCCEEDED; 27. }; 28. //+------------------------------------------------------------------+ 29. int OnCalculate(const int rates_total, const int prev_calculated, const datetime &Time[], const double &Open[], const double &High[], const double &Low[], const double &Close[], const long &TickVolume[], const long &Volume[], const int &Spread[]) 30. { 31. double k = 2. / (1 + user01), 32. price = 0; 33. 34. for (int c = (prev_calculated > 0 ? prev_calculated - 1 : 0); c < rates_total; c++) 35. { 36. switch (user02) 37. { 38. case HIGH : 39. price = High[c]; 40. break; 41. case OPEN : 42. price = Open[c]; 43. break; 44. case CLOSE : 45. price = Close[c]; 46. break; 47. case LOW : 48. price = Low[c]; 49. break; 50. } 51. gl_buffer[c] = (c ? ((price - gl_buffer[c - 1]) * k) + gl_buffer[c - 1] : price * k); 52. } 53. 54. return rates_total; 55. }; 56. //+------------------------------------------------------------------+
Code 10
In diesem Code sehen wir, dass wir in Zeile 32 eine neue Variable hinzugefügt haben. Sie soll uns die Verwaltung aller Elemente erleichtern. In Zeile 36 fügen wir eine switch-Anweisung hinzu, um den Wert zu bestimmen, der in die Preisvariable gesetzt wird. Dies erfordert nicht viele Änderungen am Berechnungscode, da wir nur die Berechnungszeile anpassen müssen, um die Preisvariable zu verwenden, wie in Zeile 51 zu sehen. Toll, nicht wahr? Aber bevor wir diesen Artikel abschließen, können wir noch eine Sache tun.
Beachten Sie, dass es in der in Zeile 10 deklarierten Enumeration mehrere Werte gibt, die eindeutig sind und direkt in der Schnittstelle verwendet werden können. Man kann aber auch einen anderen Code vorziehen. Wie können wir also die Situation für künftige Nutzer und für uns selbst klären? Dies würde es schließlich leicht machen, zu verstehen, welcher Wert in die Berechnung einfließen wird. Dazu machen wir etwas Ähnliches wie bei den Eingabezeilen – wir fügen einen String-Kommentar hinzu. Der resultierende Code sieht wie folgt aus:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #property indicator_type1 DRAW_LINE 05. #property indicator_color1 clrBlack 06. //+----------------+ 07. #property indicator_buffers 1 08. #property indicator_plots 1 09. //+----------------+ 10. enum Enum_TypeInput { 11. en_HIGH, //Use maximum prices 12. en_OPEN, //Use opening prices 13. en_CLOSE, //Use closing prices 14. en_LOW //Use minimum prices 15. }; 16. //+----------------+ 17. input uchar user01 = 9; //Exponential average periods 18. input Enum_TypeInput user02 = en_CLOSE; //Data type for calculation 19. //+----------------+ 20. double gl_buffer[]; 21. //+------------------------------------------------------------------+ 22. int OnInit() 23. { 24. SetIndexBuffer(0, gl_buffer, INDICATOR_DATA); 25. 26. return INIT_SUCCEEDED; 27. }; 28. //+------------------------------------------------------------------+ 29. int OnCalculate(const int rates_total, const int prev_calculated, const datetime &Time[], const double &Open[], const double &High[], const double &Low[], const double &Close[], const long &TickVolume[], const long &Volume[], const int &Spread[]) 30. { 31. double k = 2. / (1 + user01), 32. price = 0; 33. 34. for (int c = (prev_calculated > 0 ? prev_calculated - 1 : 0); c < rates_total; c++) 35. { 36. switch (user02) 37. { 38. case en_HIGH : 39. price = High[c]; 40. break; 41. case en_OPEN : 42. price = Open[c]; 43. break; 44. case en_CLOSE : 45. price = Close[c]; 46. break; 47. case en_LOW : 48. price = Low[c]; 49. break; 50. } 51. gl_buffer[c] = (c ? ((price - gl_buffer[c - 1]) * k) + gl_buffer[c - 1] : price * k); 52. } 53. 54. return rates_total; 55. }; 56. //+------------------------------------------------------------------+
Code 11
Sehen wir uns das Endergebnis an. Dieser Code wird in den Anhang aufgenommen und gibt Ihnen die Möglichkeit, zu lernen und zu üben. Nachdem wir es ausgeführt haben, sehen wir im MetaTrader 5-Terminal Folgendes.

Abbildung 09
Wie bereits erwähnt, haben wir alle Informationen über die interne Struktur des Codes einfach vor dem Nutzer verborgen. Für den Nutzer ändert sich nichts. Für Sie wird dieser Code jedoch etwas komplizierter zu implementieren sein, da wir viele Regeln und weniger Unterstützung durch MetaTrader 5 haben als zuvor. Da aber jeder Fall etwas anders ist, wissen Sie jetzt, wie Sie einen einfachen Indikator implementieren können, der Ihnen aber noch viele andere Dinge beibringen kann.
Abschließende Gedanken
In diesem Artikel haben wir gezeigt, wie man relativ einfach einen einfachen Indikator implementieren kann, indem man eine Reihe von Schritten befolgt. Ich habe Dinge wie das Verschieben der Chart-Linie nicht behandelt, aber da es sehr einfach ist (fügen Sie einfach eine weitere Konstante in den Code ein und verwenden Sie sie in der OnCalculate-Ereignisfunktion), werden wir nicht näher darauf eingehen, wie es gemacht wird. Überlassen wir solche Aufgaben denjenigen, die üben und versuchen wollen, mehr über Indikatoren zu verstehen.
Im nächsten Artikel werden wir uns mit etwas anderem beschäftigen, das ebenfalls mit Indikatoren zu tun hat. Also, bis später.
Übersetzt aus dem Portugiesischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/pt/articles/15802
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.
Marktsimulation (Teil 14): Sockets (VIII)
Analyse überkaufter und überverkaufter Trends mit Ansätzen der Chaostheorie
Quantitative Analyse von Trends: Sammeln von Statistiken in Python
Dreieckige und Sägezahnwellen: Analysetools für Händler
- 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.
Hallo!
Der Artikel hat mir gut gefallen!
Ich denke, es wäre angenehmer, mehr Bilder zu verwenden, um zu beschreiben, was Sie durch jeden Schritt tun.
He!
Adorei o artigo!
Ich finde es sehr interessant, mehr Bilder zu verwenden, um zu beschreiben, was Sie in den einzelnen Etappen tun.