English Русский Español Português
preview
Von der Grundstufe zur Mittelstufe: Objekte (III)

Von der Grundstufe zur Mittelstufe: Objekte (III)

MetaTrader 5Beispiele |
32 0
CODE X
CODE X

Einführung

Im vorigen Artikel Von der Grundstufe zur Mittelstufe: Mausereignisse haben wir uns angesehen, wie man mausbezogene Ereignisse sowie Maus-Tastatur-Kombinationen auf einfache, klare und hinreichend objektive Weise handhabt. Dies ist notwendig, um Kombinationen gedrückter Tasten bzw. Maustasten für bestimmte Aktionen im Chart zu verwenden.

Obwohl die im vorigen Artikel besprochene Funktionalität auf den ersten Blick relativ einfach erscheinen mag, stellen wir bei der Umsetzung in die Praxis fest, dass sie weitaus komplizierter sein kann, als sie aussieht. Das liegt daran, dass je nach Art der geplanten Funktionalität und ihrer Umsetzung manche Benutzer nicht über die erforderliche Hardware verfügen. Und obwohl dies heutzutage relativ verbreitet ist, nutzen viele Menschen keine Desktop-Computer.

Selbst unter denjenigen, die sie nutzen, ist bekannt, dass manche Benutzer keine Maus mit mehr als drei Tasten haben. Dies erschwert die Implementierung einiger Arten von Funktionen, die für eine breite Benutzerbasis gedacht sind. Für den persönlichen Gebrauch gibt es keine Probleme, aber wenn Sie beabsichtigen, Programmierung zu erlernen, um Ihre Anwendungen zu verkaufen, werden Sie feststellen, dass nicht jeder über die notwendige Hardware verfügt. Daher können Sie entweder gemäß den Bedürfnissen dieser Zielgruppe handeln oder sie ignorieren und riskieren, einen Teil des Marktes zu verlieren. Und all das aufgrund einer einfachen Entscheidung.

In Ordnung, aber wir sind nicht hier, um zu lernen, wie man die von uns erstellten Anwendungen verkauft, sondern um unsere Ideen mit MQL5 zum Leben zu erwecken. Da sich der vorige Artikel stärker auf technische Aspekte und eine einfache Demonstration konzentrierte, ist es an der Zeit, das dort Gezeigte in die Praxis umzusetzen und somit eine Alternative zum Fadenkreuz von MetaTrader 5 zu schaffen.

Obwohl das Ziel nicht darin besteht, einen Ersatz dafür zu schaffen, sondern Ihnen zu zeigen, dass Sie auch mit sehr begrenztem Wissen nützliche Dinge bauen können, vorausgesetzt, Sie verstehen die richtigen Konzepte.


Benutzerdefinierter Indikator

Was wir hier zeigen möchten, war bereits Gegenstand eines anderen unserer Artikel Entwicklung eines Replay-Systems (Teil 30): Expert Advisor Projekt — Die Klasse C_Mouse (IV) . Dort haben wir gezeigt, wie man einen mausbasierten Indikator mithilfe der objektorientierten Programmierung (OOP) implementiert. Aber hier werden wir etwas Ähnliches sehen, wenn auch mit einem viel einfacheren Programm, das sich an Anfänger richtet. Wie Sie, lieber Leser.

Warum wiederholen, was bereits früher gezeigt wurde? Tatsächlich werden wir nichts wiederholen; es ist nur ein Konzept. Da ich aber bis zu diesem Punkt überhaupt nicht über OOP gesprochen habe und nur der strukturierte Ansatz gezeigt wurde, möchten wir zeigen, dass wir keine Spezialisten sein müssen, um etwas zu erstellen, das unseren Anforderungen entspricht. Wir können dies mit minimalem Wissen tun, während wir stets versuchen, die bereits gelernten Konzepte anzuwenden. Und doch werden wir am Ende das gewünschte Ergebnis erhalten. Mit anderen Worten: Einfachheit funktioniert, selbst wenn sie unzureichend erscheint.

Zunächst werden wir einen Code implementieren, der auf einem der im vorherigen Artikel untersuchten Codes basiert. Er ist unten zu sehen:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_Prefix  "Demo"
05. //+------------------------------------------------------------------+
06. int OnInit()
07. {
08.     IndicatorSetString(INDICATOR_SHORTNAME, def_Prefix);
09.     ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, true);
10. //+----------------+    
11.     ChartSetInteger(0, CHART_MOUSE_SCROLL, false);
12.     ChartSetInteger(0, CHART_CONTEXT_MENU, false);
13.     ChartSetInteger(0, CHART_CROSSHAIR_TOOL, false);
14. //+----------------+
15.     ChartSetInteger(0, CHART_KEYBOARD_CONTROL, false);
16.     ChartSetInteger(0, CHART_QUICK_NAVIGATION, false);
17. //+----------------+
18. 
19.     return INIT_SUCCEEDED;
20. };
21. //+------------------------------------------------------------------+
22. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
23. {
24.     return rates_total;
25. };
26. //+------------------------------------------------------------------+
27. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
28. {
29.     string sz = "";
30. 
31.     switch (id)
32.     {
33.         case CHARTEVENT_KEYDOWN:
34.             break;
35.         case CHARTEVENT_MOUSE_MOVE:
36.             Comment(sz);
37.             sz += "Mouse position X: " + (string)(short)lparam;
38.             sz += "\nMouse position Y: " + (string)(short)dparam;
39.             Comment(sz);
40.             if ((uchar)sparam != 0) PrintFormat("Hexadecimal mask of mouse buttons is: 0x%02X", (uchar)sparam);
41.             break;
42.         case CHARTEVENT_MOUSE_WHEEL:
43.             break;
44.     }
45. };
46. //+------------------------------------------------------------------+
47. void OnDeinit(const int reason)
48. {
49.     Comment("");
50.     ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, false);
51. //+----------------+    
52.     ChartSetInteger(0, CHART_MOUSE_SCROLL, true);
53.     ChartSetInteger(0, CHART_CONTEXT_MENU, true);
54.     ChartSetInteger(0, CHART_CROSSHAIR_TOOL, true);
55. //+----------------+
56.     ChartSetInteger(0, CHART_KEYBOARD_CONTROL, true);
57.     ChartSetInteger(0, CHART_QUICK_NAVIGATION, true);
58. //+----------------+
59. };
60. //+------------------------------------------------------------------+

Code 01

Code 01 ist eine Modifikation des im vorherigen Artikel besprochenen Codes. Dies wird uns das Testen erleichtern. Bitte beachten Sie, dass wir in Zeile 40 etwas im Terminal ausgeben werden. Dies ist genau der Wert, den wir verwenden müssen. Wenn wir also Code 01 ausführen, werden wir etwas sehen, das der folgenden Abbildung ähnelt.

Abbildung 01

Die in Abbildung 01 hervorgehobene Information ist genau die Maske, die wir verwenden müssen. Dies ist notwendig, damit wir ein Ereignis erstellen können, das das benutzerdefinierte Fadenkreuz für die Analyse anzeigt. Achten Sie auf Folgendes. Der Wert, den wir in Abbildung 01 sehen, ist der Wert, der erfasst wird, wenn die mittlere Taste gedrückt wird. Da das Drücken der mittleren Taste also das benutzerdefinierte Fadenkreuz für die Analyse erstellen sollte, können wir uns überlegen, wie wir das umsetzen. Es gibt noch eine weitere Sache, die verstanden werden muss.

Wenn Sie mit dem Code aus dem vorherigen Artikel geübt haben, sollten Sie bemerkt haben, dass jeder der Aufrufe in der Funktion OnInit in Zeile 06 von Code 01 eine Standard-Eigenschaft des MetaTrader 5 aktiviert oder deaktiviert. Da wir jedoch nur ein benutzerdefiniertes Fadenkreuz für die Analyse erstellen müssen, brauchen wir nicht alle diese Ereignisse zu deaktivieren, da dies die Nutzung von MetaTrader 5 für einige Benutzer zu stark einschränken würde. Am besten deaktivieren wir nur die Ereignis, das MetaTrader 5 nicht standardmäßig verwenden soll. In diesem Fall ist es das Ereignis in Zeile 13 von Code 01.

Wir haben also das erste Element, das wir in Code 01 unbedingt ändern müssen. Aber wir können bereits mit der Arbeit am Fadenkreuz beginnen, bevor wir zum nächsten Code übergehen, den wir implementieren werden. Achten Sie genau auf das, was wir besprechen werden; es ist sehr wichtig.

Im Artikel Von der Grundstufe zur Mittelstufe: Objekte (II) haben wir gezeigt, wie es möglich ist, mit zwei Objekten gleichzeitig im Chart zu arbeiten: einer vertikalen Linie und einer horizontalen Linie.

Beide genannten Objekte verwenden also KEINE BILDSCHIRMKOORDINATEN, da X- und Y-Koordinaten aus der Computergrafik als Bildschirmkoordinaten bekannt sind. Beide Objekte, OBJ_VLINE und OBJ_HLINE, verwenden Zeit- und Preiskoordinaten. Diese Chart-Koordinaten basieren auf Preis und Zeit. Und da das Betriebssystem uns X- und Y-Koordinaten liefert, müssen wir diese Bildschirmkoordinaten in Zeit- und Preiskoordinaten umwandeln. Diese Aufgabe manuell durchzuführen ist recht mühsam, da der minimale und maximale Preis sowie die minimale und maximale Zeit berücksichtigt werden müssen, um das Verhältnis zwischen Preis und Zeit sowie den vom Betriebssystem bereitgestellten X- und Y-Werten zu ermitteln.

Glücklicherweise wird diese Aufgabe mithilfe einer Prozedur aus der MQL5-Standardbibliothek ausgeführt, was den Prozess erheblich vereinfacht. Bevor wir uns also das im Chart gezeichnete Fadenkreuz ansehen, wollen wir untersuchen, wie X- und Y-Werte in Chart-Koordinaten auf Basis von Zeit und Preis umgewandelt werden.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_Prefix  "Demo"
05. //+------------------------------------------------------------------+
06. int OnInit()
07. {
08.     IndicatorSetString(INDICATOR_SHORTNAME, def_Prefix);
09.     ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, true);
10.     ChartSetInteger(0, CHART_CROSSHAIR_TOOL, false);
11. 
12.     return INIT_SUCCEEDED;
13. };
14. //+------------------------------------------------------------------+
15. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
16. {
17.     return rates_total;
18. };
19. //+------------------------------------------------------------------+
20. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
21. {
22.     string      sz = "";
23.     short       x, y;
24.     int         sub;
25.     datetime    dt;
26.     double      price;
27. 
28.     switch (id)
29.     {
30.         case CHARTEVENT_KEYDOWN:
31.             break;
32.         case CHARTEVENT_MOUSE_MOVE:
33.             x = (short)lparam;
34.             y = (short)dparam;
35.             ChartXYToTimePrice(0, x, y, sub, dt, price);
36.             Comment(sz);
37.             sz += "Mouse position X: " + (string)x;
38.             sz += "\nMouse position Y: " + (string)y;
39.             sz += "\nMouse position time: " + TimeToString(dt, TIME_DATE | TIME_MINUTES);
40.             sz += "\nMouse position price: " + (string)price;
41.             Comment(sz);
42.             if ((uchar)sparam != 0) PrintFormat("Hexadecimal mask of mouse buttons is: 0x%02X", (uchar)sparam);
43.             break;
44.         case CHARTEVENT_MOUSE_WHEEL:
45.             break;
46.     }
47. };
48. //+------------------------------------------------------------------+
49. void OnDeinit(const int reason)
50. {
51.     Comment("");
52.     ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, false);
53.     ChartSetInteger(0, CHART_CROSSHAIR_TOOL, true);
54. };
55. //+------------------------------------------------------------------+

Code 02

Hier ist ein einfacherer Code, der dazu dient, nur die wirklich notwendigen Funktionen zu aktivieren oder zu deaktivieren. Alles andere bleibt beim Standardverhalten von MetaTrader 5. Bitte beachten Sie, dass wir kleine Änderungen an dem Teil vorgenommen haben, der dem Maus-Ereignishandler entspricht. Und in Zeile 35 sehen wir genau die Bibliotheksprozedur, die verwendet wird, um die Werte in die umzuwandeln, die wir tatsächlich benötigen oder verwenden möchten. Es ist wichtig, dies zu verstehen, bevor diese Werte direkt zur Steuerung der OBJ_VLINE- und OBJ_HLINE-Objekte verwendet werden. Um diese Punkte zu demonstrieren, betrachten Sie die folgende Animation.

Animation 01

In Animation 01 können Sie sehen, dass der Chart auf dem täglichen Zeitrahmen angezeigt wird. Wenn wir uns jedoch die Zeitwerte ansehen, die in der Animation selbst angezeigt werden, werden wir sehen, dass wir es mit Daten zu tun haben, die als Intraday-Daten betrachtet werden können. Und das kann am Ende viele Leute verwirren, denn wenn wir uns einen Tageschart ansehen, wie können wir dann Intraday-Werte sehen?

Tatsächlich werden diese Werte genau deshalb angezeigt, denn die Funktion ChartXYToTimePrice KONVERTIERT DIE WERTE NICHT in das von uns erwartete Format, sondern wandelt lediglich Bildschirmkoordinaten in Zeit- und Preiskoordinaten um. Und da kleine Schwankungen entlang der Achse zu nicht-ganzzahligen Werten führen, werden diese Werte schließlich zu Werten, die während des Tages vorhanden sind. Deshalb sehen wir Werte, die auf den ersten Blick bedeutungslos erscheinen. Daher ist dies kein Fehler, sondern nur ein kleines Detail im Zusammenhang mit dem Übergang von einer Koordinatenart zur anderen.

Auf jeden Fall werden Sie, basierend auf dem, was wir hier untersucht haben, und mit ein wenig Fantasie und gesundem Menschenverstand bald verstehen, dass wir bereits alles Notwendige haben, um darüber nachzudenken, wie OBJ_HLINE- und OBJ_VLINE-Objekte gesteuert werden können. Dies unterscheidet sich von dem, was im Artikel Von der Grundstufe zur Mittelstufe: Objekte (II) gezeigt wurde, wo die Steuerung über die Tastatur erfolgte. Aber jetzt werden wir dafür die Maus verwenden.

Gut, und wie werden wir das machen? Hier beginnt der interessanteste Teil, lieber Leser. Dazu erstellen wir zunächst ein Fadenkreuz, das ständig auf dem Chart bleibt. Dies geschieht mit dem unten gezeigten Code.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_Prefix  "Demo"
05. //+------------------------------------------------------------------+
06. #define macro_NameObject  def_Prefix + (string)ObjectsTotal(0)
07. //+------------------------------------------------------------------+
08. string  gl_Objs[2];
09. //+------------------------------------------------------------------+
10. int OnInit()
11. {
12.     IndicatorSetString(INDICATOR_SHORTNAME, def_Prefix);
13.     ObjectCreate(0, gl_Objs[0] = macro_NameObject, OBJ_VLINE, 0, 0, 0);
14.     ObjectCreate(0, gl_Objs[1] = macro_NameObject, OBJ_HLINE, 0, 0, 0);
15.     ObjectSetString(0, gl_Objs[0], OBJPROP_TOOLTIP, "\n");
16.     ObjectSetString(0, gl_Objs[1], OBJPROP_TOOLTIP, "\n");
17.     ObjectSetInteger(0, gl_Objs[0], OBJPROP_COLOR, clrBlue);
18.     ObjectSetInteger(0, gl_Objs[1], OBJPROP_COLOR, clrBlue);
19. 
20.     ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, true);
21.     ChartSetInteger(0, CHART_CROSSHAIR_TOOL, false);
22. 
23.     return INIT_SUCCEEDED;
24. };
25. //+------------------------------------------------------------------+
26. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
27. {
28.     return rates_total;
29. };
30. //+------------------------------------------------------------------+
31. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
32. {
33.     int         sub;
34.     datetime    dt;
35.     double      price;
36. 
37.     switch (id)
38.     {
39.         case CHARTEVENT_KEYDOWN:
40.             break;
41.         case CHARTEVENT_MOUSE_MOVE:
42.             ChartXYToTimePrice(0, (short)lparam, (short)dparam, sub, dt, price);
43.             ObjectMove(0, gl_Objs[0], 0, dt, price);
44.             ObjectMove(0, gl_Objs[1], 0, dt, price);
45.             if ((uchar)sparam != 0) PrintFormat("Hexadecimal mask of mouse buttons is: 0x%02X", (uchar)sparam);
46.             break;
47.         case CHARTEVENT_MOUSE_WHEEL:
48.             break;
49.     }
50.     ChartRedraw();
51. };
52. //+------------------------------------------------------------------+
53. void OnDeinit(const int reason)
54. {
55.     ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, false);
56.     ChartSetInteger(0, CHART_CROSSHAIR_TOOL, true);
57. 
58.     ObjectsDeleteAll(0, def_Prefix);
59.     ChartRedraw();
60. };
61. //+------------------------------------------------------------------+

Code 03

Die Anwendung von Code 03 auf den Chart führt zu einem Ergebnis, das diesem ähnelt:

Animation 02

Bitte beachten Sie, dass sich der Zeitwert zu Beginn der Animation 02 ändert, obwohl die vertikale Linie unverändert bleibt. Dies geschieht aufgrund kleiner Mausbewegungen, was völlig normal und akzeptabel ist. Beachten Sie, dass die vertikalen und horizontalen Linien der Mausbewegung folgen, wenn sich der Mauszeiger bewegt, wodurch wir das Fadenkreuz ständig auf dem Chart anzeigen können.

Da das, was hier getan wird, bereits teilweise im Artikel Von der Grundstufe zur Mittelstufe: Objekte (II) behandelt wurde, konzentrieren wir uns hier nur auf das Neue. In diesem Fall ist die Bewegung nun spezifisch mit den Werten verknüpft, die von der Bibliotheksprozedur bei der Umwandlung von Bildschirmkoordinaten in Zeit-und-Preiskoordinaten zurückgegeben werden.

Aber das war nicht genau das, was wir wollten. Wir wollten dasselbe Fadenkreuz anzeigen, aber erst nach dem Drücken der mittleren Taste. Um dies zu beheben, müssen wir daher einige Änderungen am Code vornehmen. Aber lassen Sie uns zuerst verstehen, warum die Zeilen 15, 16 und 58 benötigt werden. In Ordnung, die Zeilen 15 und 16 verhindern, dass MetaTrader 5 eine kleine Meldung anzeigt, wenn wir uns über einem Objekt befinden. Wenn Sie eine Meldung anzeigen möchten, die das Objekt selbst erklärt, diese aber nicht ständig auf dem Chart behalten wollen, verwenden Sie die Technik aus den Zeilen 15 und 16, um dort eine aussagekräftige Meldung zu platzieren. Diese Meldung wird angezeigt, wenn der Mauszeiger für kurze Zeit still über dem Objekt steht, auf das er zeigt.

Hingegen werden in Zeile 58 alle auf dem Chart vorhandenen Objekte gelöscht, deren Präfix mit dem angegebenen übereinstimmt. Da wir die Objekte mit dem Kurznamen des Indikators selbst verknüpfen, wird es einfacher, Objekte zu löschen, die sich auf den Indikator beziehen. Wenn Sie möchten, dass ein Objekt auf dem Chart verbleibt, verwenden Sie einfach ein anderes Präfix für den Indikatornamen. Nun können wir uns den nächsten Code ansehen.

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #define def_Prefix  "Demo"
005. //+------------------------------------------------------------------+
006. #define macro_NameObject  def_Prefix + (string)ObjectsTotal(0)
007. //+------------------------------------------------------------------+
008. enum eBtnMouse  {
009.         MOUSE_LEFT      = 0x01, 
010.         MOUSE_RIGHT     = 0x02,
011.         MOUSE_KEY_SHIFT = 0x04,
012.         MOUSE_KEY_CTRL  = 0x08,
013.         MOUSE_MIDDLE    = 0x10,
014.         MOUSE_EXTRA_1   = 0x20,
015.         MOUSE_EXTRA_2   = 0x40
016.                 };
017. //+------------------------------------------------------------------+
018. struct st_Cross
019. {
020.     private :
021. //+----------------+
022.         bool    IsView;
023.         string  Objs[2];
024. //+----------------+
025.         void Create(char index, ENUM_OBJECT type)
026.         {
027.             ObjectCreate(0, Objs[index] = macro_NameObject, type, 0, 0, 0);
028.             ObjectSetString(0, Objs[index], OBJPROP_TOOLTIP, "\n");
029.             ObjectSetInteger(0, Objs[index], OBJPROP_COLOR, clrBlue);
030.         }
031. //+----------------+
032.     public  :
033. //+----------------+
034.         void Show(void)
035.         {
036.             if (IsView) return;
037.             Create(0, OBJ_VLINE);
038.             Create(1, OBJ_HLINE);
039.             IsView = true;
040.             ChartRedraw();
041.         }
042. //+----------------+
043.         void Hide(void)
044.         {
045.             if (!IsView) return;
046.             for(uint c = 0; c < Objs.Size(); c++)
047.                 ObjectDelete(0, Objs[c]);
048.             ChartRedraw();
049.             IsView = false;
050.         }
051. //+----------------+
052.         void Move(short x, short y)
053.         {
054.             int         sub;
055.             datetime    dt;
056.             double      price;
057. 
058.             if (!IsView) return;
059.             ChartXYToTimePrice(0, x, y, sub, dt, price);
060.             ObjectMove(0, Objs[0], 0, dt, price);
061.             ObjectMove(0, Objs[1], 0, dt, price);
062.             ChartRedraw();
063.         }
064. //+----------------+
065. }gl_Cross;
066. //+------------------------------------------------------------------+
067. int OnInit()
068. {
069.     gl_Cross.Hide();
070.     
071.     IndicatorSetString(INDICATOR_SHORTNAME, def_Prefix);
072.     ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, true);
073.     ChartSetInteger(0, CHART_CROSSHAIR_TOOL, false);
074. 
075.     return INIT_SUCCEEDED;
076. };
077. //+------------------------------------------------------------------+
078. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
079. {
080.     return rates_total;
081. };
082. //+------------------------------------------------------------------+
083. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
084. {
085. 
086.     switch (id)
087.     {
088.         case CHARTEVENT_KEYDOWN:
089.             break;
090.         case CHARTEVENT_MOUSE_MOVE:
091.             if (((uchar)sparam & MOUSE_MIDDLE) != 0) gl_Cross.Show();
092.             gl_Cross.Move((ushort)lparam, (ushort)dparam);
093.             break;
094.         case CHARTEVENT_MOUSE_WHEEL:
095.             break;
096.     }
097.     ChartRedraw();
098. };
099. //+------------------------------------------------------------------+
100. void OnDeinit(const int reason)
101. {
102.     ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, false);
103.     ChartSetInteger(0, CHART_CROSSHAIR_TOOL, true);
104. 
105.     gl_Cross.Hide();
106. };
107. //+------------------------------------------------------------------+

Code 04

In Ordnung, aber was macht dieser Code 04? Am Ende wird das passieren, was wir in der folgenden Animation sehen.

Animation 03

„Interessant. Aber ich sehe, dass Sie dazu übergegangen sind, strukturierten Code zu verwenden. Warum?“ Nun, lieber Leser, der Grund ist genau der, dass strukturierter Code für das Fadenkreuz selbst einfacher zu erstellen und zu warten ist. Denken Sie daran, dass der Zweck einer Struktur genau darin besteht, eine ganze Reihe von Funktionen und Prozeduren zu erstellen, die direkt mit einem bestimmten Ziel zusammenhängen.

Im Artikel Von der Grundstufe zur Mittelstufe: Strukturen (VII), wo wir erklärt haben, wie man Code strukturiert, wurde alles in einer abstrakten Form präsentiert. Aber wenn wir das aus einem weniger abstrakten Blickwinkel betrachten, ergibt alles einen Sinn.

In diesem Fall befinden wir uns in Code 04 an der Grenze zwischen strukturiertem Code und objektorientiertem Code. Dies liegt daran, dass in Code 04 klar ist, dass bestimmte Elemente initialisiert werden müssen, bevor wir die Struktur selbst verwenden können. Und das ist einer der Gründe, warum OOP geschaffen wurde. Achten Sie auf Folgendes: Wenn die Struktur st_Cross in Zeile 65 deklariert wird, um die Variable gl_Cross zu verwenden, haben wir keine Gewissheit über die Werte, die in IsView und im Array Objs enthalten sind. Dies erschwert die Erstellung bestimmter Arten von Code, insbesondere von Code, der für die Arbeit mit spezifischen Datenarten vorgesehen ist.

Da der Fall hier jedoch relativ unkompliziert ist, können wir Zeile 69 verwenden, um die Struktur vorzubereiten und mit ihr zu arbeiten. Dies ist nicht der beste Weg, aber wie bereits erwähnt, haben wir es hier mit etwas relativ Einfachem zu tun. Wenn wir also in Zeile 91 prüfen, ob die mittlere Taste gedrückt wurde oder nicht, können wir wählen, ob die Objekte, die das einfache Fadenkreuz für die Analyse erstellen, angezeigt werden sollen oder nicht. Dies geschieht durch den Aufruf der Funktion Show der Struktur st_Cross.

Auf jeden Fall haben wir in Zeile 92 die Position der Objekte so angepasst, dass sie mit dem Mauszeiger verknüpft sind. Wenn die Objekte jedoch nicht im Chart vorhanden sind, ist der Wert von IsView false. Und in Zeile 58 wird dadurch die Ausführung gestoppt und die Kontrolle sofort an den Aufrufer zurückgegeben, was in diesem Fall Zeile 92 ist. Dies wird fortgesetzt, bis die mittlere Taste gedrückt wird, woraufhin das Fadenkreuz schließlich erstellt wird. Von diesem Moment an erfolgt die Aktualisierung bei jeder Mausbewegung, da IsView in diesem Fall den Wert true hat, was es Zeile 92 ermöglicht, die Position zu aktualisieren, da die Bedingung in Zeile 58 nicht mehr erfüllt ist.

In Ordnung, aber wie können wir das Fadenkreuz verschwinden lassen, sobald es sichtbar wird? Dies hängt davon ab, wofür Sie das Fadenkreuz verwenden wollen und wie es funktionieren soll. Aber ohne großen Aufwand und ohne viel Code zu verwenden, können wir eine Taste hinzufügen, um das Fadenkreuz aus dem Chart auszublenden, bis dieses Verhalten vollständig implementiert ist. Dazu haben wir den Event-Handling-Code geändert, um diese Methode zum Entfernen des Fadenkreuzes aus dem Chart zu implementieren, wie im folgenden Codefragment gezeigt.

                   .
                   .
                   .
082. //+------------------------------------------------------------------+
083. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
084. {
085. 
086.     switch (id)
087.     {
088.         case CHARTEVENT_KEYDOWN:
089.             if (TerminalInfoInteger(TERMINAL_KEYSTATE_ESCAPE)) gl_Cross.Hide();
090.             break;
091.         case CHARTEVENT_MOUSE_MOVE:
092.             if (((uchar)sparam & MOUSE_MIDDLE) != 0) gl_Cross.Show();
093.             gl_Cross.Move((ushort)lparam, (ushort)dparam);
094.             break;
095.         case CHARTEVENT_MOUSE_WHEEL:
096.             break;
097.     }
098.     ChartRedraw();
099. };
100. //+------------------------------------------------------------------+
                   .
                   .
                   .

Code 05

Diese einfache Änderung, die in Zeile 89 zu sehen ist, ermöglicht es uns, das zu tun, was in der folgenden Animation gezeigt wird.

Animation 04

Beachten Sie, dass wir das Fadenkreuz nun nach Belieben für die Analyse ein- oder ausblenden können. Aber hier, in Animation 04, ergibt sich ein wichtiges Problem: die Tick-Größe des Kontrakts, das heißt, um wie viele Punkte sich der Preis ändert. Im Gegensatz zu vielen Fällen ändert sich dieser Futures-Kontrakt, in diesem Fall der Dollar-Futures-Kontrakt, nicht um eine Einheit, sondern um 0.5. Um dies zu überprüfen, können wir uns die Kontraktspezifikationen ansehen:

Abbildung 02

Und was zeigt uns Abbildung 02? Darin sehen wir, wie stark sich der Symbolpreis ändert. Genau das möchte ich hier betonen. Diese Information ist wichtig, damit wir eine geeignete Methode entwickeln können, mit der die Preislinie immer einen gültigen Marktpreis widerspiegelt. Es hat keinen Sinn, in der Preislinie einen Wert anzugeben, der niemals verwendet wird. Dies liegt daran, dass ein solcher Wert beispielsweise nicht zum Senden von Aufträgen verwendet werden kann.

Um dieses Problem zu lösen, müssen wir den Code erneut ändern. Aber dieses Mal ändern wir nicht den Event-Handler; wir ändern die Struktur st_Cross, wie unten dargestellt.

                   .
                   .
                   .
                   
017. //+------------------------------------------------------------------+
018. struct st_Cross
019. {
020.     private :
021. //+----------------+
022.         bool    IsView;
023.         string  Objs[2];
024.         int     nDigits;
025.             double  PointPerTick;
026. //+----------------+
027.         void Create(char index, ENUM_OBJECT type)
028.         {
029.             ObjectCreate(0, Objs[index] = macro_NameObject, type, 0, 0, 0);
030.             ObjectSetString(0, Objs[index], OBJPROP_TOOLTIP, "\n");
031.             ObjectSetInteger(0, Objs[index], OBJPROP_COLOR, clrBlue);
032.         }
033. //+----------------+
034.     public  :
035. //+----------------+
036.         void Init(void)
037.         {
038.             nDigits         = (int) SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
039.             PointPerTick    = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
040.             Hide();
041.         }
042. //+----------------+
                   .
                   .
                   .
060. //+----------------+
061.         void Move(short x, short y)
062.         {
063.             int         sub;
064.             datetime    dt;
065.             double      price;
066. 
067.             if (!IsView) return;
068.             ChartXYToTimePrice(0, x, y, sub, dt, price);
069.             price = NormalizeDouble(MathRound(price / PointPerTick) * PointPerTick, nDigits);
070.             ObjectMove(0, Objs[0], 0, dt, price);
071.             ObjectMove(0, Objs[1], 0, dt, price);
072.             ChartRedraw();
073.         }
074. //+----------------+
075. }gl_Cross;
076. //+------------------------------------------------------------------+
077. int OnInit()
078. {
079.     gl_Cross.Init();
080. 
081.     IndicatorSetString(INDICATOR_SHORTNAME, def_Prefix);
082.     ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, true);
083.     ChartSetInteger(0, CHART_CROSSHAIR_TOOL, false);
084. 
085.     return INIT_SUCCEEDED;
086. };
087. //+------------------------------------------------------------------+
                   .
                   .
                   .

Code 06

Bitte beachten Sie, dass wir in Codefragment 06 sehr einfache Änderungen vorgenommen haben. Zuerst fügen wir der Struktur neue Variablen hinzu. Dies hilft uns, das ständige Lesen derselben Informationen zu vermeiden. Unmittelbar danach haben wir eine neue Prozedur hinzugefügt: Init. Dies geschieht in Zeile 36, wo wir die Struktur auf eine etwas angemessenere Weise initialisieren. Daher haben wir in der Methode OnInit den Aufruf, der zuvor an die Funktion Hide erfolgte, geändert und auf Init umgeleitet, wie in Zeile 79 zu sehen ist.

Um diese Abweichung in der Preislinie zu korrigieren, verwenden wir nun Zeile 69, um den Preis entsprechend anzupassen. Am Ende sehen wir das, was in der folgenden Animation dargestellt ist:

Animation 05

Einfach perfekt. Mit extrem einfachem Code ist es uns gelungen, unseren grundlegenden und vollständig anpassbaren Indikator zu erstellen. Nun stellt sich die Frage: Welche Arten von Analysen können wir mit diesem Indikator durchführen? Denn wir haben uns hier nicht auf eine bestimmte Art von Analyse konzentriert. Nun, lieber Leser, es gibt keine einfache Antwort auf diese Frage. Dies liegt daran, dass wir absolut jede Art von Analyse durchführen können. Es hängt alles davon ab, was wir wirklich erreichen wollen und wie wir das, was wir geschaffen haben, später zu nutzen planen.

Denken Sie daran, dass der Inhalt dieser Artikel lehrreicher Natur ist. Und da keiner der hier betrachteten Punkte in irgendeiner Weise einem vordefinierten Ziel entspricht, das wir zu erreichen versuchen, sollten Sie sich nicht an die eine oder andere Implementierungsmethode klammern; versuchen Sie einfach, die Konzepte zu verstehen. Und sobald Sie das Konzept verstanden haben, bleibt nur noch, das zu implementieren, was Sie wollen oder brauchen.

Wie können wir diesen Indikator in etwas wirklich Praktisches, unglaublich Interessantes und Fesselndes verwandeln? Lassen Sie uns Folgendes tun: Verschieben Sie die Struktur st_Cross in eine Header-Datei. Auf diese Weise können verschiedene Indikatoren mit minimalem Aufwand implementiert werden. Darüber hinaus können wir sie so vorbereiten, dass sie später zu einer Klasse werden kann, wenn wir anfangen, über Klassen zu sprechen. Somit haben wir jetzt zwei Dateien. Eine davon ist unten dargestellt – dies ist die Header-Datei.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. enum eBtnMouse  {
05.         MOUSE_LEFT      = 0x01, 
06.         MOUSE_RIGHT     = 0x02,
07.         MOUSE_KEY_SHIFT = 0x04,
08.         MOUSE_KEY_CTRL  = 0x08,
09.         MOUSE_MIDDLE    = 0x10,
10.         MOUSE_EXTRA_1   = 0x20,
11.         MOUSE_EXTRA_2   = 0x40
12.                 };
13. //+------------------------------------------------------------------+
14. struct st_TimePrice
15. {
16.     double      Price;
17.     datetime    Time;
18. };
19. //+------------------------------------------------------------------+
20. struct st_Cross
21. {
22.     private :
23. //+----------------+
24.         bool    IsView;
25.         string  Objs[2];
26.         int     nDigits;
27.         double      PointPerTick;
28. //+----------------+
29.         void Create(char index, ENUM_OBJECT type)
30.         {
31.             ObjectCreate(0, Objs[index] = macro_NameObject, type, 0, 0, 0);
32.             ObjectSetString(0, Objs[index], OBJPROP_TOOLTIP, "\n");
33.             ObjectSetInteger(0, Objs[index], OBJPROP_COLOR, clrBlue);
34.         }
35. //+----------------+
36.     public  :
37. //+----------------+
38.         void Init(void)
39.         {
40.             nDigits         = (int) SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
41.             PointPerTick    = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
42.             Hide();
43.         }
44. //+----------------+
45.         void Show(void)
46.         {
47.             if (IsView) return;
48.             Create(0, OBJ_VLINE);
49.             Create(1, OBJ_HLINE);
50.             IsView = true;
51.             ChartRedraw();
52.         }
53. //+----------------+
54.         void Hide(void)
55.         {
56.             if (!IsView) return;
57.             for(uint c = 0; c < Objs.Size(); c++)
58.                 ObjectDelete(0, Objs[c]);
59.             ChartRedraw();
60.             IsView = false;
61.         }
62. //+----------------+
63.         st_TimePrice Move(short x, short y)
64.         {
65.             int             sub;
66.             st_TimePrice    tp;
67. 
68.             ChartXYToTimePrice(0, x, y, sub, tp.Time, tp.Price);
69.             tp.Price = NormalizeDouble(MathRound(tp.Price / PointPerTick) * PointPerTick, nDigits);
70.             if (IsView)
71.             {
72.                 ObjectMove(0, Objs[0], 0, tp.Time, tp.Price);
73.                 ObjectMove(0, Objs[1], 0, tp.Time, tp.Price);
74.                 ChartRedraw();
75.             }
76. 
77.             return tp;
78.         }
79. //+----------------+
80. };
81. //+------------------------------------------------------------------+

Code 07

Und dieser entspricht dem Indikator, den wir implementieren möchten:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_Prefix  "Demo"
05. //+------------------------------------------------------------------+
06. #define macro_NameObject  def_Prefix + (string)ObjectsTotal(0)
07. //+------------------------------------------------------------------+
08. #include <Tutorial\File 01.mqh>
09. //+------------------------------------------------------------------+
10. st_Cross gl_Cross;
11. //+------------------------------------------------------------------+
12. int OnInit()
13. {
14.     gl_Cross.Init();
15. 
16.     IndicatorSetString(INDICATOR_SHORTNAME, def_Prefix);
17.     ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, true);
18.     ChartSetInteger(0, CHART_CROSSHAIR_TOOL, false);
19. 
20.     return INIT_SUCCEEDED;
21. };
22. //+------------------------------------------------------------------+
23. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
24. {
25.     return rates_total;
26. };
27. //+------------------------------------------------------------------+
28. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
29. {
30. 
31.     switch (id)
32.     {
33.         case CHARTEVENT_KEYDOWN:
34.             if (TerminalInfoInteger(TERMINAL_KEYSTATE_ESCAPE)) gl_Cross.Hide();
35.             break;
36.         case CHARTEVENT_MOUSE_MOVE:
37.             if (((uchar)sparam & MOUSE_MIDDLE) != 0) gl_Cross.Show();
38.             gl_Cross.Move((ushort)lparam, (ushort)dparam);
39.             break;
40.         case CHARTEVENT_MOUSE_WHEEL:
41.             break;
42.     }
43.     ChartRedraw();
44. };
45. //+------------------------------------------------------------------+
46. void OnDeinit(const int reason)
47. {
48.     ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, false);
49.     ChartSetInteger(0, CHART_CROSSHAIR_TOOL, true);
50. 
51.     gl_Cross.Hide();
52. };
53. //+------------------------------------------------------------------+

Code 08

Bitte beachten Sie, dass wir in Code 07 eine neue Struktur hinzugefügt haben. Ihr Zweck ist es, den aktuellen Wert basierend auf der Bewegung und Position der Maus zurückzugeben. Und beachten Sie in Code 08, dass die Header-Datei nach den Definitionen hinzugefügt wird. Dies ist wichtig, damit die Datei kompiliert werden kann. Dank dessen haben wir nun für eine ganze Weile etwas, woran wir arbeiten können.

Beginnen wir also mit einem relativ einfachen und schlanken Indikator. Ich sage „kostengünstig“, weil die Implementierung nicht viel Aufwand erfordert, nur ein wenig Aufmerksamkeit. Aber um das Material richtig zu trennen, lassen Sie uns zu einem neuen Thema übergehen.


Trendlinien-Indikator

Einer der einfachsten, kostengünstigsten und am wenigsten aufwendigen Indikatoren ist eine Trendlinie. Betrachten Sie dieses Beispiel: Wir möchten einen Indikator erstellen, der das Hinzufügen von Trendlinien ermöglicht, ohne dass dieses Objekt im MetaTrader 5-Menü ausgewählt werden muss. Wir entscheiden uns also, einen Indikator zu erstellen, um diese Art von Aktivität zu optimieren. Basierend auf Code 08, der ein zuverlässiges Werkzeug für die Erstellung aller anderen Arten von Indikatoren ist, die Objekte verwenden, werden wir kleine Änderungen am Code vornehmen, um einen Indikator zu erstellen, der eine Trendlinie verwendet.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_Prefix  "Demo"
05. //+------------------------------------------------------------------+
06. #define macro_NameObject  def_Prefix + (string)ObjectsTotal(0)
07. //+------------------------------------------------------------------+
08. #include <Tutorial\File 01.mqh>
09. //+------------------------------------------------------------------+
10. st_Cross gl_Cross;
11. //+------------------------------------------------------------------+
12. int OnInit()
13. {
14.     gl_Cross.Init();
15. 
16.     IndicatorSetString(INDICATOR_SHORTNAME, def_Prefix);
17.     ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, true);
18.     ChartSetInteger(0, CHART_CROSSHAIR_TOOL, false);
19. 
20.     return INIT_SUCCEEDED;
21. };
22. //+------------------------------------------------------------------+
23. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
24. {
25.     return rates_total;
26. };
27. //+------------------------------------------------------------------+
28. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
29. {
30.     st_TimePrice tp;
31.     static string isPaint = "";
32. 
33.     switch (id)
34.     {
35.         case CHARTEVENT_KEYDOWN:
36.             if (TerminalInfoInteger(TERMINAL_KEYSTATE_ESCAPE)) gl_Cross.Hide();
37.             break;
38.         case CHARTEVENT_MOUSE_MOVE:
39.             if (((uchar)sparam & MOUSE_MIDDLE) != 0)
40.             {
41.                 gl_Cross.Show();
42.                 tp = gl_Cross.Move((ushort)lparam, (ushort)dparam);
43.                 if (isPaint == "")
44.                 {
45.                     ObjectCreate(0, isPaint = macro_NameObject, OBJ_TREND, 0, tp.Time, tp.Price);
46.                     ObjectSetInteger(0, isPaint, OBJPROP_SELECTABLE, true);
47.                     ObjectSetInteger(0, isPaint, OBJPROP_COLOR, clrMagenta);
48.                     ObjectSetInteger(0, isPaint, OBJPROP_WIDTH, 3);
49.                     ObjectSetInteger(0, isPaint, OBJPROP_RAY_RIGHT, true);
50.                 }
51.                 ObjectMove(0, isPaint, 1, tp.Time, tp.Price);
52.             }else
53.             {
54.                 isPaint = "";
55.                 gl_Cross.Hide();
56.             }
57.             break;
58.         case CHARTEVENT_MOUSE_WHEEL:
59.             break;
60.     }
61.     ChartRedraw();
62. };
63. //+------------------------------------------------------------------+
64. void OnDeinit(const int reason)
65. {
66.     ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, false);
67.     ChartSetInteger(0, CHART_CROSSHAIR_TOOL, true);
68. 
69.     gl_Cross.Hide();
70. 
71.     if (reason == REASON_REMOVE)
72.         ObjectsDeleteAll(0, def_Prefix);
73. };
74. //+------------------------------------------------------------------+

Code 09

Code 09 wird helfen, etwas zu erklären, das viele Anfänger nicht wissen.

In der Programmierung ändert die Reihenfolge der Faktoren das Produkt.

Dieser einfache Satz, der scheinbar keinen Einfluss auf die meisten Entscheidungen im Leben eines jeden Programmierers hat, ist eine grundlegende Wahrheit, die vielen Anfängern unbekannt ist. Das ist genau so, denn wenn wir Code 09 auf dem Chart ausführen, erhalten wir das, was wir unten sehen:

Animation 06

Mit anderen Worten: Unsere Idee funktioniert. Aber Sie denken vielleicht: Was ist, wenn ich aus irgendeinem Grund die Art und Weise ändern möchte, wie die Trendlinie gezeichnet wird? Wie kann ich das tun? Dies ist der einfachste Teil, lieber Leser. Normalerweise müssen Sie ein Objekt mit einem Doppelklick auswählen. Wenn Sie jedoch den in der nächsten Abbildung gezeigten Parameter ändern, ist nur ein Klick erforderlich, um das Objekt auszuwählen.

Abbildung 03

Denken Sie daran, dass dies nur für auswählbare Objekte gilt. Aber darauf werden wir später noch näher eingehen. Vorerst sind in MetaTrader 5 alle Objekte standardmäßig auswählbar. In Ordnung, mit der in Abbildung 03 markierten Option können wir wie gezeigt Folgendes tun:

Animation 07

Mit anderen Worten: Wir sind sofort stolz darauf, dass es uns gelungen ist, einen Code aus einem anderen zu erstellen. Aber dennoch gibt es hier einen Nachteil. Wenn wir versuchen, eine neue Trendlinie zu erstellen, wie in der Animation unten gezeigt, sehen Sie, was passiert:

Animation 08

„Oh, jetzt verstehe ich gar nichts mehr. Wie seltsam. Warum wurde die vorherige Trendlinie gelöscht? Und warum erschien eine der sich schneidenden Linien nicht, als wir versuchten, eine neue Trendlinie zu erstellen? Diese Situation ist sehr seltsam. Ich hatte nicht erwartet, dass dies passiert, da der Code nach Animation 06 zu urteilen perfekt zu funktionieren schien.“ Das Problem ist also, lieber Leser, dass die Objekte in der falschen Reihenfolge erstellt werden. Es klingt seltsam. Aber ja, was wir in Animation 08 sehen, ist genau die Konsequenz daraus, Objekte in der falschen Reihenfolge zu erstellen.

Wenn Sie sich Code 09 ansehen, werden Sie feststellen, dass wir zuerst das Fadenkreuz erstellt haben. Dies geschieht in Zeile 41. Unmittelbar danach, in Zeile 45, haben wir die Trendlinie erstellt. Es wird kein Problem mit einer solchen Erstellungssequenz geben, vorausgesetzt natürlich, dass kein Versuch unternommen wird, ein neues Objekt zu erstellen. Wenn dies geschieht, wie in Animation 08 zu sehen ist, haben wir ein Problem. Obwohl tatsächlich zwei Probleme auftreten würden. Das erste Problem ist, dass das Makro in Zeile 6 von Code 09 nicht auf bereits im Chart vorhandene Objekte prüft. Wenn also Objekte mit demselben Präfix aufgelistet werden, werden wir irgendwann auf ein Objekt stoßen, das bereits im Chart vorhanden ist.

Aber wie ist das möglich? In Ordnung, nehmen wir an, dass sich anfangs keine Objekte im Chart befinden. Nach dem Erstellen des Fadenkreuzes werden wir zwei Objekte haben: eines wird „Demo0“ genannt, das andere „Demo1“. Alles läuft gut. Wenn die Trendlinie in Zeile 45 von Code 09 erstellt wird, werden wir ein drittes Objekt haben, „Demo2“.

Nun haben wir den Punkt erreicht, der der in Animation 06 gezeigten Zeile entspricht. Beim Versuch, eine neue Trendlinie zu erstellen, wird das Fadenkreuz versuchen, die Objekte „Demo1“ und „Demo2“ zu erstellen. Aber warten Sie einen Moment: Es gibt bereits ein Objekt im Chart mit dem Namen „Demo2“, das die vorherige Trendlinie darstellt. Ja, lieber Leser, genau deshalb wird eine der Fadenkreuzlinien nicht erstellt. Und dies erzeugt das, was am Anfang von Animation 08 zu sehen ist. Wenn das Fadenkreuz jedoch entfernt wird, verschwinden sowohl „Demo1“ als auch „Demo2“ und mit ihnen die vorherige Trendlinie. Und in diesem Moment befinden sich keine Objekte mehr auf dem Chart, was uns zum Ausgangszustand für das Erstellen der Trendlinie zurückführt.

Es sollte beachtet werden, dass das erste Problem aufgrund der Zählung der auf dem Chart vorhandenen Objekte auftrat. Das zweite Problem hängt jedoch mit der Reihenfolge zusammen, in der die Objekte erstellt werden. Um beide Probleme zu beseitigen, müssen wir daher Code 09 ändern und ihn in dieser Form belassen:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_Prefix  "Demo"
05. //+------------------------------------------------------------------+
06. #define macro_NameObject  def_Prefix + (string)(ObjectsTotal(0) + 1)
07. //+------------------------------------------------------------------+
08. #include <Tutorial\File 01.mqh>
09. //+------------------------------------------------------------------+
10. st_Cross gl_Cross;
11. //+------------------------------------------------------------------+
12. int OnInit()
13. {
14.     gl_Cross.Init();
15. 
16.     IndicatorSetString(INDICATOR_SHORTNAME, def_Prefix);
17.     ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, true);
18.     ChartSetInteger(0, CHART_CROSSHAIR_TOOL, false);
19. 
20.     return INIT_SUCCEEDED;
21. };
22. //+------------------------------------------------------------------+
23. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
24. {
25.     return rates_total;
26. };
27. //+------------------------------------------------------------------+
28. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
29. {
30.     st_TimePrice tp;
31.     static string isPaint = "";
32. 
33.     switch (id)
34.     {
35.         case CHARTEVENT_KEYDOWN:
36.             if (TerminalInfoInteger(TERMINAL_KEYSTATE_ESCAPE)) gl_Cross.Hide();
37.             break;
38.         case CHARTEVENT_MOUSE_MOVE:
39.             if (((uchar)sparam & MOUSE_MIDDLE) != 0)
40.             {
41.                 tp = gl_Cross.Move((ushort)lparam, (ushort)dparam);
42.                 if (isPaint == "")
43.                 {
44.                     ObjectCreate(0, isPaint = macro_NameObject, OBJ_TREND, 0, tp.Time, tp.Price);
45.                     ObjectSetInteger(0, isPaint, OBJPROP_SELECTABLE, true);
46.                     ObjectSetInteger(0, isPaint, OBJPROP_COLOR, clrMagenta);
47.                     ObjectSetInteger(0, isPaint, OBJPROP_WIDTH, 3);
48.                     ObjectSetInteger(0, isPaint, OBJPROP_RAY_RIGHT, true);
49.                 }
50.                 ObjectMove(0, isPaint, 1, tp.Time, tp.Price);
51.                 gl_Cross.Show();
52.             }else
53.             {
54.                 isPaint = "";
55.                 gl_Cross.Hide();
56.             }
57.             break;
58.         case CHARTEVENT_MOUSE_WHEEL:
59.             break;
60.     }
61.     ChartRedraw();
62. };
63. //+------------------------------------------------------------------+
64. void OnDeinit(const int reason)
65. {
66.     ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, false);
67.     ChartSetInteger(0, CHART_CROSSHAIR_TOOL, true);
68. 
69.     gl_Cross.Hide();
70. 
71.     if (reason == REASON_REMOVE)
72.         ObjectsDeleteAll(0, def_Prefix);
73. };
74. //+------------------------------------------------------------------+

Code 10

Nach dem Ausführen von Code 10 werden wir das Ergebnis in der folgenden Animation sehen:

Animation 09

Bitte beachten Sie, dass wir jetzt so viele Trendlinien hinzufügen können, wie wir möchten. Und dies geschieht ohne Probleme, da Zeile 06 von Code 10 das Problem des Namenskonflikts behebt. Und das Fadenkreuz wird erst angezeigt, nachdem die Trendlinie, die auf dem Chart platziert werden soll, erstellt wurde. Dies ist in Zeile 51 zu sehen, wo wir tatsächlich ein einfaches Fadenkreuz für die Analyse erstellt haben. Mit anderen Worten: Eine einfache Änderung der Ausführungsreihenfolge ermöglichte es uns, das zweite Problem zu lösen, das verhinderte, dass das gewünschte Objekt auf dem Chart verblieb.


Abschließende Überlegungen

In diesem Artikel haben wir uns angesehen, wie man in der Praxis das umsetzt, was viele für schwierig halten oder zumindest keinen praktischen Weg zur Umsetzung finden – das direkte Arbeiten mit Objekten in einem Indikator, deren Platzierung auf dem Chart und deren anschließende Verwendung.

Tatsächlich haben wir hier gezeigt, wie man mit einem relativ einfachen Objekt arbeitet, nämlich einer Trendlinie. Denken Sie jedoch nicht, dass die hier vorgestellte Anwendung trotz alledem nutzlos ist. Sie können das hier Gezeigte nutzen, um verschiedene Fähigkeiten zu trainieren und zu üben, während Sie versuchen, eine Anwendung zu entwickeln, die für Sie, lieber Leser, tatsächlich nützlich sein wird. Versuchen Sie, es zu studieren und zu sehen, was an dem in diesem Artikel Gezeigten verbessert werden kann.

Denken Sie daran, dass wir noch nicht mit Klassen arbeiten, was die Aufgabe noch interessanter macht. Je nachdem, wie Sie Ihre Lösungen umsetzen möchten, werden Sie feststellen, dass einige Aspekte ihrer Implementierung verbessert werden können. Diese Punkte führen uns zu einem neuen Programmierstil, der in dieser kurzen Artikelserie noch nicht vorgestellt wurde, der aber bald ebenfalls behandelt wird.

Viel Spaß also mit den Codes im Anhang und bis zum nächsten Artikel, in dem wir uns ansehen werden, wie man andere Manipulationen mit Objekten auf dem Chart durchführt.

MQ5-Datei Beschreibung
Code 01 Objektdemonstration

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

Beigefügte Dateien |
Anexo.zip (2.42 KB)
Die Übertragung der Trading-Signale in einem universalen Expert Advisor. Die Übertragung der Trading-Signale in einem universalen Expert Advisor.
In diesem Artikel wurden die verschiedenen Möglichkeiten beschrieben, um die Trading-Signale von einem Signalmodul des universalen EAs zum Steuermodul der Positionen und Orders zu übertragen. Es wurden die seriellen und parallelen Interfaces betrachtet.
Neuronale Netze im Trading: Actor–Director–Critic (Letzter Teil) Neuronale Netze im Trading: Actor–Director–Critic (Letzter Teil)
Das Framework von Actor–Director–Critic ist eine Weiterentwicklung der klassischen Agenten-Lernarchitektur. Der Artikel präsentiert praktische Erfahrungen mit seiner Implementierung und Anpassung an die Bedingungen der Finanzmärkte.
Eine alternative Log-datei mit der Verwendung der HTML und CSS Eine alternative Log-datei mit der Verwendung der HTML und CSS
In diesem Artikel werden wir eine sehr einfache, aber leistungsfähige Bibliothek zur Erstellung der HTML-Dateien schreiben, dabei lernen wir auch, wie man eine ihre Darstellung einstellen kann (nach seinem Geschmack) und sehen wir, wie man es leicht in seinem Expert Advisor oder Skript hinzufügen oder verwenden kann.
Marktsimulation (Teil 23): Positionsansicht (I) Marktsimulation (Teil 23): Positionsansicht (I)
Der Inhalt, den wir ab diesem Punkt behandeln werden, ist in Bezug auf Theorie und Konzepte weitaus komplexer. Ich werde versuchen, den Stoff so einfach wie möglich zu gestalten. Der Programmierteil selbst ist recht einfach und unkompliziert. Aber wenn Sie die Theorie dahinter nicht verstehen, werden Sie überhaupt keine praktische Grundlage haben, um das Replay-/Simulationssystem für andere Aufgaben als die, die ich zeigen werde, zu verfeinern oder anzupassen. Ich möchte nicht, dass Sie den von mir präsentierten Code lediglich kompilieren und verwenden. Ich möchte, dass Sie lernen, verstehen und, wenn möglich, in der Lage sind, etwas noch Besseres zu schaffen.