English Русский 中文 Español 日本語 Português
preview
Entwicklung eines Replay Systems (Teil 51): Die Dinge werden kompliziert (III)

Entwicklung eines Replay Systems (Teil 51): Die Dinge werden kompliziert (III)

MetaTrader 5Beispiele | 18 November 2024, 12:55
106 0
Daniel Jose
Daniel Jose

Einführung

Im vorherigen Artikel Entwicklung eines Replay Systems (Teil 50): Die Dinge werden kompliziert (II), haben wir begonnen, den Kontrollindikator weiter zu modifizieren, um sicherzustellen, dass er innerhalb der Grenzen des Charts bleibt. Nicht in einem beliebigen Chart, sondern in dem, das vom Replay/Simulator-Dienst geöffnet wurde. Das Wichtigste, was dort implementiert wurde, war die Möglichkeit, eine nutzerdefinierte Vorlage zu verwenden, nicht die vom System geforderte.

Wenn man so etwas zulässt, macht die Nutzung des gesamten Replay/Simulator-Systems viel mehr Spaß und ist für diejenigen geeignet, die das System tatsächlich zur Recherche nutzen wollen. Sie können eine Vorlage erstellen, sie im Wiederholungs-/Simulatorsystem verwenden und dann dieselbe Vorlage für ein Live-Konto nutzen. Aus diesem Grund ändern wir jetzt das System.

Allerdings war die Interaktion zwischen dem Kontrollanzeiger und dem Nutzer nicht sehr gut. Dies ist darauf zurückzuführen, dass es mehrere doppelte Punkte im System gibt. Das Hauptproblem ist jedoch, dass das System nicht so sicher und stabil war, wie es schien. Dies geschah, weil in der ersten Entwicklungsphase nicht berücksichtigt wurde, dass einige Nutzer versuchen könnten, das System auf eine nicht vorgesehene Weise zu nutzen. Aber mit dem derzeitigen Richtungswechsel und der neuen Positionierung wird sich das Niveau der Sicherheit und Stabilität allmählich angleichen.

Im Moment haben wir es noch mit einem instabilen System zu tun, was die Nutzerfreundlichkeit angeht, aber das wird bald behoben sein. Wenn dies geschehen ist, wird das gesamte System davon profitieren.

Wenn Sie den vorherigen Artikel gelesen haben (was ich Ihnen ans Herz lege), wissen Sie, dass wir begonnen haben, den Kontrollindikator direkt über den Dienst zu verwenden. Danach war der Indikator nicht mehr frei für die Platzierung auf einem beliebigen Chart verfügbar.

Nachdem ich den Code in Ruhe analysiert hatte, stellte ich fest, dass wir einige Änderungen vornehmen könnten, um ihn noch besser zu machen. Es müssen jedoch einige Änderungen an anderen Modulen vorgenommen werden, die auch vom Replay/Simulator-System verwendet werden. Aus diesem Grund werden wir uns in diesem Artikel auf die Erläuterung der Änderungen konzentrieren, die eingeführt werden. Dies liegt daran, dass einige dieser Module dem Nutzer zur Verfügung stehen werden. Daher ist es wichtig, dass Sie verstehen, was manipuliert werden kann und was nicht, denn Sie sind der Nutzer dieses Systems und der Programmierer, der seine Entwicklung überwacht. Dies ist notwendig, um Instabilitäten bei der Verwendung der einzelnen Module zu vermeiden. Bitte beachten Sie, dass einige Module auf LIVE- und DEMO-Konten verwendet werden können, nicht nur im Replay/Simulator-System.

Sie werden vielleicht nicht denken, dass die Änderungen, die Sie hier sehen, so groß sind, aber bevor wir weitere Änderungen vornehmen, müssen wir den Code stabil machen. Aus diesem Grund mag der Artikel vage erscheinen. Aber ich wiederhole: Sie müssen verstehen, wie sich das System entwickelt. Wenn Sie dies nicht verstehen, werden Sie das System nicht richtig nutzen können.

Kommen wir ohne weiteres zu den Änderungen.


Umfassende Nutzung von Modulen

Die erste Änderung, die wir vornehmen müssen, betrifft den Quellcode des Kontrollindikators. Der Code, der im vorigen Artikel besprochen wurde, verwendete einige Module nicht, die bereits entwickelt und erstellt worden waren. Dies führte zu einer gewissen Inkonsistenz zwischen dem, was auf dem Kontrollanzeiger und in anderen Modulen des Systems geschah. 

Eines dieser Module ist der Mauszeiger. Dieses Modul wurde entwickelt, um alles, was mit der Maus zu tun hat, zu sammeln. Auf diese Weise muss jeder andere neue Code nicht speziell getestet und analysiert werden. All dies wird durch das Mausanzeigemodul erledigt.

Dieser Indikator wurde vor einiger Zeit im Rahmen dieser Artikelserie über das Replay/Simulator-System entwickelt. Es wurde jedoch ursprünglich für die Arbeit mit einem realen oder Demo-Konto konzipiert und ist für die Arbeit in der aktuellen Form nicht gut geeignet, aber darauf werden wir später noch genauer eingehen.

Im folgenden Code sehen Sie, wie Sie das Mausindikatormodul in den Kontrollindikator integrieren können, der bereits einige Korrekturen und Änderungen enthält, die eine solche Integration ermöglichen.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property icon "/Images/Market Replay/Icons/Replay - Device.ico"
04. #property description "Control indicator for the Replay-Simulator service."
05. #property description "This one doesn't work without the service loaded."
06. #property version   "1.51"
07. #property link "https://www.mql5.com/en/articles/11877"
08. #property indicator_chart_window
09. #property indicator_plots 0
10. //+------------------------------------------------------------------+
11. #include <Market Replay\Service Graphics\C_Controls.mqh>
12. //+------------------------------------------------------------------+
13. C_Controls *control = NULL;
14. //+------------------------------------------------------------------+
15. input long user00 = 0;   //ID
16. //+------------------------------------------------------------------+
17. int OnInit()
18. {
19.     u_Interprocess Info;
20. 
21.     ResetLastError();       
22.     if (CheckPointer(control = new C_Controls(user00, "Market Replay Control", "Indicator Mouse Study")) == POINTER_INVALID)
23.             SetUserError(C_Terminal::ERR_PointerInvalid);
24.     if (_LastError != ERR_SUCCESS)
25.     {
26.             Print("Control indicator failed on initialization.");
27.             return INIT_FAILED;
28.     }       
29.     if (GlobalVariableCheck(def_GlobalVariableReplay)) Info.df_Value = GlobalVariableGet(def_GlobalVariableReplay); else Info.df_Value = 0;
30.     EventChartCustom(user00, C_Controls::ev_WaitOff, 1, Info.df_Value, "");
31.     (*control).Init(Info.s_Infos.isPlay);
32.     
33.     return INIT_SUCCEEDED;
34. }
35. //+------------------------------------------------------------------+
36. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
37. {
38.     static bool bWait = false;
39.     u_Interprocess Info;
40.     
41.     Info.df_Value = GlobalVariableGet(def_GlobalVariableReplay);
42.     if (!bWait)
43.     {
44.             if (Info.s_Infos.isWait)
45.             {
46.                     EventChartCustom(user00, C_Controls::ev_WaitOn, 1, 0, "");
47.                     bWait = true;
48.             }
49.     }else if (!Info.s_Infos.isWait)
50.     {
51.             EventChartCustom(user00, C_Controls::ev_WaitOff, 1, Info.df_Value, "");
52.             bWait = false;
53.     }
54. 
55.     return rates_total;
56. }
57. //+------------------------------------------------------------------+
58. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
59. {
60.     (*control).DispatchMessage(id, lparam, dparam, sparam);
61. }
62. //+------------------------------------------------------------------+
63. void OnDeinit(const int reason)
64. {
65.     switch (reason)
66.     {
67.             case REASON_TEMPLATE:
68.                     Print("Modified template. Replay/simulation system shutting down.");
69.             case REASON_PARAMETERS:
70.             case REASON_REMOVE:
71.             case REASON_CHARTCLOSE:
72.                     if (ChartSymbol(user00) != def_SymbolReplay) break;
73.                     GlobalVariableDel(def_GlobalVariableReplay);
74.                     ChartClose(user00);
75.                     break;
76.     }
77.     delete control;
78. }
79. //+------------------------------------------------------------------+

Quellcode des Kontrollindikators

Sie haben vielleicht bemerkt, dass sich dieser Code stark von dem unterscheidet, was vorher existierte. Der auffälligste Unterschied besteht jedoch in der Funktion OnInit, wo in Zeile 22 eine völlig andere Deklaration als zuvor für den Verweis auf die Steuerklasse verwendet wird.

Sie werden sich vielleicht ein wenig verloren fühlen, wenn Sie versuchen herauszufinden, was Zeile 22 bedeutet. Aber in dieser Zeile tun wir zwei Dinge:

  • Zunächst wird der Kontrollindikator in ein Modul umgewandelt; dieses Modul kann von jedem anderen Modul verwendet werden, das wissen muss, was dieser Indikator tut.
  • Zweitens teilen wir dem Kontrollindikator mit, dass wir kein Coding mehr für die Analyse der Maus verwenden werden. Mit anderen Worten: Der Kontrollindikator fragt nun den Mausindikator, was der Nutzer gerade tut oder tun will. Auf der Grundlage dieser Informationen führt der Kontrollanzeiger das entsprechende Verfahren durch.

Diese Änderung oder Entscheidung, den Mauszeiger für die Interaktion zwischen Nutzer, Maus und Grafik verantwortlich zu machen, ist sicherlich der beste Weg, dies zu erreichen. Es macht keinen Sinn, Dinge zu duplizieren, weil wir das Problem im Maus-Indikator beheben, aber Probleme im Kontroll-Indikator schaffen, und wenn wir sie zusammenfügen, stört das eine das andere. Das ist nicht das, was wir wollen. Wir wollen, dass beide harmonisch zusammenarbeiten. Auf diese Weise verschwenden wir keine Zeit mit Korrekturen, da es ausreicht, zu bearbeiten, wie die Interaktion durchgeführt wird.

Wenn Sie jetzt genau hinsehen, werden Sie feststellen, dass der Kontrollindikator die Klasse C_Terminal nicht mehr erwähnt, wie es früher der Fall war. Warum? Der Grund dafür ist die Vererbung. Ich habe beschlossen, die Kontrollklasse von der Klasse C_Terminal abzuleiten. Der Grund für diese Aktion ist jetzt etwas schwierig zu erklären, aber sie war notwendig wegen dem, was wir später tun werden.

Nach dieser ersten Erläuterung des Indikatorcodes wollen wir uns nun den Code der Steuerklasse ansehen.

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "..\Auxiliar\Interprocess.mqh"
005. //+------------------------------------------------------------------+
006. #define def_PathBMP           "Images\\Market Replay\\Control\\"
007. #define def_ButtonPlay        def_PathBMP + "Play.bmp"
008. #define def_ButtonPause       def_PathBMP + "Pause.bmp"
009. #define def_ButtonLeft        def_PathBMP + "Left.bmp"
010. #define def_ButtonLeftBlock   def_PathBMP + "Left_Block.bmp"
011. #define def_ButtonRight       def_PathBMP + "Right.bmp"
012. #define def_ButtonRightBlock  def_PathBMP + "Right_Block.bmp"
013. #define def_ButtonPin         def_PathBMP + "Pin.bmp"
014. #define def_ButtonWait        def_PathBMP + "Wait.bmp"
015. #resource "\\" + def_ButtonPlay
016. #resource "\\" + def_ButtonPause
017. #resource "\\" + def_ButtonLeft
018. #resource "\\" + def_ButtonLeftBlock
019. #resource "\\" + def_ButtonRight
020. #resource "\\" + def_ButtonRightBlock
021. #resource "\\" + def_ButtonPin
022. #resource "\\" + def_ButtonWait
023. //+------------------------------------------------------------------+
024. #define def_PrefixObjectName    "Market Replay _ "
025. #define def_NameObjectsSlider   def_PrefixObjectName + "Slider"
026. #define def_PosXObjects         120
027. //+------------------------------------------------------------------+
028. #include "..\Auxiliar\C_Terminal.mqh"
029. #include "..\Auxiliar\C_Mouse.mqh"
030. //+------------------------------------------------------------------+
031. class C_Controls : private C_Terminal
032. {
033.    protected:
034.            enum EventCustom {ev_WaitOn, ev_WaitOff};
035.    private :
036. //+------------------------------------------------------------------+
037.            string  m_szBtnPlay;
038.            bool    m_bWait;
039.            struct st_00
040.            {
041.                    string  szBtnLeft,
042.                            szBtnRight,
043.                            szBtnPin,
044.                            szBarSlider,
045.                            szBarSliderBlock;
046.                    int     posPinSlider,
047.                            posY,
048.                            Minimal;
049.            }m_Slider;
050.            C_Mouse *m_MousePtr;
051. //+------------------------------------------------------------------+
052. inline void CreateObjectBitMap(int x, int y, string szName, string Resource1, string Resource2 = NULL)
053.                    {
054.                            ObjectCreate(GetInfoTerminal().ID, szName, OBJ_BITMAP_LABEL, 0, 0, 0);
055.                            ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_XDISTANCE, def_PosXObjects + x);
056.                            ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_YDISTANCE, y);
057.                            ObjectSetString(GetInfoTerminal().ID, szName, OBJPROP_BMPFILE, 0, "::" + Resource1);
058.                            ObjectSetString(GetInfoTerminal().ID, szName, OBJPROP_BMPFILE, 1, "::" + (Resource2 == NULL ? Resource1 : Resource2));
059.                            ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_ZORDER, 1);
060.                    }
061. //+------------------------------------------------------------------+
062. inline void CreteBarSlider(int x, int size)
063.                    {
064.                            ObjectCreate(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJ_RECTANGLE_LABEL, 0, 0, 0);
065.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_XDISTANCE, def_PosXObjects + x);
066.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_YDISTANCE, m_Slider.posY - 4);
067.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_XSIZE, size);
068.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_YSIZE, 9);
069.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_BGCOLOR, clrLightSkyBlue);
070.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_BORDER_COLOR, clrBlack);
071.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_WIDTH, 3);
072.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_BORDER_TYPE, BORDER_FLAT);
073.                            ObjectCreate(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJ_RECTANGLE_LABEL, 0, 0, 0);
074.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_XDISTANCE, def_PosXObjects + x);
075.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_YDISTANCE, m_Slider.posY - 9);
076.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_YSIZE, 19);
077.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_BGCOLOR, clrRosyBrown);
078.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_BORDER_TYPE, BORDER_RAISED);
079.                    }
080. //+------------------------------------------------------------------+
081.            void CreateBtnPlayPause(bool state)
082.                    {
083.                            m_szBtnPlay = def_PrefixObjectName + "Play";
084.                            CreateObjectBitMap(0, 25, m_szBtnPlay, (m_bWait ? def_ButtonWait : def_ButtonPause), (m_bWait ? def_ButtonWait : def_ButtonPlay));
085.                            ObjectSetInteger(GetInfoTerminal().ID, m_szBtnPlay, OBJPROP_STATE, state);
086.                    }
087. //+------------------------------------------------------------------+
088.            void CreteCtrlSlider(void)
089.                    {
090.                            u_Interprocess Info;
091.                            
092.                            m_Slider.szBarSlider       = def_NameObjectsSlider + " Bar";
093.                            m_Slider.szBarSliderBlock  = def_NameObjectsSlider + " Bar Block";
094.                            m_Slider.szBtnLeft         = def_NameObjectsSlider + " BtnL";
095.                            m_Slider.szBtnRight        = def_NameObjectsSlider + " BtnR";
096.                            m_Slider.szBtnPin          = def_NameObjectsSlider + " BtnP";
097.                            m_Slider.posY = 40;
098.                            CreteBarSlider(77, 436);
099.                            CreateObjectBitMap(47, 25, m_Slider.szBtnLeft, def_ButtonLeft, def_ButtonLeftBlock);
100.                            CreateObjectBitMap(511, 25, m_Slider.szBtnRight, def_ButtonRight, def_ButtonRightBlock);
101.                            CreateObjectBitMap(0, m_Slider.posY, m_Slider.szBtnPin, def_ButtonPin);
102.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBtnPin, OBJPROP_ANCHOR, ANCHOR_CENTER);
103.                            if (GlobalVariableCheck(def_GlobalVariableReplay)) Info.df_Value = GlobalVariableGet(def_GlobalVariableReplay); else Info.df_Value = 0;
104.                            m_Slider.Minimal = Info.s_Infos.iPosShift;
105.                            PositionPinSlider(Info.s_Infos.iPosShift);
106.                    }
107. //+------------------------------------------------------------------+
108. inline void RemoveCtrlSlider(void)
109.                    {                       
110.                            ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, false);
111.                            ObjectsDeleteAll(GetInfoTerminal().ID, def_NameObjectsSlider);
112.                            ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, true);
113.                    }
114. //+------------------------------------------------------------------+
115. inline void PositionPinSlider(int p, const int minimal = 0)
116.                    {
117.                            m_Slider.posPinSlider = (p < minimal ? minimal : (p > def_MaxPosSlider ? def_MaxPosSlider : p));
118.                            m_Slider.posPinSlider = (p < m_Slider.Minimal ? m_Slider.Minimal : (p > def_MaxPosSlider ? def_MaxPosSlider : p));
119.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBtnPin, OBJPROP_XDISTANCE, m_Slider.posPinSlider + def_PosXObjects + 95);
120.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBtnLeft, OBJPROP_STATE, m_Slider.posPinSlider != minimal);
121.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBtnLeft, OBJPROP_STATE, m_Slider.posPinSlider != m_Slider.Minimal);
122.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBtnRight, OBJPROP_STATE, m_Slider.posPinSlider < def_MaxPosSlider);
123.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_XSIZE, minimal + 2);
124.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_XSIZE, m_Slider.Minimal + 2);
125.                            ChartRedraw(GetInfoTerminal().ID);
126.                    }
127. //+------------------------------------------------------------------+
128.    public  :
129. //+------------------------------------------------------------------+
130.            C_Controls(const long Arg0, const string szShortName, C_Mouse *MousePtr)
131.                    :C_Terminal(Arg0),
132.                     m_bWait(false),
133.                     m_MousePtr(MousePtr)
134.                    {
135.                            if (!IndicatorCheckPass(szShortName)) SetUserError(C_Terminal::ERR_Unknown);
136.                            m_szBtnPlay          = NULL;
137.                            m_Slider.szBarSlider = NULL;
138.                            m_Slider.szBtnPin    = NULL;
139.                            m_Slider.szBtnLeft   = NULL;
140.                            m_Slider.szBtnRight  = NULL;
141.                    }
142. //+------------------------------------------------------------------+
143.            ~C_Controls()
144.                    {
145.                            ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, false);
146.                            ObjectsDeleteAll(GetInfoTerminal().ID, def_PrefixObjectName);
147.                    }
148. //+------------------------------------------------------------------+
149.            void Init(const bool state)
150.                    {
151.                            CreateBtnPlayPause(state);
152.                            GlobalVariableTemp(def_GlobalVariableReplay);
153.                            if (!state) CreteCtrlSlider();
154.                            ChartRedraw(GetInfoTerminal().ID);
155.                    }
156. //+------------------------------------------------------------------+
157.            void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
158.                    {
159.                            u_Interprocess Info;
160.                            
161.                            switch (id)
162.                            {
163.                                    case (CHARTEVENT_CUSTOM + C_Controls::ev_WaitOn):
164.                                            if (lparam == 0) break;
165.                                            m_bWait = true;
166.                                            CreateBtnPlayPause(true);
167.                                            break;
168.                                    case (CHARTEVENT_CUSTOM + C_Controls::ev_WaitOff):
169.                                            if (lparam == 0) break;
170.                                            m_bWait = false;
171.                                            Info.df_Value = dparam;
172.                                            CreateBtnPlayPause(Info.s_Infos.isPlay);
173.                                            break;
174.                                    case CHARTEVENT_OBJECT_DELETE:
175.                                            if (StringSubstr(sparam, 0, StringLen(def_PrefixObjectName)) == def_PrefixObjectName)
176.                                            {
177.                                                    if (StringSubstr(sparam, 0, StringLen(def_NameObjectsSlider)) == def_NameObjectsSlider)
178.                                                    {
179.                                                            RemoveCtrlSlider();
180.                                                            CreteCtrlSlider();
181.                                                    }else
182.                                                    {
183.                                                            Info.df_Value = GlobalVariableGet(def_GlobalVariableReplay);
184.                                                            CreateBtnPlayPause(Info.s_Infos.isPlay);
185.                                                    }
186.                                                    ChartRedraw(GetInfoTerminal().ID);
187.                                            }
188.                                            break;
189.                            }
190.                    }
191. //+------------------------------------------------------------------+
192. };
193. //+------------------------------------------------------------------+
194. #undef def_PosXObjects
195. #undef def_ButtonPlay
196. #undef def_ButtonPause
197. #undef def_ButtonLeft
198. #undef def_ButtonRight
199. #undef def_ButtonPin
200. #undef def_NameObjectsSlider
201. #undef def_PrefixObjectName
202. #undef def_PathBMP
203. //+------------------------------------------------------------------+

Quellcode der Klasse C_Control

Möglicherweise bemerken Sie keine wesentlichen Änderungen im Code. Sie sind in der Tat recht subtil. Erstens ist die Steuerklasse ein privater Erbe der Klasse C_Terminal. Dies ist in Zeile 31 zu sehen. Auf diese Weise brauchen wir keinen Zeiger mehr für den Zugriff auf die Klasse C_Terminal zu verwenden. Mit anderen Worten, der Bulk-Zugriff auf die Klasse C_Terminal kann direkt ausgeführt werden, und in einigen Fällen aufgrund bestimmter Kompilierungsdetails sogar etwas schneller, aber das ist nicht mehr wichtig.

In Zeile 130, wo der Klassenkonstruktor deklariert wird, haben wir etwas sehr Interessantes. Bitte beachten Sie, dass in diesem frühen Stadium nur die Dienststelle Zugriff auf diesen Kontrollindikator hat. Sehen Sie sich Zeile 135 an. Eigentlich ist dies nicht notwendig, aber da die Wiederverwendung von Code immer sinnvoll ist, stellen wir sicher, dass der Indikator den gewünschten Annahmen entspricht. Mit anderen Worten: Es sollte nur einen einzigen Indikator pro Chart geben. Auch dies ist nicht notwendig, da in diesem frühen Stadium nur der Dienst Zugang zu diesem Indikator hat und ihn dem Chart hinzufügen kann. Es gibt noch andere Möglichkeiten, dies zu tun, aber ich werde nicht ins Detail gehen, weil ich niemanden ermutigen möchte, die Ressource zu nutzen, ohne zu verstehen, wie sie funktioniert.

Ein weiterer erwähnenswerter Punkt in diesem Konstruktor ist, dass wir in Zeile 133 den Zeiger in einer privaten globalen Klassenvariablen speichern, um auf den Mauszeiger zuzugreifen. Aber das wird später klar werden.

Wenn Sie aufpassen, können Sie in diesem Code eine Korrektur sehen, die vorgenommen wurde. Es ist eine sehr subtile Änderung, die aber einen großen Unterschied macht. Nicht für den Code, sondern für MetaTrader 5. Der Punkt ist, dass ChartRedraw-Aufrufe einen Wert erhalten. Normalerweise werden bei diesem Aufruf keine Werte übergeben. Warum also tun wir das jetzt? Der Grund dafür ist, dass der Wert der Chart-ID unterschiedlich ist.

Ich habe diesen Unterschied im vorigen Artikel erläutert. Aber vielleicht ist das noch nicht deutlich genug, sodass wir die gewonnenen Erkenntnisse konsolidieren sollten. Das Hauptproblem bei der Chart-ID ist nicht, wer das Chart öffnet, sondern wer Objekte darauf platziert. Nehmen wir uns einen Moment Zeit, um zu verstehen, warum wir manchmal einen Wert an den ChartRedraw-Aufruf übergeben müssen und manchmal nicht.

Wenn der Dienst ein Chart öffnet (obwohl es alles sein kann), erhält dieses Chart eine ID von MetaTrader 5. Wenn Sie diesen ID überprüfen, sehen Sie beim Parsen des von ChartOpen zurückgegebenen Wertes einen Wert. Gut. Um nun z.B. einen Indikator in diesem Chart zu platzieren, muss das Programm dieselbe ID verwenden, der von der Funktion ChartOpen zurückgegeben wird. So weit ist alles klar, aber genau an dieser Stelle taucht ein Problem auf.

Wenn Sie als Nutzer denselben Indikator auf dem Chart platzieren, erhalten Sie nicht dieselbe Chart-ID, die das Programm mit ChartOpen erhalten hat. Jetzt scheint alles verwirrend. Es mag sogar so aussehen, als wäre ich verrückt oder würde nicht verstehen, wovon ich rede. Wenn der Dienst das Chart öffnet, erhalten wir den ID-Wert über ChartOpen, und wir sollten diesen Wert verwenden, um das Indikator-Handle zu erstellen, um den Indikator auf dem gewünschten Chart zu platzieren. Wenn der Indikator, wie im Falle des Kontrollindikators, Funktionen verwendet, um Objekte mit Hilfe eines ObjectCreate-Aufrufs im Chart zu platzieren, müssen wir einen Bezeichner angeben, damit MetaTrader 5 weiß, welches Chart das richtige ist.

Die angegebene ID darf NICHT die ID sein, die man über ChartID erhält, wenn das Chart über ChartOpen geöffnet wurde. Wenn wir die in ChartID angegebene Chart-ID verwenden, wird das Objekt beim Aufruf von ObjectCreate in einem mit ChartOpen geöffneten Chart nicht im Chart angezeigt. Wenn jedoch derselbe Code vom Nutzer oder einer Vorlage mit der Funktion ObjectCreate in das Chart eingefügt wird, muss der von ChartID bereitgestellte Wert als ID verwendet werden.

Ich weiß, es scheint sehr verwirrend, aber genau das ist nötig. Aus diesem Grund wurde vor ein paar Artikeln die Klasse C_Terminal speziell für dieses Problem aktualisiert.

Da unser Code aber nicht weiß, wer genau ihn auf dem Chart ausgeführt hat, verwenden wir einen Aufruf an die Klasse C_Terminal, um die korrekte ID zurückzugeben. Auf diese Weise wird die Funktion ChartRedraw das Chart wie erwartet korrekt aktualisieren.

Trotz all dieser Komplexität können Sie in Zeile 157, wo der Message-Handler beginnt, sehen, dass dieser Code für die Behandlung der vom MetaTrader 5 gemeldeten Ereignisse verantwortlich ist. Wir haben Ereignisse entfernt, die Mausbewegungen und Klickereignisse auswerten. Der Grund dafür ist, dass wir diese Art von Ereignissen etwas anders behandeln werden, wenn auch innerhalb derselben Funktion. 

Bevor wir jedoch mit der Verarbeitung beginnen, müssen wir noch einige Änderungen vornehmen. Aber jetzt steht es weder in diesem Indikatorcode noch im Servicecode. Wir müssen den Code des Mausindikators noch einmal überprüfen und einige Änderungen vornehmen.


Aktualisierung des Codes für den Mauszeiger

Die Aktualisierung, die wir jetzt vornehmen werden, steht nicht im Widerspruch zu dem, was bereits in der Mausanzeige verwendet wird. Wir müssen diese Aktualisierung genau aus dem oben genannten Grund vornehmen. Sobald wir den Replay/Simulator-Dienst starten, wird ein Mausindikator auf dem Chart angezeigt, sodass wir mit dem Replay/Simulator-System interagieren können. Über einige Dinge müssen Sie sich jedoch keine Gedanken machen, da der Code selbst die notwendigen Änderungen vornimmt, sodass der Mausindikator genauso funktioniert, wie wenn Sie als Nutzer ihn manuell oder mithilfe einer Vorlage im Chart platziert hätten.

Das große Problem tritt auf, wenn wir die Werkzeuge verwenden, die wir haben, um das Wiedergabe-/Simulatorsystem richtig zu initialisieren. Schauen wir uns also die erste Aktualisierung an, nämlich den Quellcode des Mauszeigers. Hier ist der vollständige Code.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property description "This is an indicator for graphical studies using the mouse."
04. #property description "This is an integral part of the Replay / Simulator system."
05. #property description "However it can be used in the real market."
06. #property version "1.51"
07. #property icon "/Images/Market Replay/Icons/Indicators.ico"
08. #property link "https://www.mql5.com/en/articles/11877"
09. #property indicator_chart_window
10. #property indicator_plots 0
11. #property indicator_buffers 1
12. //+------------------------------------------------------------------+
13. #include <Market Replay\Auxiliar\Study\C_Study.mqh>
14. //+------------------------------------------------------------------+
15. C_Study *Study      = NULL;
16. //+------------------------------------------------------------------+
17. input long  user00  = 0;                                    //ID
18. input C_Study::eStatusMarket user01 = C_Study::eAuction;    //Market Status
19. input color user02  = clrBlack;                             //Price Line
20. input color user03  = clrPaleGreen;                         //Positive Study
21. input color user04  = clrLightCoral;                        //Negative Study
22. //+------------------------------------------------------------------+
23. C_Study::eStatusMarket m_Status;
24. int m_posBuff = 0;
25. double m_Buff[];
26. //+------------------------------------------------------------------+
27. int OnInit()
28. {
29.     ResetLastError();
30.     Study = new C_Study(user00, "Indicator Mouse Study", user02, user03, user04);
31.     if (_LastError != ERR_SUCCESS) return INIT_FAILED;
32.     if ((*Study).GetInfoTerminal().szSymbol != def_SymbolReplay)
33.     {
34.             MarketBookAdd((*Study).GetInfoTerminal().szSymbol);
35.             OnBookEvent((*Study).GetInfoTerminal().szSymbol);
36.             m_Status = C_Study::eCloseMarket;
37.     }else
38.             m_Status = user01;
39.     SetIndexBuffer(0, m_Buff, INDICATOR_DATA);
40.     ArrayInitialize(m_Buff, EMPTY_VALUE);
41.     
42.     return INIT_SUCCEEDED;
43. }
44. //+------------------------------------------------------------------+
45. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
46. {
47.     m_posBuff = rates_total - 4;
48.     (*Study).Update(m_Status);      
49.     
50.     return rates_total;
51. }
52. //+------------------------------------------------------------------+
53. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
54. {
55.     (*Study).DispatchMessage(id, lparam, dparam, sparam);
56.     SetBuffer();
57.     
58.     ChartRedraw((*Study).GetInfoTerminal().ID);
59. }
60. //+------------------------------------------------------------------+
61. void OnBookEvent(const string &symbol)
62. {
63.     MqlBookInfo book[];
64.     C_Study::eStatusMarket loc = m_Status;
65.    
66.     if (symbol != (*Study).GetInfoTerminal().szSymbol) return;
67.     MarketBookGet((*Study).GetInfoTerminal().szSymbol, book);
68.     m_Status = (ArraySize(book) == 0 ? C_Study::eCloseMarket : C_Study::eInTrading);
69.     for (int c0 = 0; (c0 < ArraySize(book)) && (m_Status != C_Study::eAuction); c0++)
70.             if ((book[c0].type == BOOK_TYPE_BUY_MARKET) || (book[c0].type == BOOK_TYPE_SELL_MARKET)) m_Status = C_Study::eAuction;
71.     if (loc != m_Status) (*Study).Update(m_Status);
72. }
73. //+------------------------------------------------------------------+
74. void OnDeinit(const int reason)
75. {
76.     if (reason != REASON_INITFAILED)
77.     {
78.             if ((*Study).GetInfoTerminal().szSymbol != def_SymbolReplay)
79.                     MarketBookRelease((*Study).GetInfoTerminal().szSymbol);
80.             delete Study;
81.     }
82. }
83. //+------------------------------------------------------------------+
84. inline void SetBuffer(void)
85. {
86.     uCast_Double Info;
87.     
88.     m_posBuff = (m_posBuff < 0 ? 0 : m_posBuff);
89.     m_Buff[m_posBuff + 0] = (*Study).GetInfoMouse().Position.Price;
90.     Info._datetime = (*Study).GetInfoMouse().Position.dt;
91.     m_Buff[m_posBuff + 1] = Info.dValue;
92.     Info._int[0] = (*Study).GetInfoMouse().Position.X;
93.     Info._int[1] = (*Study).GetInfoMouse().Position.Y;
94.     m_Buff[m_posBuff + 2] = Info.dValue;
95.     Info._char[0] = ((*Study).GetInfoMouse().ExecStudy == C_Mouse::eStudyNull ? (char)(*Study).GetInfoMouse().ButtonStatus : 0);
96.     m_Buff[m_posBuff + 3] = Info.dValue;
97. }
98. //+------------------------------------------------------------------+

Quellcode des Mauszeigers

Achtung! Man könnte meinen, dass es keinen Unterschied zwischen diesem Code und seiner vorherigen Version gibt. Aber es gibt Unterschiede. Mit ihnen muss man sehr vorsichtig umgehen.

Für den Nutzer besteht der Hauptunterschied in Zeile 17. Dies ist wahrscheinlich die komplexeste Zeile des gesamten Codes. Im Idealfall sollte der Nutzer diese Zeile NIEMALS ändern oder in der Indikatorschnittstelle sehen können. Dies liegt daran, dass jeder neue Nutzer versucht sein wird, diese Zeile zu ändern, da es sich um eine Eingabe handelt. Bitte denken Sie jedoch daran, dass Sie diesen Wert NIEMALS ändern dürfen. Dieser Wert muss entweder von dem Programm, das den Indikator aufruft, oder von dem Indikator selbst gesetzt werden. Sie als Nutzer sollten diesen Wert jedoch NIEMALS ändern. Die anderen Werte können vom Nutzer problemlos konfiguriert und geändert werden, aber der ID-Wert kann niemals geändert werden.

Außerdem ist der Inhalt von Zeile 30 interessant. Hier initialisieren wir den Zeiger der Studienklasse. Bisher erhielt der Konstruktor 3 Werte, die hauptsächlich die Farben für den Mauszeiger darstellten, aber jetzt erhält der Konstruktor zwei zusätzliche Werte. Der erste Wert ist die Chart-ID und der zweite ist der Name des Indikators. Dieser Name wird für spätere Zugriffe auf den Indikatorpuffer verwendet. Wenn der Mausindikator im Chart angezeigt wird und wir seinen Puffer lesen wollen, verwenden wir den hier angegebenen Namen.

Es gibt noch ein paar weitere Unterschiede im Code, aber da sie nicht schwer zu verstehen sind, werden wir sie nicht diskutieren. Dann können wir mit dem Code der Klasse C_Study fortfahren. In diesem Code werden wir keinen Zeiger für den Zugriff auf die Klasse C_Terminal verwenden, sondern stattdessen Vererbung einsetzen.

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "..\C_Mouse.mqh"
005. //+------------------------------------------------------------------+
006. #define def_ExpansionPrefix "MouseExpansion_"
007. #define def_ExpansionBtn1 def_ExpansionPrefix + "B1"
008. #define def_ExpansionBtn2 def_ExpansionPrefix + "B2"
009. #define def_ExpansionBtn3 def_ExpansionPrefix + "B3"
010. //+------------------------------------------------------------------+
011. class C_Study : public C_Mouse
012. {
013.    private :
014. //+------------------------------------------------------------------+
015.            struct st00
016.            {
017.                    eStatusMarket   Status;
018.                    MqlRates        Rate;
019.                    string          szInfo;
020.                    color           corP,
021.                                    corN;
022.                    int             HeightText;
023.            }m_Info;
024. //+------------------------------------------------------------------+
025.            const datetime GetBarTime(void)
026.                    {
027.                            datetime dt;
028.                            u_Interprocess info;
029.                            int i0 = PeriodSeconds();
030.                            
031.                            if (m_Info.Status == eInReplay)
032.                            {
033.                                    if (!GlobalVariableGet(def_GlobalVariableServerTime, info.df_Value)) return ULONG_MAX;
034.                                    if ((dt = info.ServerTime) == ULONG_MAX) return ULONG_MAX;
035.                            }else dt = TimeCurrent();
036.                            if (m_Info.Rate.time <= dt)
037.                                    m_Info.Rate.time = (datetime)(((ulong) dt / i0) * i0) + i0;
038. 
039.                            return m_Info.Rate.time - dt;
040.                    }
041. //+------------------------------------------------------------------+
042.            void Draw(void)
043.                    {
044.                            double v1;
045.                            
046.                            ObjectSetInteger(GetInfoTerminal().ID, def_ExpansionBtn1, OBJPROP_YDISTANCE, GetInfoMouse().Position.Y - 18);
047.                            ObjectSetInteger(GetInfoTerminal().ID, def_ExpansionBtn2, OBJPROP_YDISTANCE, GetInfoMouse().Position.Y - 1);
048.                            ObjectSetInteger(GetInfoTerminal().ID, def_ExpansionBtn3, OBJPROP_YDISTANCE, GetInfoMouse().Position.Y - 1);
049.                            ObjectSetString(GetInfoTerminal().ID, def_ExpansionBtn1, OBJPROP_TEXT, m_Info.szInfo);
050.                            v1 = NormalizeDouble(100.0 - ((m_Info.Rate.close / GetInfoMouse().Position.Price) * 100.0), 2);
051.                            ObjectSetInteger(GetInfoTerminal().ID, def_ExpansionBtn2, OBJPROP_BGCOLOR, (v1 < 0 ? m_Info.corN : m_Info.corP));
052.                            ObjectSetString(GetInfoTerminal().ID, def_ExpansionBtn2, OBJPROP_TEXT, StringFormat("%.2f%%", MathAbs(v1)));
053.                            v1 = NormalizeDouble(100.0 - ((m_Info.Rate.close / iClose(GetInfoTerminal().szSymbol, PERIOD_D1, 0)) * 100.0), 2);
054.                            ObjectSetInteger(GetInfoTerminal().ID, def_ExpansionBtn3, OBJPROP_BGCOLOR, (v1 < 0 ? m_Info.corN : m_Info.corP));
055.                            ObjectSetString(GetInfoTerminal().ID, def_ExpansionBtn3, OBJPROP_TEXT, StringFormat("%.2f%%", MathAbs(v1)));
056.                    }
057. //+------------------------------------------------------------------+
058.    public  :
059. //+------------------------------------------------------------------+
060.            C_Study(long IdParam, string szShortName, color corH, color corP, color corN)
061.                    :C_Mouse(IdParam, szShortName, corH, corP, corN)
062.                    {
063.                            if (_LastError != ERR_SUCCESS) return;
064.                            ZeroMemory(m_Info);
065.                            m_Info.Status = eCloseMarket;
066.                            m_Info.Rate.close = iClose(GetInfoTerminal().szSymbol, PERIOD_D1, ((GetInfoTerminal().szSymbol == def_SymbolReplay) || (macroGetDate(TimeCurrent()) != macroGetDate(iTime(GetInfoTerminal().szSymbol, PERIOD_D1, 0))) ? 0 : 1));
067.                            m_Info.corP = corP;
068.                            m_Info.corN = corN;
069.                            CreateObjectInfo(2, 110, def_ExpansionBtn1, clrPaleTurquoise);
070.                            CreateObjectInfo(2, 53, def_ExpansionBtn2);
071.                            CreateObjectInfo(58, 53, def_ExpansionBtn3);
072.                    }
073. //+------------------------------------------------------------------+
074.            ~C_Study()
075.                    {
076.                            ObjectsDeleteAll(GetInfoTerminal().ID, def_ExpansionPrefix);
077.                    }
078. //+------------------------------------------------------------------+
079.            void Update(const eStatusMarket arg)
080.                    {
081.                            datetime dt;
082.                            
083.                            switch (m_Info.Status = (m_Info.Status != arg ? arg : m_Info.Status))
084.                            {
085.                                    case eCloseMarket : m_Info.szInfo = "Closed Market";
086.                                            break;
087.                                    case eInReplay    :
088.                                    case eInTrading   :
089.                                            if ((dt = GetBarTime()) < ULONG_MAX)
090.                                            {
091.                                                    m_Info.szInfo = TimeToString(dt, TIME_SECONDS);
092.                                                    break;
093.                                            }
094.                                    case eAuction     : m_Info.szInfo = "Auction";
095.                                            break;
096.                                    default           : m_Info.szInfo = "ERROR";
097.                            }
098.                            Draw();
099.                    }
100. //+------------------------------------------------------------------+
101. virtual void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
102.                    {
103.                            C_Mouse::DispatchMessage(id, lparam, dparam, sparam);
104.                            if (id == CHARTEVENT_MOUSE_MOVE) Draw();
105.                    }
106. //+------------------------------------------------------------------+
107. };
108. //+------------------------------------------------------------------+
109. #undef def_ExpansionBtn3
110. #undef def_ExpansionBtn2
111. #undef def_ExpansionBtn1
112. #undef def_ExpansionPrefix
113. //+------------------------------------------------------------------+

Quellcode der Klasse C_Study

Es gibt praktisch keinen Unterschied zwischen diesem Code und dem alten. Der einzige Unterschied besteht darin, dass wir den Graph-Identifikator direkt in der Klasse C_Terminal suchen. Früher haben wir einen Zeiger verwendet und jetzt verwenden wir Vererbung, aber woher kommt diese Vererbung? Sie stammt aus der Klasse C_Mouse. Schauen wir uns den Code der Klasse C_Mouse an, um diese Vererbung besser zu verstehen.

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "C_Terminal.mqh"
005. #include "Interprocess.mqh"
006. //+------------------------------------------------------------------+
007. #define def_MousePrefixName "MouseBase_"
008. #define def_NameObjectLineH def_MousePrefixName + "H"
009. #define def_NameObjectLineV def_MousePrefixName + "TV"
010. #define def_NameObjectLineT def_MousePrefixName + "TT"
011. #define def_NameObjectStudy def_MousePrefixName + "TB"
012. //+------------------------------------------------------------------+
013. class C_Mouse : public C_Terminal
014. {
015.    public  :
016.            enum eStatusMarket {eCloseMarket, eAuction, eInTrading, eInReplay};
017.            enum eBtnMouse {eKeyNull = 0x00, eClickLeft = 0x01, eClickRight = 0x02, eSHIFT_Press = 0x04, eCTRL_Press = 0x08, eClickMiddle = 0x10};
018.            struct st_Mouse
019.            {
020.                    struct st00
021.                    {
022.                            int      X,
023.                                     Y;
024.                            double   Price;
025.                            datetime dt;
026.                    }Position;
027.                    uint    ButtonStatus;
028.                    bool    ExecStudy;
029.            };
030. //+------------------------------------------------------------------+
031.    protected:
032.            enum eEventsMouse {ev_HideMouse, ev_ShowMouse};
033. //+------------------------------------------------------------------+
034.            void CreateObjectInfo(int x, int w, string szName, color backColor = clrNONE) const
035.                    {
036.                            if (m_Mem.szShortName != NULL) return;
037.                            CreateObjectGraphics(szName, OBJ_BUTTON, clrNONE);
038.                            ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_STATE, true);
039.                            ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_BORDER_COLOR, clrBlack);
040.                            ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_COLOR, clrBlack);
041.                            ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_BGCOLOR, backColor);
042.                            ObjectSetString(GetInfoTerminal().ID, szName, OBJPROP_FONT, "Lucida Console");
043.                            ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_FONTSIZE, 10);
044.                            ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_CORNER, CORNER_LEFT_UPPER); 
045.                            ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_XDISTANCE, x);
046.                            ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_YDISTANCE, TerminalInfoInteger(TERMINAL_SCREEN_HEIGHT) + 1);
047.                            ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_XSIZE, w); 
048.                            ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_YSIZE, 18);
049.                    }
050. //+------------------------------------------------------------------+
051.    private :
052.            enum eStudy {eStudyNull, eStudyCreate, eStudyExecute};
053.            struct st01
054.            {
055.                    st_Mouse Data;
056.                    color    corLineH,
057.                             corTrendP,
058.                             corTrendN;
059.                    eStudy   Study;
060.            }m_Info;
061.            struct st_Mem
062.            {
063.                    bool     CrossHair,
064.                             IsFull;
065.                    datetime dt;
066.                    string   szShortName;
067.            }m_Mem;
068. //+------------------------------------------------------------------+
069.            void GetDimensionText(const string szArg, int &w, int &h)
070.                    {
071.                            TextSetFont("Lucida Console", -100, FW_NORMAL);
072.                            TextGetSize(szArg, w, h);
073.                            h += 5;
074.                            w += 5;
075.                    }
076. //+------------------------------------------------------------------+
077.            void CreateStudy(void)
078.                    {
079.                            if (m_Mem.IsFull)
080.                            {
081.                                    CreateObjectGraphics(def_NameObjectLineV, OBJ_VLINE, m_Info.corLineH);
082.                                    CreateObjectGraphics(def_NameObjectLineT, OBJ_TREND, m_Info.corLineH);
083.                                    ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectLineT, OBJPROP_WIDTH, 2);
084.                                    CreateObjectInfo(0, 0, def_NameObjectStudy);
085.                            }
086.                            m_Info.Study = eStudyCreate;
087.                    }
088. //+------------------------------------------------------------------+
089.            void ExecuteStudy(const double memPrice)
090.                    {
091.                            double v1 = GetInfoMouse().Position.Price - memPrice;
092.                            int w, h;
093.                            
094.                            if (!CheckClick(eClickLeft))
095.                            {
096.                                    m_Info.Study = eStudyNull;
097.                                    ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, true);
098.                                    if (m_Mem.IsFull) ObjectsDeleteAll(GetInfoTerminal().ID, def_MousePrefixName + "T");
099.                            }else if (m_Mem.IsFull)
100.                            {
101.                                    string sz1 = StringFormat(" %." + (string)GetInfoTerminal().nDigits + "f [ %d ] %02.02f%% ",
102.                                            MathAbs(v1), Bars(GetInfoTerminal().szSymbol, PERIOD_CURRENT, m_Mem.dt, GetInfoMouse().Position.dt) - 1, MathAbs((v1 /memPrice) * 100.0)));
103.                                    GetDimensionText(sz1, w, h);
104.                                    ObjectSetString(GetInfoTerminal().ID, def_NameObjectStudy, OBJPROP_TEXT, sz1);                                                                                                                          
105.                                    ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectStudy, OBJPROP_BGCOLOR, (v1 < 0 ? m_Info.corTrendN : m_Info.corTrendP));
106.                                    ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectStudy, OBJPROP_XSIZE, w);
107.                                    ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectStudy, OBJPROP_YSIZE, h);
108.                                    ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectStudy, OBJPROP_XDISTANCE, GetInfoMouse().Position.X - w);
109.                                    ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectStudy, OBJPROP_YDISTANCE, GetInfoMouse().Position.Y - (v1 < 0 ? 1 : h));                           
110.                                    ObjectMove(GetInfoTerminal().ID, def_NameObjectLineT, 1, GetInfoMouse().Position.dt, GetInfoMouse().Position.Price);
111.                                    ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectLineT, OBJPROP_COLOR, (memPrice > GetInfoMouse().Position.Price ? m_Info.corTrendN : m_Info.corTrendP));
112.                            }
113.                            m_Info.Data.ButtonStatus = eKeyNull;
114.                    }
115. //+------------------------------------------------------------------+
116.    public  :
117. //+------------------------------------------------------------------+
118.            C_Mouse(const long id, const string szShortName)
119.                    :C_Terminal(id)
120.                    {
121.                            m_Mem.szShortName = szShortName;
122.                    }
123. //+------------------------------------------------------------------+
124.            C_Mouse(const long id, const string szShortName, color corH, color corP, color corN)
125.                    :C_Terminal(id)
126.                    {
127.                            if (!IndicatorCheckPass(szShortName)) SetUserError(C_Terminal::ERR_Unknown);
128.                            m_Mem.szShortName = NULL;
129.                            if (_LastError != ERR_SUCCESS) return;
130.                            m_Mem.CrossHair = (bool)ChartGetInteger(GetInfoTerminal().ID, CHART_CROSSHAIR_TOOL);
131.                            ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_MOUSE_MOVE, true);
132.                            ChartSetInteger(GetInfoTerminal().ID, CHART_CROSSHAIR_TOOL, false);
133.                            ZeroMemory(m_Info);
134.                            m_Info.corLineH  = corH;
135.                            m_Info.corTrendP = corP;
136.                            m_Info.corTrendN = corN;
137.                            m_Info.Study = eStudyNull;
138.                            if (m_Mem.IsFull = (corP != clrNONE) && (corH != clrNONE) && (corN != clrNONE))
139.                                    CreateObjectGraphics(def_NameObjectLineH, OBJ_HLINE, m_Info.corLineH);
140.                    }
141. //+------------------------------------------------------------------+
142.            ~C_Mouse()
143.                    {
144.                            ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, 0, false);
145.                            ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_MOUSE_MOVE, false);
146.                            ChartSetInteger(GetInfoTerminal().ID, CHART_CROSSHAIR_TOOL, m_Mem.CrossHair);
147.                            ObjectsDeleteAll(GetInfoTerminal().ID, def_MousePrefixName);
148.                    }
149. //+------------------------------------------------------------------+
150. inline bool CheckClick(const eBtnMouse value) 
151.                    {
152.                            return (GetInfoMouse().ButtonStatus & value) == value;
153.                    }
154. //+------------------------------------------------------------------+
155. inline const st_Mouse GetInfoMouse(void)
156.                    {
157.                            if (m_Mem.szShortName != NULL)
158.                            {
159.                                    double Buff[];
160.                                    uCast_Double loc;
161.                                    int handle = ChartIndicatorGet(GetInfoTerminal().ID, 0, m_Mem.szShortName);
162.                                    
163.                                    ZeroMemory(m_Info.Data);
164.                                    if (CopyBuffer(handle, 0, 0, 4, Buff) == 4)
165.                                    {
166.                                            m_Info.Data.Position.Price = Buff[0];
167.                                            loc.dValue = Buff[1];
168.                                            m_Info.Data.Position.dt = loc._datetime;
169.                                            loc.dValue = Buff[2];
170.                                            m_Info.Data.Position.X = loc._int[0];
171.                                            m_Info.Data.Position.Y = loc._int[1];
172.                                            loc.dValue = Buff[3];
173.                                            m_Info.Data.ButtonStatus = loc._char[0];
174.                                            IndicatorRelease(handle);
175.                                    }
176.                            }
177. 
178.                            return m_Info.Data;
179.                    }
180. //+------------------------------------------------------------------+
181. virtual void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
182.                    {
183.                            int w = 0;
184.                            static double memPrice = 0;
185.                            
186.                            if (m_Mem.szShortName == NULL) switch (id)
187.                            {
188.                                    case (CHARTEVENT_CUSTOM + ev_HideMouse):
189.                                            if (m_Mem.IsFull) ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectLineH, OBJPROP_COLOR, clrNONE);
190.                                            break;
191.                                    case (CHARTEVENT_CUSTOM + ev_ShowMouse):
192.                                            if (m_Mem.IsFull) ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectLineH, OBJPROP_COLOR, m_Info.corLineH);
193.                                            break;
194.                                    case CHARTEVENT_MOUSE_MOVE:
195.                                            ChartXYToTimePrice(GetInfoTerminal().ID, m_Info.Data.Position.X = (int)lparam, m_Info.Data.Position.Y = (int)dparam, w, m_Info.Data.Position.dt, m_Info.Data.Position.Price);
196.                                            if (m_Mem.IsFull) ObjectMove(GetInfoTerminal().ID, def_NameObjectLineH, 0, 0, m_Info.Data.Position.Price = AdjustPrice(m_Info.Data.Position.Price));
197.                                            m_Info.Data.Position.dt = AdjustTime(m_Info.Data.Position.dt);
198.                                            ChartTimePriceToXY(GetInfoTerminal().ID, w, m_Info.Data.Position.dt, m_Info.Data.Position.Price, m_Info.Data.Position.X, m_Info.Data.Position.Y);
199.                                            if ((m_Info.Study != eStudyNull) && (m_Mem.IsFull)) ObjectMove(GetInfoTerminal().ID, def_NameObjectLineV, 0, m_Info.Data.Position.dt, 0);
200.                                            m_Info.Data.ButtonStatus = (uint) sparam;
201.                                            if (CheckClick(eClickMiddle))
202.                                                    if ((!m_Mem.IsFull) || ((color)ObjectGetInteger(GetInfoTerminal().ID, def_NameObjectLineH, OBJPROP_COLOR) != clrNONE)) CreateStudy();
203.                                            if (CheckClick(eClickLeft) && (m_Info.Study == eStudyCreate))
204.                                            {
205.                                                    ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, false);
206.                                                    if (m_Mem.IsFull) ObjectMove(GetInfoTerminal().ID, def_NameObjectLineT, 0, m_Mem.dt = GetInfoMouse().Position.dt, memPrice = GetInfoMouse().Position.Price);
207.                                                    m_Info.Study = eStudyExecute;
208.                                            }
209.                                            if (m_Info.Study == eStudyExecute) ExecuteStudy(memPrice);
210.                                            m_Info.Data.ExecStudy = m_Info.Study == eStudyExecute;
211.                                            break;
212.                                    case CHARTEVENT_OBJECT_DELETE:
213.                                            if ((m_Mem.IsFull) && (sparam == def_NameObjectLineH)) CreateObjectGraphics(def_NameObjectLineH, OBJ_HLINE, m_Info.corLineH);
214.                                            break;
215.                            }
216.                    }
217. //+------------------------------------------------------------------+
218. };
219. //+------------------------------------------------------------------+
220. #undef def_MousePrefixName
221. #undef def_NameObjectLineV
222. #undef def_NameObjectLineH
223. #undef def_NameObjectLineT
224. #undef def_NameObjectStudy
225. //+------------------------------------------------------------------+

Quellcode der Klasse C_Mouse

Wie zuvor besteht der Hauptunterschied zwischen diesem Code und dem alten darin, dass für den Zugriff auf die Klasse C_Terminal kein Zeiger mehr verwendet wird. Die Verantwortung für dieses Verfahren ist in Zeile 13 zu sehen, wo wir die Klasse C_Mouse von der Klasse C_Terminal ableiten. Somit erweitern nun alle Prozeduren und öffentlichen Funktionen der Klasse C_Terminal die Klasse C_Mouse. Der gesamte Code ist größtenteils bereits in den Artikeln zum Mausindikator erklärt. 

Der letzte Artikel zu diesem Thema war Entwicklung eines Replay Systems (Teil 31): Expert Advisor Projekt — Die Klasse C_Mouse (V), Sie können diesen Artikel verwenden, um zum ersten Artikel über diesen Mausindikator zu springen, da es einen Link am Anfang des Artikels gibt, wie in allen meinen Artikeln, sodass Sie zurückgehen und die Entwicklung des Systems verfolgen können.

Wenn Sie Zweifel an der Funktionsweise des Mausindikators haben oder ihn an Ihre persönlichen Bedürfnisse anpassen möchten, können Sie die Artikel lesen, in denen erklärt wird, wie der Indikator entwickelt wurde. Versuchen Sie zu verstehen, wie es funktioniert, und kehren Sie dann an diese Stelle zurück, um die gezeigten Änderungen vorzunehmen. Sie können auch andere Dinge, die Sie brauchen, im Mauszeiger unterbringen. Wenn Sie alles richtig machen, können Sie Ihren eigenen Mauszeiger in diesem Replay/Simulator-System verwenden.

Hier ein Tipp für alle, die wirklich programmieren lernen wollen: Versuchen Sie, dem Mauszeiger neue Funktionen hinzuzufügen und sie im Wiedergabe-/Simulatorsystem zu verwenden. Vergessen Sie aber nicht, die Quelle Ihres Wissens anzugeben, was mir große Freude bereiten wird. Es motiviert mich nicht nur, sondern es macht mir auch Spaß zu zeigen, wie ich Lösungen für Probleme finde, die viele Menschen für unüberwindbar halten.

Aber kommen wir zurück zur Erklärung des Codes. Im Konstruktor der Klasse C_Mouse können Sie sehen, dass wir in Zeile 125 die Klasse C_Terminal initialisieren. Der bei der Initialisierung zu verwendende Wert wird im Indikatorcode in der Eingabe namens user00 angegeben. Lässt der Nutzer diese Eingabe unangetastet, ermittelt die Klasse C_Terminal die Chart-ID und schaltet bei Bedarf auf die Verwendung dieser ID um, um Objekte auf dem gewünschten Chart zu platzieren.

Außerdem wird der Indikator in Zeile 127 gesperrt, sodass er nur einmal im Chart platziert werden kann. Dadurch wird verhindert, dass der Nutzer einen anderen Mausindikator auf dem Chart platziert. Der Indikator wird in der Liste der MetaTrader 5 Indikatoren erscheinen.

Der Rest des Codes funktioniert genauso wie zuvor. Dieser Mausindikator ist dafür verantwortlich, dass MetaTrader 5 Mausereignisse empfängt und sie an das Chart sendet, in dem sich der Indikator befindet. Bitte beachten Sie, dass sich der Mausindikator im Chart befinden muss, um Mausereignisse in MetaTrader 5 zu empfangen; der Versuch, von einem anderen Chart aus auf den Indikator zuzugreifen, macht keinen Sinn. Selbst wenn wir die Möglichkeit haben, zu lesen, was ein Mausindikator auf einem anderen Chart macht, wird MetaTrader 5 keine Mausereignisse für ein Chart erzeugen, das keinen solchen Indikator enthält.

Hierfür ist die Zeile 131 zuständig. Auf diese Weise erhält jeder Expert Advisor oder andere Indikator Mausereignisse, solange sich der Indikator auf dem Chart befindet. Merken Sie sich diese Tatsache, denn die folgenden Codes setzen voraus, dass der Mausindikator im Chart vorhanden ist, damit die Mausereignisse von MetaTrader 5 an andere Codes weitergegeben werden, die sie auf eine bestimmte Weise behandeln.

Bevor ich zur nächsten Phase übergehe, in der wir den Kontrollindikator ändern werden, halte ich es für notwendig, genauer zu erklären, was eigentlich passiert. Auf diese Weise können Sie den gesamten Inhalt dieses Artikels richtig verstehen.


Verstehen und Verinnerlichen der Kenntnisse

Sie denken vielleicht, dass alles, worüber ich in diesem Artikel spreche, Unsinn ist, denn viele von Ihnen haben sicher viel Erfahrung mit MQL5 oder kennen jemanden, der ein Experte für dieses Kodierungssystem ist. Aber um alles klar zu machen, werde ich die ausführbare Datei des Mausindikators an diesen Artikel anhängen. Dies geschieht der Einfachheit halber. Sie können jedoch die Materialien in dieser Serie verwenden und erhalten das gleiche Ergebnis, da ich den vollständigen Code in die Artikel einfüge. Sie können es ausprobieren und sehen, was passiert, indem Sie den folgenden Code verwenden, der viel einfacher ist als das Replay/Simulator-System, aber nach den gleichen Prinzipien funktioniert.

01. //+------------------------------------------------------------------+
02. #property service
03. #property copyright "Daniel Jose"
04. #property version   "1.00"
05. //+------------------------------------------------------------------+
06. input string user00 = "EURUSD"; // Symbol
07. //+------------------------------------------------------------------+
08. void OnStart()
09. {
10.    long id;
11.    int handle;
12.    
13.    SymbolSelect(user00, true);
14.    id = ChartOpen(user00, PERIOD_M5);
15.    handle = iCustom(ChartSymbol(id), ChartPeriod(id), "\\Indicators\\Replay\\Mouse Study.ex5", id);
16.    ChartIndicatorAdd(id, 0, handle);
17.    IndicatorRelease(handle);
18. 
19.    Print("ID: ", id);
20.     
21.    while ((!_StopFlag) && (ChartSymbol(id) == user00)) Sleep(300);
22.     
23.    ChartClose(id);
24.    SymbolSelect(user00, false);    
25. }
26. //+------------------------------------------------------------------+

Quellcode des Testdienstes

In Zeile 06 geben wir dem Nutzer die Möglichkeit, das Asset zu spezifizieren. Sie kann zum Öffnen eines Charts verwendet werden, aber das Symbol muss sich im Fenster Market Watch befinden. Hierfür verwenden wir Zeile 13. In Zeile 14 bitten wir MetaTrader 5, einen Chart für uns zu öffnen. In Zeile 15 erstellen wir ein Handle, das den Indikator zum Chart hinzufügt; in diesem Fall zwingen wir MetaTrader 5, den Mausindikator zu laden. In Zeile 16 fügen wir den Indikator in das Chart ein. In Zeile 17 geben wir das Handle frei, da wir ihn nicht mehr brauchen. In Zeile 19 wird eine Meldung an das Terminal ausgegeben, die die ID des vom Dienst geöffneten Charts angibt. In Zeile 21 warten wir nun darauf, dass der Nutzer das Chart schließt oder den Dienst beendet.

Wenn der Nutzer das Chart schließt, bewirkt Zeile 23 nichts. Beendet der Nutzer jedoch den Dienst, wird in Zeile 23 das zuvor geöffnete Chart geschlossen. In Zeile 24 wird das Symbol aus dem Fenster der Marktübersicht entfernt. Ein weiterer Punkt: Damit ein Symbol entfernt werden kann, darf es keine Elemente haben, die mit ihm verbunden sind. In diesem Fall verwenden wir das Chart nur zu Testzwecken, es wird also nichts mit ihm verbunden sein.

Man könnte meinen, dass dieser Code nicht viel Sinn macht. Warum sollte man sich die Mühe machen, so etwas zu entwickeln und zu erklären, wie es funktioniert? Das ist genau das, was jeder tun sollte: sich bemühen, über die Grenzen hinauszugehen, mit denen viele vertraut sind. Nur dann werden Sie wirklich verstehen, wie alles funktioniert.

Ich möchte, dass Sie die Idee und das Wesentliche verstehen, da dies für die folgenden Artikel von großer Bedeutung ist. Sie können sich das Video 01 ansehen und die Tests verfolgen. Sie können auch versuchen, es selbst anhand Ihrer eigenen Kriterien zu testen. Ich möchte das betonen: Akzeptieren Sie nicht die „Wahrheit“ von jemandem, nur weil er Ihnen besser oder kompetenter erscheint. Prüfen Sie, stellen Sie Autoritäten in Frage, und nur dann wird wahres Wissen deutlich werden.


Video 01


Schlussfolgerung

Dieser Artikel war einer der schwierigsten bis jetzt. Dies ist auf die Komplexität der dargestellten Informationen zurückzuführen. Obwohl mir die Programmierung selbst recht einfach erscheint, ist es sehr schwierig zu erklären, was passiert. Der Grund dafür ist, dass es nicht nur von Leuten mit einem viel höheren Wissensstand gelesen werden kann, sondern auch von Enthusiasten, die ihre ersten Versuche unternehmen, diese PROGRAMMIERWELT zu verstehen.

Im nächsten Artikel werde ich zeigen, wie man Interaktionen zwischen dem Mausindikator, dem Kontrollindikator, dem Wiedergabe-/Simulatorsystem und Ihnen, liebe Nutzer des Systems, herstellt. Bis bald

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

Beigefügte Dateien |
Anexo.zip (420.65 KB)
Neuronale Netze leicht gemacht (Teil 92): Adaptive Vorhersage im Frequenz- und Zeitbereich Neuronale Netze leicht gemacht (Teil 92): Adaptive Vorhersage im Frequenz- und Zeitbereich
Die Autoren der FreDF-Methode haben den Vorteil der kombinierten Vorhersage im Frequenz- und Zeitbereich experimentell bestätigt. Die Verwendung von gewichteten Hyperparameter ist jedoch für nicht-stationäre Zeitreihen nicht optimal. In diesem Artikel werden wir uns mit der Methode der adaptiven Kombination von Vorhersagen im Frequenz- und Zeitbereich vertraut machen.
Neuronale Netze leicht gemacht (Teil 91): Vorhersage durch Frequenzbereiche (Frequency Domain Forecasting, FreDF) Neuronale Netze leicht gemacht (Teil 91): Vorhersage durch Frequenzbereiche (Frequency Domain Forecasting, FreDF)
Wir fahren fort mit der Analyse und Vorhersage von Zeitreihen im Frequenzbereich. In diesem Artikel machen wir uns mit einer neuen Methode zur Vorhersage von Daten im Frequenzbereich vertraut, die zu vielen der bisher untersuchten Algorithmen hinzugefügt werden kann.
Wichtigste Änderungen des Algorithmus für die künstliche kooperative Suche (ACSm) Wichtigste Änderungen des Algorithmus für die künstliche kooperative Suche (ACSm)
Hier werden wir die Entwicklung des ACS-Algorithmus betrachten: drei Änderungen zur Verbesserung der Konvergenzeigenschaften und der Effizienz des Algorithmus. Umwandlung eines der führenden Optimierungsalgorithmen. Von Matrixmodifikationen bis hin zu revolutionären Ansätzen zur Bevölkerungsbildung.
PSAR, Heiken Ashi und Deep Learning gemeinsam für den Handel nutzen PSAR, Heiken Ashi und Deep Learning gemeinsam für den Handel nutzen
Dieses Projekt erforscht die Verschmelzung von Deep Learning und technischer Analyse, um Handelsstrategien im Forex-Bereich zu testen. Für schnelle Experimente wird ein Python-Skript verwendet, das ein ONNX-Modell neben traditionellen Indikatoren wie PSAR, SMA und RSI einsetzt, um die Entwicklung des EUR/USD vorherzusagen. Ein MetaTrader 5-Skript bringt diese Strategie dann in eine Live-Umgebung und nutzt historische Daten und technische Analysen, um fundierte Handelsentscheidungen zu treffen. Die Backtesting-Ergebnisse deuten auf einen vorsichtigen, aber konsequenten Ansatz hin, bei dem der Schwerpunkt eher auf Risikomanagement und stetigem Wachstum als auf aggressivem Gewinnstreben liegt.