English Русский 中文 Español 日本語 Português
preview
Vom Anfänger zum Fortgeschrittenen: Indikator (V)

Vom Anfänger zum Fortgeschrittenen: Indikator (V)

MetaTrader 5 — Beispiele |
26 0
CODE X
CODE X

Einleitung

Im vorherigen Artikel „Vom Anfänger zum Fortgeschrittenen: Vererbung“ haben wir erläutert, wie Vererbung im Kontext der strukturierten Programmierung angewendet werden kann. Um Sie nicht mit Informationen zu überfrachten, werden wir uns im heutigen Artikel mit etwas Einfacherem befassen – oder zumindest mit Stoff, der parallel zu den Inhalten der vorherigen Artikel gelernt und geübt werden kann.

Mir ist klar, dass dieses Thema auf den ersten Blick vielleicht etwas schwer zu verstehen ist, und da zuvor bereits sechs Artikel in Folge mit recht umfangreichem Stoff erschienen sind, schlage ich vor, sich erst einmal zu entspannen und sich etwas Einfacheres und Praktischeres anzuschauen.

In den Artikeln zum Thema Indikatoren – der neueste Beitrag der Reihe ist „Vom Anfänger zum Fortgeschrittenen: Indikator (IV)“ – haben wir uns damit befasst, wie sich etwas mithilfe einer vollständig statischen und sehr einfachen Implementierung direkt im Chart anzeigen lässt, sowie mit einer direkten Methode zur Erstellung eines Indikators. Und obwohl sich das in diesen Artikeln vorgestellte Format als ideal für praktisch jeden Indikator erwiesen hat, den Sie erstellen können, sind wir im Grunde nicht ausschließlich darauf beschränkt. Wir haben außerdem die Möglichkeit, dynamische Indikatoren zu erstellen, sofern wir sie als solche klassifizieren können.

Damit Sie jedoch verstehen, warum ich das bisherige Format als statisch betrachte und inwiefern es sich von einem dynamisch erstellten Format unterscheidet, muss ich einige Punkte erläutern. Um das richtig zu machen, wollen wir nun zu einem neuen Thema übergehen.


Der Unterschied zwischen einem statischen und einem dynamischen Indikator

Wahrscheinlich werden viele andere Programmierer mit dem, was ich nun sagen werde, nicht einverstanden sein, aber das ist nur ein kleines Detail, da jeder das Recht auf seine eigene Meinung hat. Und eine Meinung widerlegt nicht zwangsläufig andere, vorausgesetzt, die Begründung ist klar dargelegt und erläutert.

Um zu verstehen, warum ich die zuvor besprochenen Indikatoren als „statisch“ bezeichne, müssen wir uns ihren Code genauer ansehen. Tatsächlich bin ich damals nicht auf die Einzelheiten eingegangen, auch weil eine solche Umsetzung recht leicht zu verstehen ist. Um jedoch zu verstehen, was wir heute sehen werden, müssen wir zunächst diese vorherigen Codeausschnitte verstehen.

Verwenden wir einen dieser Codeausschnitte als Beispiel. Nachstehend sehen Sie das Beispiel, anhand dessen wir unsere Erläuterungen beginnen werden.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #property indicator_type1       DRAW_COLOR_CANDLES
05. #property indicator_color1      clrRed, clrRoyalBlue, clrGreen
06. //+----------------+
07. #property indicator_buffers     5
08. #property indicator_plots       1
09. //+----------------+
10. double  gl_Buff_High[],
11.         gl_Buff_Open[],
12.         gl_Buff_Close[],
13.         gl_Buff_Low[],
14.         gl_Buff_Color[];
15. //+------------------------------------------------------------------+
16. int OnInit()
17. {
18.    SetIndexBuffer(1, gl_Buff_High, INDICATOR_DATA);
19.    SetIndexBuffer(0, gl_Buff_Open, INDICATOR_DATA);
20.    SetIndexBuffer(3, gl_Buff_Close, INDICATOR_DATA);
21.    SetIndexBuffer(2, gl_Buff_Low, INDICATOR_DATA);
22.    SetIndexBuffer(4, gl_Buff_Color, INDICATOR_COLOR_INDEX);
23. 
24.    return INIT_SUCCEEDED;
25. };
26. //+------------------------------------------------------------------+
27. 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[])
28. {
29.    static double  high = DBL_MIN,
30.                   low = DBL_MAX;
31. 
32.    for (int c = (prev_calculated > 0 ? prev_calculated - 1 : 0); c < rates_total; c++)
33.    {
34.       gl_Buff_High[c] = High[c];
35.       gl_Buff_Open[c] = Open[c];
36.       gl_Buff_Close[c] = Close[c];
37.       gl_Buff_Low[c] = Low[c];
38. 
39.       gl_Buff_Color[c] = (Open[c] > Close[c] ? 0 : 2);
40.       if ((c - 1) > 0)
41.       {
42.          high = (High[c - 1] > high ? High[c - 1] : high);
43.          low = (Low[c - 1] < low ? Low[c - 1] : low);
44.          gl_Buff_Color[c] = ((high > High[c]) && (low < Low[c]) ? 1 : gl_Buff_Color[c]);
45.          if (gl_Buff_Color[c] != 1)
46.          {
47.             high = DBL_MIN;
48.             low = DBL_MAX;
49.          }
50.       }
51.    }
52. 
53.    return rates_total;
54. };
55. //+------------------------------------------------------------------+

Code 01

Bei der Ausführung verwandelt Code 01 die Chartdarstellung in eine indikatorähnliche Darstellung. In diesem Fall handelt es sich um einen Inside-Bar-Indikator, wie in der Abbildung unten zu sehen ist.

Bild 01

Wir wissen also, wie Code 01 funktioniert – zumindest in seinen Grundzügen –, gerade weil bei seiner Einführung erklärt wurde, wie bestimmte Parameter deklariert werden müssen. Dieser als Code 01 bezeichnete Indikator ist jedoch, wie ich es verstehe, ein statischer Indikator. Und der Grund dafür liegt genau in den Deklarationen in den Zeilen 04 und 05. Und bitte, liebe Leser, verstehen Sie mich nicht falsch. Ich meine damit, dass der Indikator in Code 01 statisch ist, weil wir den Indikator selbst nach der Kompilierung nicht mehr ändern können, da er mithilfe von MQL5-Kompilierungsdirektiven deklariert wurde, zu denen auch die eben erwähnten Zeilen gehören.

Entscheidend ist jedoch, ob der Indikator funktioniert und ob er die Aufgabe erfüllt, für die er entwickelt wurde. Da dies auf recht einfache Weise erreicht wird, gibt es kein Problem mit der Verwendung dieser statischen Implementierung. Dennoch können wir genau denselben Code erstellen – oder besser gesagt: einen sehr ähnlichen Indikator erstellen (allerdings mit dynamischen Eigenschaften) –, zumindest was die Umsetzung und Darstellung betrifft.

Wie funktioniert ein dynamischer Indikator? Welche Vorteile bietet die Umsetzung? Gehen wir Schritt für Schritt vor. Erstens sollte ein dynamischer Indikator in seinem Code nicht die Zeilen 04 und 05 enthalten, die in Code 01 zu sehen sind. Das ist der erste und wichtigste Unterschied. Der Vorteil läge gerade im Fehlen dieser beiden Zeilen. Das mag auf den ersten Blick seltsam erscheinen, wird aber später klarer werden, wenn wir zu komplexeren Modellen übergehen.

Aber gerade weil wir diese Codezeilen nicht deklariert haben, können wir ändern, wie der Indikator Informationen darstellt, ohne den Code kompilieren zu müssen, um diese Darstellungsart zu ändern. Der vielleicht größte Vorteil besteht jedoch darin, dass wir im Indikatorcode eine eigene Logik zur Auswahl der Darstellungsart implementieren, um auszuwählen, wie die Informationen im Chart angezeigt werden.

Achten Sie jetzt gut auf. Was wir hier machen werden, ändert nichts am Funktionsprinzip des Indikators aus Code 01. Wir werden einfach eine Möglichkeit schaffen, diese Aktionen mithilfe von Code umzusetzen. Mit anderen Worten: Ein Teil der Aktionen, die normalerweise durch Kompilierungsdirektiven ausgeführt werden, wird über Aufrufe der MQL5-Standardbibliothek durchgeführt. Als Erstes müssen die Zeilen entfernt werden, die den Indikator aus Code 01 zu einem statischen Indikator machen. Nachdem wir den statischen Teil entfernt haben, erhalten wir Folgendes:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #property indicator_buffers     5
05. #property indicator_plots       1
06. //+----------------+
07. double  gl_Buff_High[],
08.         gl_Buff_Open[],
09.         gl_Buff_Close[],
10.         gl_Buff_Low[],
11.         gl_Buff_Color[];
12. //+------------------------------------------------------------------+
13. int OnInit()
14. {
15.    SetIndexBuffer(1, gl_Buff_High, INDICATOR_DATA);
16.    SetIndexBuffer(0, gl_Buff_Open, INDICATOR_DATA);
17.    SetIndexBuffer(3, gl_Buff_Close, INDICATOR_DATA);
18.    SetIndexBuffer(2, gl_Buff_Low, INDICATOR_DATA);
19.    SetIndexBuffer(4, gl_Buff_Color, INDICATOR_COLOR_INDEX);
20. 
21.    return INIT_SUCCEEDED;
22. };
23. //+------------------------------------------------------------------+
24. 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[])
25. {
26.    static double  high = DBL_MIN,
27.                   low = DBL_MAX;
28. 
29.    for (int c = (prev_calculated > 0 ? prev_calculated - 1 : 0); c < rates_total; c++)
30.    {
31.       gl_Buff_High[c] = High[c];
32.       gl_Buff_Open[c] = Open[c];
33.       gl_Buff_Close[c] = Close[c];
34.       gl_Buff_Low[c] = Low[c];
35. 
36.       gl_Buff_Color[c] = (Open[c] > Close[c] ? 0 : 2);
37.       if ((c - 1) > 0)
38.       {
39.          high = (High[c - 1] > high ? High[c - 1] : high);
40.          low = (Low[c - 1] < low ? Low[c - 1] : low);
41.          gl_Buff_Color[c] = ((high > High[c]) && (low < Low[c]) ? 1 : gl_Buff_Color[c]);
42.          if (gl_Buff_Color[c] != 1)
43.          {
44.             high = DBL_MIN;
45.             low = DBL_MAX;
46.          }
47.       }
48.    }
49. 
50.    return rates_total;
51. };
52. //+------------------------------------------------------------------+

Code 02

Code 02 lässt sich zwar kompilieren, FUNKTIONIERT ABER NICHT. Zumindest wird dies keinerlei Auswirkungen auf den Chart haben, auf dem er platziert ist. Das ist jedoch kein wirkliches Problem. Das lag daran, dass wir den Teil entfernt haben, der MetaTrader 5 genau mitteilt, wie der Indikator verwendet wird. Somit weiß MetaTrader 5 lediglich, dass wir vier Datenpuffer und einen Farbpuffer haben und dass nur ein Plot gezeichnet wird. MetaTrader 5 WEISS JEDOCH NICHT, wie diese Puffer zu verwenden sind, obwohl sie so konfiguriert sind, dass sie bestimmte Informationen anzeigen. Daher passiert auf dem Chart nichts.

Na gut, wie lösen wir dieses Problem? Wie können wir MetaTrader 5 mitteilen, wie die in Code 02 deklarierten Puffer verwendet werden sollen? Nun, das ist der interessanteste Teil, denn an dieser Stelle werden wir die Verwendung der Puffer in MetaTrader 5 dynamisch konfigurieren. Dazu werden dem Code mehrere neue Zeilen hinzugefügt, wie unten gezeigt.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #property indicator_buffers     5
05. #property indicator_plots       1
06. //+----------------+
07. double  gl_Buff_High[],
08.         gl_Buff_Open[],
09.         gl_Buff_Close[],
10.         gl_Buff_Low[],
11.         gl_Buff_Color[];
12. //+------------------------------------------------------------------+
13. int OnInit()
14. {
15.    const color cor[] = {clrRed, clrRoyalBlue, clrGreen};
16. 
17.    PlotIndexSetInteger(0, PLOT_DRAW_TYPE, DRAW_COLOR_CANDLES);   
18.    PlotIndexSetInteger(0, PLOT_COLOR_INDEXES, cor.Size());
19.    for (uint c = 0; c < cor.Size(); c++)
20.       PlotIndexSetInteger(c, PLOT_LINE_COLOR, c, cor[c]);
21. 
22.    SetIndexBuffer(1, gl_Buff_High, INDICATOR_DATA);
23.    SetIndexBuffer(0, gl_Buff_Open, INDICATOR_DATA);
24.    SetIndexBuffer(3, gl_Buff_Close, INDICATOR_DATA);
25.    SetIndexBuffer(2, gl_Buff_Low, INDICATOR_DATA);
26.    SetIndexBuffer(4, gl_Buff_Color, INDICATOR_COLOR_INDEX);
27. 
28.    return INIT_SUCCEEDED;
29. };
30. //+------------------------------------------------------------------+
                   .
                   .
                   .

Code 03

Da das Endergebnis mit dem in Abbildung 01 gezeigten übereinstimmt, werde ich es hier nicht erneut zeigen. Bei Verwendung des dynamischen Indikators, den wir im Codefragment 03 sehen, funktioniert jedoch alles etwas anders als bei Verwendung des statischen Indikators in Code 01.

Bevor wir jedoch fortfahren, wollen wir zunächst verstehen, was im Codefragment 03 gemacht wurde, in dem nur der für uns relevante Teil gezeigt wird. Zur Erinnerung: Der vollständige Code befindet sich im Anhang. Beachten Sie, dass in Zeile 15 ein Array konstanter Farben definiert wird. Die Farbreihenfolge entspricht der in Code 01, Zeile 05. Es ist wichtig, dass Sie dies verstehen, um zu begreifen, wie der Code tatsächlich funktioniert. Wenn nun in Code 03 Zeile 17 ausgeführt wird, erzielen wir denselben Effekt wie wenn der Compiler in Code 01 Zeile 04 liest. Beachten Sie den subtilen Unterschied, der hier besteht. Im Gegensatz zu Code 01, bei dem wir während der Kompilierung festgelegt haben, wie alles aussehen würde, tun wir das hier nicht. Wir weisen den Compiler lediglich an: Erzeuge den Code und verwende diesen Wert. Das hat jedoch schwerwiegende Folgen, wie wir später noch sehen werden.

Unmittelbar danach, in Zeile 18 von Code 03, teilen wir MetaTrader 5 mit, wie viele Farben im Farbpuffer enthalten sind. Hinweis: Die Angabe der Anzahl der Farben in Zeile 18 sagt nichts darüber aus, welchen Wert jede einzelne Farbe haben wird. Wenn wir sie also nicht initialisieren, wird MetaTrader 5 dies auf irgendeine Weise tun, und das Farbschema könnte am Ende völlig durcheinandergeraten. Deshalb verwenden wir die Schleife in Zeile 19, um die Farben durch den Aufruf der Funktion in Zeile 20 zu initialisieren. All dies entspricht Zeile 05 in Code 01.

Doch damit endet die Ähnlichkeit auch schon. Denn wenn wir diesen Indikator aus dem Codefragment 03 anwenden, geraten wir in eine Situation, die zumindest ziemlich ungewöhnlich ist. Dies ist in der folgenden Abbildung zu sehen.

Bild 02

„Hey! Moment mal. Wo sind die Farben, die auf den Indikator angewendet werden sollen?“ Ich glaube, sowohl Sie als auch der Nutzer, der Bild 02 sieht, werden davon sehr überrascht sein, und der Nutzer, der dieses Bild sieht, wird völlig verwirrt sein. Aber schauen wir einmal, was passiert, wenn wir diesen Indikator auf den Chart anwenden. Dies ist in der folgenden Animation zu sehen.

Animation 01

„Was für eine erschreckende Sache. Ich verstehe das nicht. Warum hat der Indikator trotz der Unmöglichkeit, die Farben anzupassen, wie in Bild 02 gezeigt, dennoch dasselbe Ergebnis wie in Bild 01 geliefert? Das ist ziemlich seltsam.“

Tatsächlich erscheint dieses Verhalten gar nicht so seltsam. Das liegt daran, dass wir die Parameter dynamisch definieren. Und da MetaTrader 5 noch nicht weiß, wie die Farben erzeugt werden sollen, kann es diese nicht verwenden – oder besser gesagt: Es kann sie nicht zur Konfiguration anbieten, da es noch nicht weiß, wie viele Farben und genau welche Farben verwendet werden. Das ist in der Tat sehr interessant, und in Zukunft werden wir dieses Thema auf verschiedene Weise untersuchen können. Konzentrieren wir uns vorerst auf die wichtigsten Punkte.

Da das Farbschema mit dem in Bild 01 übereinstimmt, wissen wir, dass der Indikator funktioniert. Wir können dem Benutzer auch zeigen, welche Vorlage wir beim Schreiben des Codes definiert haben. „Aber lässt sich das nicht ändern, indem man dem Nutzer ermöglicht die gewünschten Farben selbst auszuwählen?“ Was das angeht, lieber Leser, gibt es im Grunde kein Problem, denn sobald der Indikator im Chart erscheint, kann der Nutzer dessen Eigenschaften öffnen und diese ändern. Schauen Sie sich in diesem Fall an, was in Animation 02 passiert.

Animation 02

Hmm, interessant; jetzt können wir sehen, welche Farben für die Verwendung im Indikator definiert sind. „Aber Moment mal. Sie haben gesagt, dass wir die Farben ändern können. Mir ist jedoch etwas eingefallen. In Zeile 15 des Fragments aus Code 03 definieren wir genau die Farben, die in Animation 02 zu sehen sind, und dennoch sind sie als Konstanten definiert. Da scheint es einen Widerspruch zu geben. Wenn sie als Konstanten definiert sind, wie kann der Benutzer dann das Farbschema ändern?“

In dieser Hinsicht haben wir den Vorteil, dass MetaTrader 5 versteht, was gerade geschieht, da wir lediglich ein Argument oder einen Parameter des Indikators ändern. MetaTrader 5 entfernt ihn nicht vollständig aus dem Chart. Dadurch bleibt er gewissermaßen im Chart erhalten, während nur das geändert wird, was notwendig ist. Ein Beispiel ist in der folgenden Animation zu sehen.

Animation 03

Beachten Sie, dass das tatsächlich funktioniert. Dieser Ansatz hat jedoch einen kleinen Nachteil. Wenn der Benutzer den Indikator aus dem Chart entfernt oder, genauer gesagt, wenn er MetaTrader 5 anweist, den Chart neu aufzubauen, werden die in Zeile 15 deklarierten Werte wieder verwendet, wodurch alle vom Benutzer vorgenommenen Konfigurationsänderungen verloren gehen und erneut vorgenommen werden müssen.

Ein Beispiel für eine Anfrage zur Aktualisierung eines Charts könnte eine einfache Anfrage zur Änderung der Chart-Periode sein. So absurd es auch klingen mag: Wenn viele Benutzer die Einstellungen beispielsweise für 5 Minuten ändern und MetaTrader 5 anschließend sofort anweisen, eine Periode von beispielsweise 10 Minuten zu verwenden, führt dies dazu, dass wieder die bei der Erstellung des Indikators festgelegten Einstellungen verwendet werden und die vorgenommenen Änderungen somit verloren gehen. In einem anderen Artikel haben wir bereits erläutert, wie man solche Situationen lösen oder bewältigen kann. Daher werden wir es hier nicht noch einmal wiederholen.

Nach dieser einfachen Demonstration haben Sie, glaube ich, den Unterschied zwischen einer statischen und einer dynamischen Implementierung bereits verstanden. Für diejenigen, die jedoch mehr darüber erfahren möchten, wie weit wir gehen können, enthält die MQL5-Dokumentation eine Tabelle, die dies veranschaulicht. Siehe: Zusammenhang zwischen Indikatoreigenschaften und den entsprechenden Funktionen; dort können Sie feststellen, welche Direktiven oder Eigenschaften dynamisch implementiert werden können und welche nicht.

„Ausgezeichnet, aber unter uns gesagt: Ich habe immer noch nicht verstanden, worin der Vorteil dieses dynamischen Systems besteht. Trotzdem verstehe ich nicht, inwiefern mir das in irgendeiner Weise helfen soll.“ Na gut, ich verstehe Ihre mangelnde Begeisterung für eine solche Umsetzung. Tatsächlich erscheint dies unbedeutend, insbesondere angesichts einiger Schwierigkeiten, die bei dieser Art der Indikatorimplementierung häufig auftreten.

In einigen sehr speziellen Fällen, die auf die persönliche Nutzung ausgerichtet sind, könnte jedoch genau dieser Ansatz für Sie interessant sein. Um dies zu veranschaulichen, betrachten wir ein ganz konkretes Implementierungsbeispiel. Darauf wird im nächsten Abschnitt eingegangen.


Der Inside-Bar-Indikator in verschiedenen Chart-Darstellungsmodi

Der Indikator, den wir im vorherigen Abschnitt besprochen haben, ist in erster Linie für die Verwendung in einem Kerzenchart vorgesehen. Bekanntlich können Sie in MetaTrader 5 jedoch nicht nur einen Kerzenchart verwenden, sondern auch einen Balkenchart und natürlich einen Linienchart. Wenn der Benutzer versehentlich zu einem anderen Chart-Darstellungsmodus als dem Kerzenchart wechselt, wird er mit einem sehr unangenehmen Chart-Verhalten konfrontiert. Schauen wir uns das im Folgenden an:

Abbildung 03

Abbildung 03 zeigt den Inside-Bar-Indikator auf einem Balkenchart. Bitte beachten Sie, dass die Darstellung der Informationen völlig ungewöhnlich ist, wie in der nächsten Abbildung zu sehen ist.

Abbildung 04

Hier haben wir es mit einem Problem zu tun, das dem Problem in Abbildung 03 sehr ähnlich ist. In Abbildung 04 soll jedoch ein Linienchart zusammen mit dem Inside-Bar-Indikator verwendet werden. Vielleicht ergibt eine solche Situation für die meisten Menschen in der Praxis keinen Sinn, denn bei der Verwendung eines Liniencharts möchten wir Informationen nur aus einem der OHLC-Werte gewinnen, und fast immer ist für uns der Schlusskurs von Bedeutung. Das ist jedoch nicht der Fall. Das Problem ist, dass selbst wenn wir den Chart in einem anderen Format anzeigen lassen, weiterhin Kerzen angezeigt werden. Und das muss behoben werden.

Grundsätzlich gibt es zwei Möglichkeiten, dies zu tun. Und ich sage „zwei“, weil es auch noch einen dritten Weg gab, der darin bestand, für jeden Chart-Darstellungsmodus einen eigenen Indikator zu erstellen. Das würde die Nutzung für die Benutzer jedoch äußerst verwirrend machen. Konzentrieren wir uns auf die beiden anderen Möglichkeiten. Die eine besteht darin, eine eigene Ausführungslogik zu erstellen und den Code auf dem aufzubauen, was vom Compiler erzeugt wird. Die andere besteht darin, ein dynamisches System zur Verarbeitung der zu überwachenden Informationen zu erstellen.

Um unnötige Komplexität zu vermeiden, werden wir Code 03 (der im vorherigen Abschnitt behandelt wurde) so anpassen, dass die Indikatorfarben von Anfang an festgelegt werden können. Wir werden jedoch nicht festlegen, welcher Indikatortyp im Chart angezeigt wird. Dasselbe Fragment von Code 03 sieht somit wie unten dargestellt aus.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #property indicator_color1      clrRed, clrRoyalBlue, clrGreen
05. //+----------------+
06. #property indicator_buffers     5
07. #property indicator_plots       1
08. //+----------------+
09. double  gl_Buff_High[],
10.         gl_Buff_Open[],
11.         gl_Buff_Close[],
12.         gl_Buff_Low[],
13.         gl_Buff_Color[];
14. //+------------------------------------------------------------------+
15. int OnInit()
16. {
17.    PlotIndexSetInteger(0, PLOT_DRAW_TYPE, DRAW_COLOR_CANDLES);   
18. 
19.    SetIndexBuffer(1, gl_Buff_High, INDICATOR_DATA);
20.    SetIndexBuffer(0, gl_Buff_Open, INDICATOR_DATA);
21.    SetIndexBuffer(3, gl_Buff_Close, INDICATOR_DATA);
22.    SetIndexBuffer(2, gl_Buff_Low, INDICATOR_DATA);
23.    SetIndexBuffer(4, gl_Buff_Color, INDICATOR_COLOR_INDEX);
24. 
25.    return INIT_SUCCEEDED;
26. };
27. //+------------------------------------------------------------------+
28. 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[])
29. {
30.    static double  high = DBL_MIN,
31.                   low = DBL_MAX;
32. 
33.    for (int c = (prev_calculated > 0 ? prev_calculated - 1 : 0); c < rates_total; c++)
34.    {
35.       gl_Buff_High[c] = High[c];
36.       gl_Buff_Open[c] = Open[c];
37.       gl_Buff_Close[c] = Close[c];
38.       gl_Buff_Low[c] = Low[c];
39. 
40.       gl_Buff_Color[c] = (Open[c] > Close[c] ? 0 : 2);
41.       if ((c - 1) > 0)
42.       {
43.          high = (High[c - 1] > high ? High[c - 1] : high);
44.          low = (Low[c - 1] < low ? Low[c - 1] : low);
45.          gl_Buff_Color[c] = ((high > High[c]) && (low < Low[c]) ? 1 : gl_Buff_Color[c]);
46.          if (gl_Buff_Color[c] != 1)
47.          {
48.             high = DBL_MIN;
49.             low = DBL_MAX;
50.          }
51.       }
52.    }
53. 
54.    return rates_total;
55. };
56. //+------------------------------------------------------------------+

Code 04

Nun funktioniert Code 04 für jeden Indikatornutzer komfortabler, da wir die Farben von Anfang an festlegen können. Und all das liegt daran, dass wir in Zeile 04 von Code 04 die Indikatoreigenschaft definieren. Beachten Sie, dass wir aus diesem Grund den Teil des Codes, der für das Laden der Farben zuständig ist, aus dem Behandlungsblock des OnInit-Ereignisses entfernt haben. So erhalten wir einen Basisindikator, den wir weiter bearbeiten müssen.

Nun kommen wir zum interessantesten Teil: Wenn dieser Indikator, den wir in Code 04 sehen, dynamisch funktioniert und gleichzeitig einen bestimmten statischen Teil enthält, der die Anzeige der Farbvorlage ermöglicht, wie können wir dann dafür sorgen, dass er funktioniert und sich an den angezeigten Chart-Darstellungsmodus anpasst?

An dieser Stelle bekommen viele Leute, die sich meinen Code ansehen, normalerweise Gänsehaut. Bitte beachten Sie, dass wir in Zeile 17 festlegen, dass der Indikator als Kerzen gezeichnet wird. Wenn man den Chart jedoch auf einen Balkenchart umstellt, treten Probleme auf, wie in den vorherigen Abbildungen zu sehen ist. Wenn wir uns jedoch die Dokumentation genauer ansehen, entdecken wir eine recht interessante Funktion der Standardbibliothek. Damit können wir feststellen, welcher Chart-Darstellungsmodus gerade verwendet wird. Dies bezieht sich auf die Funktion ChartGetInteger. Wenn wir über diese Funktion Informationen zum verwendeten Chart-Darstellungsmodus abfragen, gibt sie eine Enumeration zurück, in diesem Fall ENUM_CHART_MODE.

Genau das müssen wir wissen. Bevor wir dies jedoch in der Praxis anwenden, wollen wir uns zunächst ansehen, wie sich der Indikator in der Anfangsphase verhält. Dazu haben wir den Code angepasst; er sieht nun wie folgt aus:

                   .
                   .
                   .
14. //+------------------------------------------------------------------+
15. #define PrintX(X) Print(__FUNCTION__, " => ",#X, " :: ",X);
16. //+------------------------------------------------------------------+
17. int OnInit()
18. {
19.     PrintX(EnumToString((ENUM_CHART_MODE)ChartGetInteger(0, CHART_MODE)));
20.     PlotIndexSetInteger(0, PLOT_DRAW_TYPE, DRAW_COLOR_CANDLES);   
21. 
22.     SetIndexBuffer(1, gl_Buff_High, INDICATOR_DATA);
23.     SetIndexBuffer(0, gl_Buff_Open, INDICATOR_DATA);
24.     SetIndexBuffer(3, gl_Buff_Close, INDICATOR_DATA);
25.     SetIndexBuffer(2, gl_Buff_Low, INDICATOR_DATA);
26.     SetIndexBuffer(4, gl_Buff_Color, INDICATOR_COLOR_INDEX);
27. 
28.     return INIT_SUCCEEDED;
29. };
30. //+------------------------------------------------------------------+
                   .
                   .
                   .

Code 05

Achten Sie jetzt gut auf, sonst verlieren Sie bei der Beschreibung dessen, was getan wird, den Überblick. Dieses Fragment von Code 05 zeigt die Änderungen, die an Code 04 vorgenommen werden mussten, um festzustellen, welcher Chart-Darstellungsmodus aktuell verwendet wird. Anschließend führen wir in Zeile 19 einen Aufruf durch, der gemäß der in Zeile 15 angegebenen Definition letztendlich in Informationen umgewandelt wird, die im MetaTrader-5-Terminal angezeigt werden. Diese Informationen sind in den folgenden Abbildungen zu sehen.

Abbildung 05

Abbildung 05 zeigt, dass wir einen Chart des Typs CHART_CANDLES verwenden.

Abbildung 06

In Abbildung 06 ist deutlich zu erkennen, dass der Chart als CHART_BARS bezeichnet wird.

Abbildung 07

Und schließlich sehen wir in Abbildung 07, dass der Chart als CHART_LINE bezeichnet wird. Gut, aber wie können wir uns das zunutze machen? Nun, alles ist ganz einfach, mit Ausnahme des CHART_LINE-Charts, dessen Lösung wir uns später ansehen werden. Damit der Indikator den richtigen Chart-Darstellungsmodus verwendet, müssen wir im Funktionsaufruf in Zeile 20 (wie in diesem Codefragment 05 gezeigt) lediglich den Typ DRAW_COLOR_CANDLES angeben, wenn wir uns in einem Kerzenchart befinden, und DRAW_COLOR_BARS, wenn wir uns in einem Balkenchart befinden.

Damit ist der wesentliche Teil erledigt. Das ist für jeden ganz einfach, und es funktioniert einwandfrei, wenn MetaTrader 5 den Indikator vom Init-Ereignis aus anwendet. Mit anderen Worten: Wenn wir es auf die einfachste Art und Weise nutzen wollen, sollte der Code wie unten dargestellt aussehen.

                   .
                   .
                   .
14. //+------------------------------------------------------------------+
15. int OnInit()
16. {
17.     switch ((ENUM_CHART_MODE)ChartGetInteger(0, CHART_MODE))
18.     {
19.         case CHART_BARS:
20.             PlotIndexSetInteger(0, PLOT_DRAW_TYPE, DRAW_COLOR_BARS);
21.             break;
22.         case CHART_CANDLES:
23.             PlotIndexSetInteger(0, PLOT_DRAW_TYPE, DRAW_COLOR_CANDLES);   
24.             break;
25.         case CHART_LINE:
26.             break;
27.     }   
28. 
29.     SetIndexBuffer(1, gl_Buff_High, INDICATOR_DATA);
30.     SetIndexBuffer(0, gl_Buff_Open, INDICATOR_DATA);
31.     SetIndexBuffer(3, gl_Buff_Close, INDICATOR_DATA);
32.     SetIndexBuffer(2, gl_Buff_Low, INDICATOR_DATA);
33.     SetIndexBuffer(4, gl_Buff_Color, INDICATOR_COLOR_INDEX);
34. 
35.     return INIT_SUCCEEDED;
36. };
37. //+------------------------------------------------------------------+
                   .
                   .
                   .

Code 06

Bitte beachten Sie, dass wir in diesem Codefragment 06 den Linienchart-Modus nicht berücksichtigen. Das wird später noch deutlich werden. Beachten Sie jedoch, wie wir die anderen Modi anzeigen. Und was wir in Code 06 sehen, funktioniert zwar, weist aber ein kleines Problem auf. In der folgenden Animation können Sie sehen, worin dieses Problem besteht.

Animation 04

In Animation 04 sehen wir, dass der Indikator nicht mehr mit den Aktionen von MetaTrader 5 Schritt halten kann, sobald der Chart in einen anderen Chart-Darstellungsmodus umgeschaltet wird. Viele würden aufgeben und sagen, dass es unmöglich sei, den Indikator korrekt zu erstellen. Allerdings wissen sie nicht, wie MetaTrader 5 eigentlich funktioniert. Wann immer etwas passiert – und in der Regel geschieht dies im Chart –, löst MetaTrader 5 ein ganz bestimmtes Ereignis aus, das Informationen enthält, anhand derer wir feststellen können, was geschehen ist. Einige Ereignisse können aktiviert oder deaktiviert werden, andere hingegen nicht. Jedes Ereignis, das wir aktivieren oder deaktivieren, wirkt sich auf die Gesamtleistung von MetaTrader 5 aus, da das System dadurch überlastet wird und Ereignisse verarbeiten muss, die es normalerweise nicht überwacht.

Und für alle, die ein Hochleistungssystem anstreben, ist dies ein naheliegender Punkt. Viele Trader, die den Einsatz von MetaTrader 5 planen, verwenden damit überhaupt keine zusätzlichen Tools wie Indikatoren oder Expert Advisors. Das liegt daran, dass solche Anwendungen MetaTrader 5 in Phasen hoher Volatilität häufig verlangsamen. Gerade in solchen Momenten wollen diese Trader Gewinne erzielen; deshalb nutzen sie die Basisversion von MetaTrader 5 – nur diese und sonst nichts.

Na gut, aber hier werden wir uns nicht auf den Handel mit MetaTrader 5 in seiner einfachsten Form konzentrieren. Ja, wir möchten Anwendungen zum Chart hinzufügen, damit sie uns bei bestimmten Aufgaben helfen können. Daher müssen wir ein bestimmtes Ereignis erfassen, das MetaTrader 5 jedes Mal auslöst, wenn im Chart eine Aktion durchgeführt wird, unabhängig von der Art dieses Ereignisses.

Das Ereignis, das wir erfassen möchten, ist ChartEvent. In unserem Code wird es von der Prozedur OnChartEvent verarbeitet. Nun passt gut auf, liebe Leser. Das ChartEvent-Ereignis wird immer dann ausgelöst, wenn im Chart etwas passiert. Wir können jedoch bestimmte Parameter aktivieren und deaktivieren, um nicht jede einzelne Änderung am Chart zu erfassen. Wird dem Chart jedoch ein Indikator hinzugefügt, löst MetaTrader 5 nacheinander zwei Ereignisse aus. In manchen Fällen wird auch ein drittes ausgelöst, aber wir werden uns nur auf die ersten beiden konzentrieren.

Das erste Ereignis, das ausgelöst wird, ist die Initialisierung (Init), die von der Funktion OnInit abgefangen und verarbeitet wird, mit der wir uns bereits seit Beginn des Themas über Indikatoren beschäftigt haben. Unmittelbar nach der Verarbeitung des Init-Ereignisses wird ein neues Ereignis ausgelöst, auch wenn wir noch nichts unternommen haben. Dieses Ereignis ist ein ChartEvent, und genau in diesem Moment wird der Indikator tatsächlich auf dem Chart platziert.

Na gut, aber um zu verstehen, was als Nächstes passiert, müssen wir uns die Deklaration der Prozedur ansehen, die dieses ChartEvent abfängt. Diese Anweisung ist direkt darunter zu sehen.

void OnChartEvent(const int id,         // Event ID
                  const long& lparam,   // Parameter of type long event
                  const double& dparam, // Parameter of type double event
                  const string& sparam  // Parameter of type string events
  );

Code 07

Wenn wir uns nun die Dokumentation zu OnChartEvent ansehen, finden wir dort eine kleine Tabelle. Aus dieser Tabelle interessiert uns der folgende Wert: CHARTEVENT_CHART_CHANGE – ein Ereignis, das auftritt, wenn sich der Chart ändert. Das ist also genau der angezeigte Wert, wenn ChartEvent zum ersten Mal ausgelöst wird. Da wir das nun wissen, können wir in unserem Code etwas ziemlich Interessantes machen. Nach der Aktualisierung sieht der Code also wie unten dargestellt aus.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #property indicator_color1      clrRed, clrRoyalBlue, clrGreen
05. //+----------------+
06. #property indicator_buffers     5
07. #property indicator_plots       1
08. //+----------------+
09. double  gl_Buff_High[],
10.         gl_Buff_Open[],
11.         gl_Buff_Close[],
12.         gl_Buff_Low[],
13.         gl_Buff_Color[];
14. //+------------------------------------------------------------------+
15. int OnInit()
16. {
17.     SetIndexBuffer(0, gl_Buff_Open, INDICATOR_DATA);
18.     SetIndexBuffer(1, gl_Buff_High, INDICATOR_DATA);
19.     SetIndexBuffer(2, gl_Buff_Low, INDICATOR_DATA);
20.     SetIndexBuffer(3, gl_Buff_Close, INDICATOR_DATA);
21.     SetIndexBuffer(4, gl_Buff_Color, INDICATOR_COLOR_INDEX);
22. 
23.     return INIT_SUCCEEDED;
24. };
25. //+------------------------------------------------------------------+
26. 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[])
27. {
28.    static double  high = DBL_MIN,
29.                   low = DBL_MAX;
30. 
31.    for (int c = (prev_calculated > 0 ? prev_calculated - 1 : 0); c < rates_total; c++)
32.    {
33.       gl_Buff_High[c] = High[c];
34.       gl_Buff_Open[c] = Open[c];
35.       gl_Buff_Close[c] = Close[c];
36.       gl_Buff_Low[c] = Low[c];
37. 
38.       gl_Buff_Color[c] = (Open[c] > Close[c] ? 0 : 2);
39.       if ((c - 1) > 0)
40.       {
41.          high = (High[c - 1] > high ? High[c - 1] : high);
42.          low = (Low[c - 1] < low ? Low[c - 1] : low);
43.          gl_Buff_Color[c] = ((high > High[c]) && (low < Low[c]) ? 1 : gl_Buff_Color[c]);
44.          if (gl_Buff_Color[c] != 1)
45.          {
46.             high = DBL_MIN;
47.             low = DBL_MAX;
48.          }
49.       }
50.    }
51. 
52.    return rates_total;
53. };
54. //+------------------------------------------------------------------+
55. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
56. {
57.     switch (id)
58.     {
59.         case CHARTEVENT_CHART_CHANGE:
60.             switch ((ENUM_CHART_MODE)ChartGetInteger(0, CHART_MODE))
61.             {
62.                 case CHART_BARS:
63.                     PlotIndexSetInteger(0, PLOT_DRAW_TYPE, DRAW_COLOR_BARS);
64.                     break;
65.                 case CHART_CANDLES:
66.                     PlotIndexSetInteger(0, PLOT_DRAW_TYPE, DRAW_COLOR_CANDLES);   
67.                     break;
68.                 case CHART_LINE:
69.                     break;
70.             }
71.             break;
72.     }
73.     ChartRedraw();
74. };
75. //+------------------------------------------------------------------+

Code 08

Hmm. „Ich weiß es nicht. Ich glaube, dieser Code wird nicht funktionieren, da wir MetaTrader 5 bei der Ausführung der OnInit-Funktion nicht mitteilen, welcher Darstellungsmodus des Charts verwendet werden soll. Ich weiß es nicht. Ich bin mir fast sicher, dass das Ergebnis dasselbe sein wird wie bei dem Fehler in Code 02.“

Tja, da haben wir ja einen Skeptiker in unseren Reihen. Deshalb werden wir ihm zeigen, dass die Welt viel größer ist, als er sich das vorstellt. Um zu zeigen, dass Code 08 wirklich funktioniert, sehen wir uns die folgende Animation an.

Animation 05

Bitte beachten Sie, dass der Wechsel in den CHART_LINE-Modus das Einzige ist, was nicht mit den Aktionen von MetaTrader 5 im Chart übereinstimmt. In diesem Fall kann unser Indikator, der keinen passenden Darstellungsmodus unterstützt, mit den Aktionen des Benutzers in MetaTrader 5 nicht Schritt halten.


Abschließende Gedanken

Im heutigen Artikel haben wir ein einfaches, aber sehr interessantes Prinzip für den Umgang mit Indikatoren vorgestellt. Obwohl wir nicht gezeigt haben, wie man das Problem löst, mit einem Indikator Charts zu zeichnen und dabei alle Anforderungen von MetaTrader 5 zu erfüllen, ist es uns gelungen, einen einfachen Indikator einen Befehl ausführen zu lassen, der dem Chart-Darstellungsmodus entspricht. Das liegt zum Teil daran, dass wir noch nicht gezeigt haben, wie man den Indikator so einrichtet, dass er gemäß der Auswahl des Benutzers auf der Plattform einen Linien-Plot anzeigt.

Da sich der Übergang vom Zeichnen von Kerzencharts oder Balkencharts (für die vier Datenpuffer und ein Farbpuffer benötigt werden) zum Zeichnen farbiger Liniencharts (für die ein Datenpuffer und ein Farbpuffer benötigt werden) derzeit jedoch nur schwer veranschaulichen lässt, werden wir dies vorerst zurückstellen. Dazu müssen wir einen etwas anderen Ansatz verfolgen und den Indikator über Aufrufe der Standardbibliothek laden und entladen. Das ist etwas, das ich im Moment nur schwer erklären könnte und das für Sie, liebe Leserinnen und Leser, nur schwer zu verstehen wäre.

Auf jeden Fall werde ich, wenn wir zu noch komplexeren Themen übergehen, irgendwann zeigen, wie man das hier besprochene Problem löst. Für den Moment empfehle ich Ihnen, den in diesem Artikel vorgestellten Stoff durchzuarbeiten und vor allem nicht zu vergessen, das in den vorherigen Artikeln Behandelte noch einmal nachzulesen. Denn im nächsten Artikel werden wir einige Konzepte mit diesem Thema der Indikatoren verknüpfen, und das wird wirklich interessant zu sehen sein. Verpassen Sie ihn also nicht.

Übersetzt aus dem Portugiesischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/pt/articles/15932

Beigefügte Dateien |
Anexo.zip (2.69 KB)
Marktsimulation (Teil 21): Erste Schritte mit SQL (IV) Marktsimulation (Teil 21): Erste Schritte mit SQL (IV)
Viele von Ihnen haben vielleicht weitaus mehr Erfahrung im Umgang mit Datenbanken als ich und sind daher möglicherweise anderer Meinung. Da es notwendig war, zu erklären, warum Datenbanken so aufgebaut sind, wie sie sind, und warum SQL diese Form hat – insbesondere, warum Primär- und Fremdschlüssel entstanden sind –, mussten einige Dinge etwas abstrakt bleiben.
Neuronale Netze im Trading: Ein domänenübergreifendes Framework zur Zeitreihenprognose (Fazit) Neuronale Netze im Trading: Ein domänenübergreifendes Framework zur Zeitreihenprognose (Fazit)
Der Artikel befasst sich mit der praktischen Implementierung des TimeFound-Modells für die Zeitreihenvorhersage. Es werden die wichtigsten Schritte bei der Umsetzung der zentralen Ansätze des Frameworks mithilfe von MQL5 untersucht.
Neuronale Netze im Trading: Ein domänenübergreifendes Framework für die Zeitreihenprognose (TimeFound) Neuronale Netze im Trading: Ein domänenübergreifendes Framework für die Zeitreihenprognose (TimeFound)
In diesem Artikel bauen wir Schritt für Schritt den Kern des intelligenten TimeFound-Modells auf und passen ihn an reale Aufgaben der Zeitreihenprognose an. Wenn Sie sich für die praktische Umsetzung von Patching-Algorithmen für neuronale Netze in MQL5 interessieren, sind Sie hier genau richtig.
Neuronale Netze im Trading: Eine intelligente Prognose-Pipeline (Time-MoE) Neuronale Netze im Trading: Eine intelligente Prognose-Pipeline (Time-MoE)
Wir laden Sie ein, das moderne Time-MoE-Framework kennenzulernen, das für Zeitreihenprognosen angepasst wurde. In diesem Artikel werden wir die wichtigsten Komponenten der Architektur Schritt für Schritt umsetzen und dabei Erläuterungen und praktische Beispiele geben. Mit diesem Ansatz können Sie nicht nur verstehen, wie das Modell funktioniert, sondern diese Prinzipien auch auf reale Handelsszenarien anwenden.