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

Entwicklung eins Replay Systems (Teil 49): Die Dinge werden kompliziert (I)

MetaTrader 5Beispiele | 4 November 2024, 14:52
99 0
Daniel Jose
Daniel Jose

Einführung

In diesem Artikel wird das verwendet, was in dem Artikel „ Entwicklung eines Replay Systems (Teil 48) Das Konzept eines Dienstes verstehen“ besprochen wurde. Wenn Sie ihn also noch nicht gelesen haben, tun Sie es bitte, denn der Inhalt dieses Artikels ist sehr wichtig, um zu verstehen, was wir hier tun werden.

Eines der Dinge, die mich beim Schreiben der vorangegangenen Artikel am meisten gestört haben, war, dass das Replay/Simulator-System einen Indikator enthielt, der für den MetaTrader 5-Nutzer in dem Bereich sichtbar war, in dem die Indikatoren aufgelistet sind, und von dem aus er auf dem Chart platziert werden konnte.

Obwohl die Artikel über eine Sperrfunktion verfügten, die verhinderte, dass ein Nutzer versuchte, einen solchen Indikator auf dem falschen Chart zu platzieren, d. h. auf einem anderen als dem vom Replay-/Simulationsdienst verwendeten Symbol, war allein das Vorhandensein dieses Indikators in dieser Liste unter den anderen für mich sehr beunruhigend.

Während all dieser Monate habe ich versucht zu analysieren, wie alles am besten organisiert werden kann. Glücklicherweise ist es mir kürzlich gelungen, eine Lösung zu finden, die die Situation verbessert. In diesem Fall wird der Kontrollindikator nicht mehr zwischen anderen Indikatoren angezeigt, sondern wird zu einem festen Bestandteil des Replay-/Simulationsdienstes.

Auf diese Weise erhalten wir einen größeren Freiheitsgrad bei einigen Faktoren. Ich werde jedoch nach und nach Änderungen vornehmen, da ich auch den Indikator verfeinern werde, um die Belastung des MetaTrader 5 zu verringern. Mit anderen Worten, wir hören auf, einige Ressourcen zu nutzen, und fangen an, andere Plattformfunktionen zu nutzen. Dies wird die Stabilität, Sicherheit und Zuverlässigkeit des Replay/Simulator-Systems verbessern.

Mal sehen, wie das umgesetzt wird, denn die Änderungen werden sehr interessant sein und uns viel Wissen darüber vermitteln, wie wir professioneller mit MQL5 arbeiten können. Auf jeden Fall bedeutet dies keine Verzögerung bei der Entwicklung unseres Replay/Simulator-Systems. In der Tat braucht das System einige Dinge, einschließlich derer, die wir in diesem Artikel besprechen werden, um in Zukunft etwas anderes zu programmieren.

Lassen Sie uns also unsere epische Reise fortsetzen, um einen fortschrittlicheren Replay/Simulator-Dienst zu implementieren.


Beginn der Änderungen

Um deutlich zu machen, was getan wird, werde ich schrittweise vorgehen, damit Sie, liebe Leserin, lieber Leser, die Veränderungen mitverfolgen können. Viele mögen die Methode, die ich zur Demonstration gewählt habe, für übertrieben halten und denken, dass man sofort zum endgültigen Code übergehen könnte und das war's. Aber wenn das genau das war, was gebraucht wurde, warum gibt es dann all die vorher geschriebenen Artikel? Das ergibt doch keinen Sinn, oder? Da dieses Material aber dazu dienen soll, Menschen zu motivieren und zu ermutigen, ihre eigenen Programme und Lösungen zu erstellen, muss ich zeigen, wie man bestehenden Code bereinigt und ihn für die Verwendung in einem komplexeren Modell geeignet macht. Außerdem sollte der Code nicht zu etwas Seltsamem und Ineffizientem werden.

Wir sollten also nichts überstürzen, sondern weitermachen und dabei immer an die denken, die weniger Erfahrung haben.

Zunächst wird das System der Chart-ID-Bindung entfernt. Dieses System verhindert, dass der Kontrollindikator auf mehr als einem Chart platziert werden kann. Gleichzeitig wird verhindert, dass der Indikator auf dem falschen Chart platziert wird. Obwohl wir dieses System jetzt entfernen, wird es später wieder in Betrieb genommen werden. In diesem Fall verbleibt der Indikator jedoch im richtigen Chart, und der Replay-/Simulationsdienst führt die Operation durch, nicht der Indikator selbst.

Um sicherzustellen, dass diese Entfernung korrekt und sicher durchgeführt wird, werden wir immer denselben Interaktionspunkt zwischen dem Dienst und dem Kontrollanzeiger verwenden. Dieser allgemeine Code ist im Folgenden dargestellt:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_SymbolReplay               "RePlay"
05. #define def_GlobalVariableReplay       def_SymbolReplay + "_Infos"
06. #define def_GlobalVariableIdGraphics   def_SymbolReplay + "_ID"
07. #define def_GlobalVariableServerTime   def_SymbolReplay + "_Time"
08. #define def_MaxPosSlider               400
09. //+------------------------------------------------------------------+
10. union u_Interprocess
11. {
12.     union u_0
13.     {
14.             double  df_Value;       // Value of the terminal global variable...
15.             ulong   IdGraphic;      // Contains the Graph ID of the asset...
16.     }u_Value;
17.     struct st_0
18.     {
19.             bool    isPlay;         // Indicates whether we are in Play or Pause mode...
20.             bool    isWait;         // Tells the user to wait...
21.             bool    isHedging;      // If true we are in a Hedging account, if false the account is Netting...
22.             bool    isSync;         // If true indicates that the service is synchronized...
23.             ushort  iPosShift;      // Value between 0 and 400...
24.     }s_Infos;
25.     datetime        ServerTime;
26. };
27. //+------------------------------------------------------------------+
28. union uCast_Double
29. {
30.     double   dValue;
31.     long     _long;                  // 1 Information
32.     datetime _datetime;              // 1 Information
33.     int      _int[sizeof(double)];   // 2 Informations
34.     char     _char[sizeof(double)];  // 8 Informations
35. };
36. //+------------------------------------------------------------------+

Quellcode in Interprocess.mqh

Zeile 06 enthält eine Definition, die sowohl dem Dienst als auch dem Indikator mitteilt, welcher globale Terminalvariablenname zur Übergabe der Chart-ID verwendet wird. Wenn wir diese Zeile entfernen, erhalten wir eine Reihe von Fehlern, wenn wir versuchen, den Indikator und den Dienst zu kompilieren. Das ist genau das, was wir brauchen. Jeder Fehler, der bei der Kompilierung auftritt, weist auf eine Stelle hin, an der wir eingreifen müssen, um diese Verbindung, die zwischen dem Dienst und dem Indikator über die globale Terminalvariable besteht, zu beseitigen.

Damit dies nicht zu mühsam und repetitiv wird, werde ich es in Codefragmenten darstellen. Ich werde immer angeben, wo und wie genau Sie im Quellcode handeln müssen, damit das System weiterhin funktioniert.

Beginnen wir also mit den folgenden Überlegungen:

01. //+------------------------------------------------------------------+
02. #property service
03. #property icon "/Images/Market Replay/Icons/Replay - Device.ico"
04. #property copyright "Daniel Jose"
05. #property version   "1.49"
06. #property description "Replay-Simulator service for MT5 platform."
07. #property description "This is dependent on the Market Replay indicator."
08. #property description "For more details on this version see the article."
09. #property link "https://www.mql5.com/ru/articles/11820"
10. //+------------------------------------------------------------------+
11. #define def_Dependence_01   "Indicators\\Replay\\Market Replay.ex5"
12. #resource "\\" + def_Dependence_01
13. //+------------------------------------------------------------------+
14. #include <Market Replay\Service Graphics\C_Replay.mqh>
15. //+------------------------------------------------------------------+
16. input string           user00 = "Forex - EURUSD.txt";  //Replay configuration file.
17. input ENUM_TIMEFRAMES  user01 = PERIOD_M5;             //Initial graphic time.
18. //+------------------------------------------------------------------+
19. void OnStart()
20. {
21.     C_Replay  *pReplay;
22. 
23.     pReplay = new C_Replay(user00);
24.     if ((*pReplay).ViewReplay(user01))
25.     {
26.             Print("Permission granted. Replay service can now be used...");
27.             while ((*pReplay).LoopEventOnTime(false));
28.     }
29.     delete pReplay;
30. }
31. //+------------------------------------------------------------------+

Quellcode: replay.mq5 Dienst

Im obigen Code, dem eigentlichen Dienstcode, sehen Sie, dass in den Zeilen 11 und 12 einige Unterschiede zu den vorherigen Codes bestehen. Auch wenn der Unterschied gering ist, wird er zu dem beitragen, was wir erreichen wollen. Wir werden später darauf zurückkommen. Hier stellt sich die Frage, ob der Kontrollindikator vor kurzem kompiliert wurde oder nicht, nachdem wir den Header-Code geändert haben, indem wir die Definition des Namens der globalen Terminalvariablen entfernt haben.

Nun, zum Zeitpunkt der Erstellung dieses Artikels haben wir noch kein MAKE-Kompilierungssystem in MetaEditor. Aber wer weiß, vielleicht werden diejenigen, die die MQL-Plattform und -Sprache entwickeln, in Zukunft diese Möglichkeit nutzen. Aber bis dahin müssen wir bestimmte Vorsichtsmaßnahmen treffen.

Bevor ich die Erklärung fortsetze, möchte ich einen Moment innehalten und erklären, was das MAKE-Kompilierungssystem ist. Für viele wird dies etwas Neues sein, aber für diejenigen, die seit vielen Jahren professionell mit der Programmierung zu tun haben, ist dieses System sehr vertraut. Es funktioniert folgendermaßen: Sie erstellen Ihren Quellcode auf die übliche Weise, aber manchmal müssen Sie mehrere ausführbare Dateien auf einmal erstellen. Es kann auch verwendet werden, um eine einzelne ausführbare Datei zu erstellen, obwohl dies nicht unbedingt mit MAKE geschehen muss.

Bevor Sie also etwas kompilieren, erstellen Sie eine weitere Datei. Dies ist eine MakeFile-Datei, die Schritte, Definitionen, Konfigurationen und Einstellungen enthält, die erforderlich sind, damit der Compiler und LinkEditor alle ausführbaren Dateien in einem Durchgang erzeugen können. Der große Vorteil dieses Systems ist, dass MAKE jede Änderung einer Datei, sei es ein Header oder eine Bibliothek, erkennt und nur die Dateien kompiliert, die wirklich benötigt werden. Auf diese Weise können Sie alle ausführbaren Dateien nach Bedarf aktualisieren.

Der Entwickler muss sich also nicht darum kümmern, was er bei der Erstellung der endgültigen ausführbaren Datei aktualisieren muss, da dies von MAKE erledigt wird. Auf diese Weise vermeiden Sie das Risiko, etwas zu erstellen oder einen Fehler zu beheben, es in einer ausführbaren Datei anzuwenden und in einer anderen zu überspringen. MAKE kann also die ganze Arbeit für uns erledigen.

Zum Zeitpunkt der Abfassung dieses Artikels gibt es ein solches Werkzeug in MetaEditor jedoch noch nicht. Es ist möglich, etwas Ähnliches mit Hilfe von Batchdateien von der Befehlszeile aus zu erstellen, aber das wäre nur eine vorübergehende Lösung und nicht ideal. Ich werde also nicht näher darauf eingehen, wie das gemacht wird. Da aber Zeile 12 des obigen Codes dem MQL5-Compiler mitteilt, dass die ausführbare Datei für den Kontrollindikator zum Dienst hinzugefügt werden soll, können Sie ihn einfach bitten, den Dienstcode zu kompilieren, und der Indikatorcode wird mitkompiliert. Ich habe dies bereits erwähnt, möchte es aber hier noch einmal betonen: Damit der Indikator korrekt kompiliert werden kann, müssen Sie jedes Mal, wenn Sie den Servicecode kompilieren, die ausführbare Datei des Indikators löschen. Andernfalls wird der Indikatorcode nicht neu kompiliert, wenn der Dienstcode kompiliert wird. Bitte beachten Sie dies.

Wenn Sie versuchen, den Code des Diensts zu kompilieren, erhalten Sie das folgende Ergebnis:

Abbildung 01

Abbildung 01. Ergebnis des Versuchs, neuen Code zu kompilieren

Sie werden feststellen, dass wir 2 Fehler und eine Warnung erhalten. Die Warnung ist nicht Ihre erste Sorge und sollte es auch nicht sein. Zunächst müssen die Fehler korrigiert werden. In der Abbildung 01 können Sie sehen, wo die Fehler liegen. Klicken Sie sie an, um den genauen Ort des Geschehens zu erfahren.

Der in Abbildung 01 gezeigte Fehler in Zeile 12 trat auf, weil der Compiler keine ausführbare Datei für die Dienstressource finden konnte.

Der in Zeile 156 gemeldete Fehler ist Teil des Dienstcodes und muss behoben werden, damit die Kompilierung fortgesetzt werden kann.

Im Folgenden finden Sie den Code für diesen Fehler: 

141. ~C_Replay()
142.    {
143.            ArrayFree(m_Ticks.Info);
144.            ArrayFree(m_Ticks.Rate);
145.            m_IdReplay = ChartFirst();
146.            do
147.            {
148.                    if (ChartSymbol(m_IdReplay) == def_SymbolReplay)
149.                            ChartClose(m_IdReplay);
150.            }while ((m_IdReplay = ChartNext(m_IdReplay)) > 0);
151.            for (int c0 = 0; (c0 < 2) && (!SymbolSelect(def_SymbolReplay, false)); c0++);
152.            CustomRatesDelete(def_SymbolReplay, 0, LONG_MAX);
153.            CustomTicksDelete(def_SymbolReplay, 0, LONG_MAX);
154.            CustomSymbolDelete(def_SymbolReplay);
155.            GlobalVariableDel(def_GlobalVariableReplay);
156.            GlobalVariableDel(def_GlobalVariableIdGraphics);
157.            GlobalVariableDel(def_GlobalVariableServerTime);
158.            Print("Finished replay service...");
159.    }

Code aus C_Replay.mqh

Sie können die Zeile 156 auskommentieren oder einfach aus dem Code entfernen. Da die Änderung dauerhaft sein wird, sollten wir diese Zeile jetzt entfernen. Auf diese Weise wird der Dienst diese globale Terminalvariable nicht mehr sehen. Wenn wir jedoch nach der Korrektur im Dienst versuchen, den Code neu zu kompilieren, erhalten wir das in Abbildung 02 dargestellte Ergebnis.

Abbildung 02

Abbildung 02. Neuer Kompilationsversuch

Jetzt wird nur noch ein Fehler angezeigt, und es gibt keine Warnungen mehr. An diesem Punkt fragen sich viele weniger erfahrene Leser, wie sie diesen angezeigten Fehler beheben können. Sie vergessen jedoch, die Compiler-Meldung zu lesen.

Siehe Abbildung 02. Direkt über der Fehlermeldung werden einige weitere Informationen angezeigt. Ignorieren Sie NIEMALS, was der Compiler Ihnen sagt. Sie sollten auf absolut ALLES achten. Die Meldung, die der Fehleranzeige unmittelbar vorausgeht, enthält die folgenden Informationen des Compilers:

compiling '\Indicators\Replay\Market Replay.mq5' failed (Kompilierung ist fehlgeschlagen)

Diese Meldung ist wichtig, da sie uns mitteilt, dass der Compiler aus irgendeinem Grund keine ausführbare Indikator-Datei erstellen kann. Da der Dienstcode teilweise kompiliert ist, müssen wir uns auf den Indikatorcode konzentrieren. Wir öffnen den gegebenen Code in MetaEditor und bitten den Compiler, zu versuchen, eine ausführbare Datei zu erzeugen. Und bevor jemand denkt: „Aber warum machen wir das, wenn wir wissen, dass der Code Fehler hat?“, ja, wir wissen, dass sie da sind, aber wir wollen, dass der Compiler uns sagt, wo genau. Die manuelle Suche nach Fehlern ist unproduktiv. Der Compiler soll sie uns anzeigen.

Wenn Sie also den Indikatorcode öffnen und den Compiler auffordern, eine ausführbare Datei zu erzeugen, sehen Sie Abbildung 03.

Abbildung 03

Abbildung 03. Versuch, den Kontrollindikator zu kompilieren

Dieses Mal werden fünf Fehler und fünf Warnungen angezeigt. Behandeln Sie zuerst Fehler, dann Warnungen. Wenn wir das erste Mal auf die erste Fehlermeldung klicken, werden wir zu dem Code weitergeleitet, der sich genau an der Stelle des Fehlers befindet. Ein wichtiger Punkt: Viele Menschen denken, dass sie Fehler willkürlich anklicken können. Nach den Regeln sollten Sie jedoch wie folgt vorgehen: Suchen Sie den ersten Fehler in der Liste. Achten Sie nicht auf den Rest, sondern beginnen Sie immer mit dem ersten. Nehmen Sie die erforderlichen Korrekturen vor und versuchen Sie erneut, den Code zu kompilieren. Wiederholen Sie diesen Vorgang, bis der Code ohne Fehler oder Warnungen kompiliert werden kann. Sehr oft, vor allem bei Anfängern, tritt Panik auf, wenn sie 100 oder mehr Kompilierungsfehler sehen.

In vielen Fällen lässt sich jedoch nach Behebung des ersten Fehlers der gesamte Code ohne Probleme kompilieren. Befolgen Sie also diesen Rat: Suchen Sie nach dem ersten Fehler, auf den der Compiler hinweist. Sehen Sie nach, wie Sie den Fehler beheben können, und versuchen Sie, den Code erneut zu kompilieren. Wenn ein neuer Fehler auftaucht, gehen Sie zum ersten aufgelisteten Fehler und beheben Sie ihn, und so weiter, bis keine Fehler mehr vorhanden sind. Das Gleiche sollte im Falle von Warnungen geschehen. Beginnen Sie immer mit dem Ersten auf der Liste.

Da sich alle vom Compiler gemeldeten Fehler (siehe Abbildung 03) im selben Code befinden, ist es einfacher, den gesamten Code anzuzeigen, damit Sie wissen, wo die Änderungen vorgenommen werden. 

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.49"
07. #property link "https://www.mql5.com/ru/articles/11820"
08. #property indicator_chart_window
09. #property indicator_plots 0
10. //+------------------------------------------------------------------+
11. #include <Market Replay\Service Graphics\C_Controls.mqh>
12. //+------------------------------------------------------------------+
13. #define def_BitShift ((sizeof(ulong) * 8) - 1)
14. //+------------------------------------------------------------------+
15. C_Terminal *terminal = NULL;
16. C_Controls *control = NULL;
17. //+------------------------------------------------------------------+
18. #define def_InfoTerminal (*terminal).GetInfoTerminal()
19. #define def_ShortName       "Market_" + def_SymbolReplay
20. //+------------------------------------------------------------------+
21. int OnInit()
22. {
23. #define macro_INIT_FAILED { ChartIndicatorDelete(def_InfoTerminal.ID, 0, def_ShortName); return INIT_FAILED; }
24.     u_Interprocess Info;
25.     ulong ul = 1;
26. 
27.     ResetLastError();
28.     if (CheckPointer(control = new C_Controls(terminal = new C_Terminal())) == POINTER_INVALID) return INIT_FAILED;
29.     if (_LastError != ERR_SUCCESS) return INIT_FAILED;
30.     ul <<= def_BitShift;
31.     IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName);
32.     if ((def_InfoTerminal.szSymbol != def_SymbolReplay) || (!GlobalVariableCheck(def_GlobalVariableIdGraphics))) macro_INIT_FAILED;
33.     Info.u_Value.df_Value = GlobalVariableGet(def_GlobalVariableIdGraphics);
34.     if (Info.u_Value.IdGraphic != def_InfoTerminal.ID) macro_INIT_FAILED;
35.     if ((Info.u_Value.IdGraphic >> def_BitShift) == 1) macro_INIT_FAILED;
36.     IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName + "Device");
37.     Info.u_Value.IdGraphic |= ul;
38.     GlobalVariableSet(def_GlobalVariableIdGraphics, Info.u_Value.df_Value);
39.     if (GlobalVariableCheck(def_GlobalVariableReplay)) Info.u_Value.df_Value = GlobalVariableGet(def_GlobalVariableReplay); else Info.u_Value.df_Value = 0;
40.     EventChartCustom(def_InfoTerminal.ID, C_Controls::ev_WaitOff, 1, Info.u_Value.df_Value, "");
41.     (*control).Init(Info.s_Infos.isPlay);
42.         
43.     return INIT_SUCCEEDED;
44.     
45. #undef macro_INIT_FAILED
46. }
47. //+------------------------------------------------------------------+
48. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
49. {
50.     static bool bWait = false;
51.     u_Interprocess Info;
52.     
53.     Info.u_Value.df_Value = GlobalVariableGet(def_GlobalVariableReplay);
54.     if (!bWait)
55.     {
56.             if (Info.s_Infos.isWait)
57.             {
58.                     EventChartCustom(def_InfoTerminal.ID, C_Controls::ev_WaitOn, 1, 0, "");
59.                     bWait = true;
60.             }
61.     }else if (!Info.s_Infos.isWait)
62.     {
63.             EventChartCustom(def_InfoTerminal.ID, C_Controls::ev_WaitOff, 1, Info.u_Value.df_Value, "");
64.             bWait = false;
65.     }
66.     
67.     return rates_total;
68. }
69. //+------------------------------------------------------------------+
70. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
71. {
72.     (*control).DispatchMessage(id, lparam, dparam, sparam);
73. }
74. //+------------------------------------------------------------------+
75. void OnDeinit(const int reason)
76. {
77.     u_Interprocess Info;
78.     ulong ul = 1;
79. 
80.     switch (reason)
81.     {
82.             case REASON_CHARTCHANGE:
83.                     ul <<= def_BitShift;
84.                     Info.u_Value.df_Value = GlobalVariableGet(def_GlobalVariableIdGraphics);
85.                     Info.u_Value.IdGraphic ^= ul;
86.                     GlobalVariableSet(def_GlobalVariableIdGraphics, Info.u_Value.df_Value);
87.                     break;
88.             case REASON_REMOVE:
89.             case REASON_CHARTCLOSE:
90.                     if (def_InfoTerminal.szSymbol != def_SymbolReplay) break;
91.                     GlobalVariableDel(def_GlobalVariableReplay);
92.                     ChartClose(def_InfoTerminal.ID);
93.                     break;
94.     }
95.     delete control;
96.     delete terminal;
97. }
98. //+------------------------------------------------------------------+

Quellcode des Indikators: Market replay.mq5

Alle durchgestrichenen Zeilen des Codes existieren nicht mehr. Wenn Sie also versuchen, erneut zu kompilieren, erhalten Sie die folgende Meldung (siehe Abbildung 04).

Abbildung 04

Abbildung 04

Bitte beachten Sie, dass dieses Abbildung 04 einen Warnhinweis enthält. Es stört den Code nicht wirklich, kann aber lästig sein. Gehen Sie also zu Zeile 77 und löschen Sie sie, und versuchen Sie dann, den Code erneut zu kompilieren. Sie erhalten die in Abbildung 05 dargestellte Meldung, und das ist genau das, was wir brauchen. Anmerkung: Zeile 77 kann nur deshalb entfernt werden, weil der Compiler uns mitgeteilt hat, dass die Variable nicht verwendet wird.

Abbildung 05

Abbildung 05. Kompilierung erfolgreich abgeschlossen

Sehr gut. Unser Code ist teilweise bereinigt worden. Wenn Sie sich jedoch den zu Beginn dieses Themas gezeigten Code der Header-Datei Interprocess.mqh ansehen, werden Sie feststellen, dass es noch einige Dinge im Code gibt, die wir nicht mehr benötigen. Dies liegt daran, dass wir nicht mehr die globale Terminal-Variable verwenden, um die Chart-ID an den Indikator zu übergeben. Daher sollte Zeile 15 der Datei Interprocess.mqh entfernt werden. Aber es stellt sich die Frage: „Warum habe ich diese Zeile nicht früher gelöscht?“ Der Grund dafür ist, dass die Servicedatei mit besonderer Sorgfalt behandelt werden sollte. Aber es gibt noch einen anderen Grund, und um den zu verstehen, sollten wir zum nächsten Thema übergehen.


Radikalisierung der Entscheidungen

Bei der Entwicklung des Replay-/Simulationsdienstes habe ich unter anderem versucht, die Möglichkeit von Nutzereingriffen in Bezug auf die Darstellung von Charts auszuschließen.

Ich habe einen Weg gefunden, dieses Problem zu lösen, indem ich eine globale Terminalvariable hinzufügte, um dem Indikator mitzuteilen, in welchem Chart er vorhanden sein soll. Der Nutzer kann sie nicht auf einem anderen Chart platzieren. In der Tat war diese Lösung angemessen und recht interessant zu implementieren. So etwas können Sie auch verwenden, wenn Sie sicherstellen wollen, dass ein Indikator, ein Skript oder ein EA nicht auf dem Chart platziert wird. Aber das ist nicht das, was uns im Moment interessiert.

In dem Moment, in dem wir einen Dienst nutzen, um zu kontrollieren, was auf dem Chart erscheinen soll und was nicht, wird ein solches System völlig unnötig. Oder besser gesagt: Da der Indikator als Dienstressource vorhanden sein wird und für den Nutzer nicht zugänglich ist, hat es keinen Sinn, diesen Code weiterhin zu unterstützen. Das Sicherheitsniveau ist erheblich gestiegen, und die Zugangskontrolle hat sich völlig verändert.

Da man also vorsichtig sein muss, wenn man eine ID-Variable aus dem System löscht, habe ich sie im vorherigen Thema nicht gelöscht. Und bald werden Sie verstehen, warum. Wäre die Löschung vorzeitig erfolgt, wäre es sehr schwierig geworden, die entsprechenden Änderungen vorzunehmen, und wir hätten viele Fehler gemacht.

Das größte Kapital eines großen Programmierers liegt genau darin: KEINE HAST. Lösen Sie ein Problem nach dem anderen, indem Sie nach und nach die Dinge so korrigieren und verändern, dass alle bestehenden Möglichkeiten des Programms erhalten bleiben und, wenn nötig oder interessant, erweitert und vervielfältigt werden. Daher lautet das Motto: 

Aufteilen und erobern.

Schauen wir uns an, wie der Code bereinigt wird. Der gesamte neue Code von Interprocess.mqh ist unten dargestellt:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_SymbolReplay             "RePlay"
05. #define def_GlobalVariableReplay     def_SymbolReplay + "_Infos"
06. #define def_GlobalVariableServerTime def_SymbolReplay + "_Time"
07. #define def_MaxPosSlider             400
08. //+------------------------------------------------------------------+
09. union u_Interprocess
10. {
11.     double  df_Value; // Value of the terminal global variable...
12.     struct st_0
13.     {
14.             bool    isPlay;     // Indicates whether we are in Play or Pause mode...
15.             bool    isWait;     // Tells the user to wait...
16.             bool    isHedging;  // If true we are in a Hedging account, if false the account is Netting...
17.             bool    isSync;     // If true indicates that the service is synchronized...
18.             ushort  iPosShift;  // Value between 0 and 400...
19.     }s_Infos;
20.     datetime ServerTime;
21. };
22. //+------------------------------------------------------------------+
23. union uCast_Double
24. {
25.     double  dValue;
26.     long     _long;                  // 1 Information
27.     datetime _datetime;              // 1 Information
28.     int      _int[sizeof(double)];   // 2 Informations
29.     char     _char[sizeof(double)];  // 8 Informations
30. };
31. //+------------------------------------------------------------------+

Quellcode: Interprocess.mqh

Beachten Sie, dass dieser Code, obwohl er nicht viel anders aussieht, einen großen Unterschied zu dem System macht, das wir im vorherigen Abschnitt geändert haben. Wenn Sie also versuchen, das System erneut zu kompilieren, sollten Sie nicht überrascht oder erschrocken sein über die Anzahl der auftretenden Fehler. Das Einzige, was wir tun müssen, ist, alles so zu konfigurieren, dass wir die gleiche Leistung wie vorher erreichen. Aber jetzt wird der Kontrollindikator für den Nutzer nicht mehr verfügbar sein. Der Dienst ist für die Platzierung und Unterstützung auf dem richtigen Chart verantwortlich.

Wenn Sie versuchen, den Dienstcode zu kompilieren, erhalten Sie die folgende Ausgabe des Compilers. Sie ist in Abbildung 06 dargestellt.

Abbildung 06

Abbildung 06. Mehrere Fehler. Aber gibt es wirklich so viele von ihnen?

Die Zahl der Fehler ist recht hoch, ebenso die Zahl der Warnungen. Wie ich bereits erwähnt habe, beginnen wir mit dem ersten Fehler auf dieser Liste. Die Änderungen beginnen daher in Zeile 28 der Header-Datei C_Replay.mqh. Um das Ganze nicht zu langweilig zu machen, sehen wir uns den folgenden Code an, denn das meiste, was wir tun müssen, ist u_Value zu entfernen. Der Code ohne diesen Verweis befindet sich hier:

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "C_ConfigService.mqh"
005. //+------------------------------------------------------------------+
006. class C_Replay : private C_ConfigService
007. {
008.    private :
009.            long   m_IdReplay;
010.            struct st01
011.            {
012.                   MqlRates Rate[1];
013.                   datetime memDT;
014.            }m_MountBar;
015.            struct st02
016.            {
017.                   bool    bInit;
018.                   double  PointsPerTick;
019.                   MqlTick tick[1];
020.            }m_Infos;
021. //+------------------------------------------------------------------+
022.            void AdjustPositionToReplay(const bool bViewBuider)
023.                    {
024.                            u_Interprocess Info;
025.                            MqlRates       Rate[def_BarsDiary];
026.                            int            iPos, nCount;
027.                            
028.                            Info.df_Value = GlobalVariableGet(def_GlobalVariableReplay);
029.                            if (Info.s_Infos.iPosShift == (int)((m_ReplayCount * def_MaxPosSlider * 1.0) / m_Ticks.nTicks)) return;
030.                            iPos = (int)(m_Ticks.nTicks * ((Info.s_Infos.iPosShift * 1.0) / (def_MaxPosSlider + 1)));
031.                            Rate[0].time = macroRemoveSec(m_Ticks.Info[iPos].time);
032.                            CreateBarInReplay(true);
033.                            if (bViewBuider)
034.                            {
035.                                    Info.s_Infos.isWait = true;
036.                                    GlobalVariableSet(def_GlobalVariableReplay, Info.df_Value);
037.                            }else
038.                            {
039.                                    for(; Rate[0].time > (m_Ticks.Info[m_ReplayCount].time); m_ReplayCount++);
040.                                    for (nCount = 0; m_Ticks.Rate[nCount].time < macroRemoveSec(m_Ticks.Info[iPos].time); nCount++);
041.                                    nCount = CustomRatesUpdate(def_SymbolReplay, m_Ticks.Rate, nCount);
042.                            }
043.                            for (iPos = (iPos > 0 ? iPos - 1 : 0); (m_ReplayCount < iPos) && (!_StopFlag);) CreateBarInReplay(false);
044.                            CustomTicksAdd(def_SymbolReplay, m_Ticks.Info, m_ReplayCount);
045.                            Info.df_Value = GlobalVariableGet(def_GlobalVariableReplay);
046.                            Info.s_Infos.isWait = false;
047.                            GlobalVariableSet(def_GlobalVariableReplay, Info.df_Value);
048.                    }
049. //+------------------------------------------------------------------+
050. inline void CreateBarInReplay(const bool bViewTicks)
051.                    {
052. #define def_Rate m_MountBar.Rate[0]
053. 
054.                            bool    bNew;
055.                            double  dSpread;
056.                            int     iRand = rand();
057.                            
058.                            if (BuildBar1Min(m_ReplayCount, def_Rate, bNew))
059.                            {
060.                                    m_Infos.tick[0] = m_Ticks.Info[m_ReplayCount];
061.                                    if ((!m_Ticks.bTickReal) && (m_Ticks.ModePlot == PRICE_EXCHANGE))
062.                                    {                                               
063.                                            dSpread = m_Infos.PointsPerTick + ((iRand > 29080) && (iRand < 32767) ? ((iRand & 1) == 1 ? m_Infos.PointsPerTick : 0 ) : 0 );
064.                                            if (m_Infos.tick[0].last > m_Infos.tick[0].ask)
065.                                            {
066.                                                    m_Infos.tick[0].ask = m_Infos.tick[0].last;
067.                                                    m_Infos.tick[0].bid = m_Infos.tick[0].last - dSpread;
068.                                            }else   if (m_Infos.tick[0].last < m_Infos.tick[0].bid)
069.                                            {
070.                                                    m_Infos.tick[0].ask = m_Infos.tick[0].last + dSpread;
071.                                                    m_Infos.tick[0].bid = m_Infos.tick[0].last;
072.                                            }
073.                                    }
074.                                    if (bViewTicks) CustomTicksAdd(def_SymbolReplay, m_Infos.tick);
075.                                    CustomRatesUpdate(def_SymbolReplay, m_MountBar.Rate);
076.                            }
077.                            m_ReplayCount++;
078. #undef def_Rate
079.                    }
080. //+------------------------------------------------------------------+
081.            void ViewInfos(void)
082.                    {
083.                            MqlRates Rate[1];
084.                            
085.                            ChartSetInteger(m_IdReplay, CHART_SHOW_ASK_LINE, m_Ticks.ModePlot == PRICE_FOREX);
086.                            ChartSetInteger(m_IdReplay, CHART_SHOW_BID_LINE, m_Ticks.ModePlot == PRICE_FOREX);
087.                            ChartSetInteger(m_IdReplay, CHART_SHOW_LAST_LINE, m_Ticks.ModePlot == PRICE_EXCHANGE);
088.                            m_Infos.PointsPerTick = SymbolInfoDouble(def_SymbolReplay, SYMBOL_TRADE_TICK_SIZE);
089.                            m_MountBar.Rate[0].time = 0;
090.                            m_Infos.bInit = true;
091.                            CopyRates(def_SymbolReplay, PERIOD_M1, 0, 1, Rate);
092.                            if ((m_ReplayCount == 0) && (m_Ticks.ModePlot == PRICE_EXCHANGE))
093.                                    for (; m_Ticks.Info[m_ReplayCount].volume_real == 0; m_ReplayCount++);
094.                            if (Rate[0].close > 0)
095.                            {
096.                                    if (m_Ticks.ModePlot == PRICE_EXCHANGE) m_Infos.tick[0].last = Rate[0].close; else
097.                                    {
098.                                            m_Infos.tick[0].bid = Rate[0].close;
099.                                            m_Infos.tick[0].ask = Rate[0].close + (Rate[0].spread * m_Infos.PointsPerTick);
100.                                    }                                       
101.                                    m_Infos.tick[0].time = Rate[0].time;
102.                                    m_Infos.tick[0].time_msc = Rate[0].time * 1000;
103.                            }else
104.                                    m_Infos.tick[0] = m_Ticks.Info[m_ReplayCount];
105.                            CustomTicksAdd(def_SymbolReplay, m_Infos.tick);
106.                            ChartRedraw(m_IdReplay);
107.                    }
108. //+------------------------------------------------------------------+
109.            void CreateGlobalVariable(const string szName, const double value)
110.                    {
111.                            GlobalVariableDel(szName);
112.                            GlobalVariableTemp(szName);     
113.                            GlobalVariableSet(szName, value);
114.                    }
115. //+------------------------------------------------------------------+
116.    public  :
117. //+------------------------------------------------------------------+
118.            C_Replay(const string szFileConfig)
119.                    {
120.                            m_ReplayCount = 0;
121.                            m_dtPrevLoading = 0;
122.                            m_Ticks.nTicks = 0;
123.                            m_Infos.bInit = false;
124.                            Print("************** Market Replay Service **************");
125.                            srand(GetTickCount());
126.                            GlobalVariableDel(def_GlobalVariableReplay);
127.                            SymbolSelect(def_SymbolReplay, false);
128.                            CustomSymbolDelete(def_SymbolReplay);
129.                            CustomSymbolCreate(def_SymbolReplay, StringFormat("Custom\\%s", def_SymbolReplay), _Symbol);
130.                            CustomRatesDelete(def_SymbolReplay, 0, LONG_MAX);
131.                            CustomTicksDelete(def_SymbolReplay, 0, LONG_MAX);
132.                            CustomSymbolSetDouble(def_SymbolReplay, SYMBOL_TRADE_TICK_SIZE, 0);
133.                            CustomSymbolSetDouble(def_SymbolReplay, SYMBOL_TRADE_TICK_VALUE, 0);
134.                            CustomSymbolSetDouble(def_SymbolReplay, SYMBOL_VOLUME_STEP, 0);
135.                            CustomSymbolSetString(def_SymbolReplay, SYMBOL_DESCRIPTION, "Symbol for replay / simulation");
136.                            CustomSymbolSetInteger(def_SymbolReplay, SYMBOL_DIGITS, 8);
137.                            m_IdReplay = (SetSymbolReplay(szFileConfig) ? 0 : -1);
138.                            SymbolSelect(def_SymbolReplay, true);
139.                    }
140. //+------------------------------------------------------------------+
141.            ~C_Replay()
142.                    {
143.                            ArrayFree(m_Ticks.Info);
144.                            ArrayFree(m_Ticks.Rate);
145.                            m_IdReplay = ChartFirst();
146.                            do
147.                            {
148.                                    if (ChartSymbol(m_IdReplay) == def_SymbolReplay)
149.                                            ChartClose(m_IdReplay);
150.                            }while ((m_IdReplay = ChartNext(m_IdReplay)) > 0);
151.                            for (int c0 = 0; (c0 < 2) && (!SymbolSelect(def_SymbolReplay, false)); c0++);
152.                            CustomRatesDelete(def_SymbolReplay, 0, LONG_MAX);
153.                            CustomTicksDelete(def_SymbolReplay, 0, LONG_MAX);
154.                            CustomSymbolDelete(def_SymbolReplay);
155.                            GlobalVariableDel(def_GlobalVariableReplay);
156.                            GlobalVariableDel(def_GlobalVariableServerTime);
157.                            Print("Finished replay service...");
158.                    }
159. //+------------------------------------------------------------------+
160.            bool ViewReplay(ENUM_TIMEFRAMES arg1)
161.                    {
162. #define macroError(A) { Print(A); return false; }
163.                            u_Interprocess info;
164.                            
165.                            if (SymbolInfoDouble(def_SymbolReplay, SYMBOL_TRADE_TICK_SIZE) == 0)
166.                                    macroError("Asset configuration is not complete, it remains to declare the size of the ticket.");
167.                            if (SymbolInfoDouble(def_SymbolReplay, SYMBOL_TRADE_TICK_VALUE) == 0)
168.                                    macroError("Asset configuration is not complete, need to declare the ticket value.");
169.                            if (SymbolInfoDouble(def_SymbolReplay, SYMBOL_VOLUME_STEP) == 0)
170.                                    macroError("Asset configuration not complete, need to declare the minimum volume.");
171.                            if (m_IdReplay == -1) return false;
172.                            if ((m_IdReplay = ChartFirst()) > 0) do
173.                            {
174.                                    if (ChartSymbol(m_IdReplay) == def_SymbolReplay)
175.                                    {
176.                                            ChartClose(m_IdReplay);
177.                                            ChartRedraw();
178.                                    }
179.                            }while ((m_IdReplay = ChartNext(m_IdReplay)) > 0);
180.                            Print("Waiting for [Market Replay] indicator permission to start replay ...");
181.                            info.ServerTime = ULONG_MAX;
182.                            CreateGlobalVariable(def_GlobalVariableServerTime, info.df_Value);
183.                            m_IdReplay = ChartOpen(def_SymbolReplay, arg1);
184.                            ChartApplyTemplate(m_IdReplay, "Market Replay.tpl");
185.                            while ((!GlobalVariableGet(def_GlobalVariableReplay, info.df_Value)) && (!_StopFlag) && (ChartSymbol(m_IdReplay) != "")) Sleep(750);
186.                            info.s_Infos.isHedging = TypeAccountIsHedging();
187.                            info.s_Infos.isSync = true;
188.                            GlobalVariableSet(def_GlobalVariableReplay, info.df_Value);
189. 
190.                            return ((!_StopFlag) && (ChartSymbol(m_IdReplay) != ""));
191. #undef macroError
192.                    }
193. //+------------------------------------------------------------------+
194.            bool LoopEventOnTime(const bool bViewBuider)
195.                    {
196.                            u_Interprocess Info;
197.                            int iPos, iTest, iCount;
198.                            
199.                            if (!m_Infos.bInit) ViewInfos();
200.                            iTest = 0;
201.                            while ((iTest == 0) && (!_StopFlag))
202.                            {
203.                                    iTest = (ChartSymbol(m_IdReplay) != "" ? iTest : -1);
204.                                    iTest = (GlobalVariableGet(def_GlobalVariableReplay, Info.df_Value) ? iTest : -1);
205.                                    iTest = (iTest == 0 ? (Info.s_Infos.isPlay ? 1 : iTest) : iTest);
206.                                    if (iTest == 0) Sleep(100);
207.                            }
208.                            if ((iTest < 0) || (_StopFlag)) return false;
209.                            AdjustPositionToReplay(bViewBuider);
210.                            Info.ServerTime = m_Ticks.Info[m_ReplayCount].time;
211.                            GlobalVariableSet(def_GlobalVariableServerTime, Info.df_Value);
212.                            iPos = iCount = 0;
213.                            while ((m_ReplayCount < m_Ticks.nTicks) && (!_StopFlag))
214.                            {
215.                                    iPos += (int)(m_ReplayCount < (m_Ticks.nTicks - 1) ? m_Ticks.Info[m_ReplayCount + 1].time_msc - m_Ticks.Info[m_ReplayCount].time_msc : 0);
216.                                    CreateBarInReplay(true);
217.                                    while ((iPos > 200) && (!_StopFlag))
218.                                    {
219.                                            if (ChartSymbol(m_IdReplay) == "") return false;
220.                                            GlobalVariableGet(def_GlobalVariableReplay, Info.df_Value);
221.                                            if (!Info.s_Infos.isPlay) return true;
222.                                            Info.s_Infos.iPosShift = (ushort)((m_ReplayCount * def_MaxPosSlider) / m_Ticks.nTicks);
223.                                            GlobalVariableSet(def_GlobalVariableReplay, Info.df_Value);
224.                                            Sleep(195);
225.                                            iPos -= 200;
226.                                            iCount++;
227.                                            if (iCount > 4)
228.                                            {
229.                                                    iCount = 0;
230.                                                    GlobalVariableGet(def_GlobalVariableServerTime, Info.df_Value);
231.                                                    if ((m_Ticks.Info[m_ReplayCount].time - m_Ticks.Info[m_ReplayCount - 1].time) > 60) Info.ServerTime = ULONG_MAX; else
232.                                                    {
233.                                                            Info.ServerTime += 1;
234.                                                            Info.ServerTime = ((Info.ServerTime + 1) < m_Ticks.Info[m_ReplayCount].time ? Info.ServerTime : m_Ticks.Info[m_ReplayCount].time);
235.                                                    };
236.                                                    GlobalVariableSet(def_GlobalVariableServerTime, Info.df_Value);
237.                                            }
238.                                    }
239.                            }                               
240.                            return (m_ReplayCount == m_Ticks.nTicks);
241.                    }                               
242. //+------------------------------------------------------------------+
243. };
244. //+------------------------------------------------------------------+
245. #undef macroRemoveSec
246. #undef def_SymbolReplay
247. //+------------------------------------------------------------------+

Quellcode der Datei C_Replay.mqh

Der obige Code wurde korrigiert, damit er dem entspricht, was wir in diesem Artikel tun werden. Aber der Indikatorcode fehlt immer noch, und wenn Sie den Dienstcode mit den Änderungen an der Header-Datei C_Replay.mqh erneut kompilieren, erhalten Sie das in Abbildung 07 gezeigte Ergebnis.

Abbildung 07

Abbildung 07. Noch vorhandene Fehler im Indikator

Jetzt müssen wir also den Code des Indikators korrigieren. Wenn Sie dies versuchen, erhalten Sie das in Abbildung 08 dargestellte Ergebnis.

Abbildung 08

Abbildung 08. Fehler, die aus demselben Grund auftreten

Auch hier machen wir Schritt für Schritt weiter. Beginnen Sie immer mit dem ersten Fehler.

Wenn die Indikatordatei korrekt geändert wird, erhalten Sie den folgenden Code:

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.49"
07. #property link "https://www.mql5.com/ru/articles/11820"
08. #property indicator_chart_window
09. #property indicator_plots 0
10. //+------------------------------------------------------------------+
11. #include <Market Replay\Service Graphics\C_Controls.mqh>
12. //+------------------------------------------------------------------+
13. C_Terminal *terminal = NULL;
14. C_Controls *control = NULL;
15. //+------------------------------------------------------------------+
16. #define def_InfoTerminal (*terminal).GetInfoTerminal()
17. #define def_ShortName       "Market_" + def_SymbolReplay
18. //+------------------------------------------------------------------+
19. int OnInit()
20. {
21.     u_Interprocess Info;
22. 
23.     ResetLastError();
24.     if (CheckPointer(control = new C_Controls(terminal = new C_Terminal())) == POINTER_INVALID) return INIT_FAILED;
25.     if (_LastError != ERR_SUCCESS) return INIT_FAILED;
26.     IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName);
27.     IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName + "Device");
28.     if (GlobalVariableCheck(def_GlobalVariableReplay)) Info.df_Value = GlobalVariableGet(def_GlobalVariableReplay); else Info.df_Value = 0;
29.     EventChartCustom(def_InfoTerminal.ID, C_Controls::ev_WaitOff, 1, Info.df_Value, "");
30.     (*control).Init(Info.s_Infos.isPlay);
31.         
32.     return INIT_SUCCEEDED;
33. }
34. //+------------------------------------------------------------------+
35. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
36. {
37.     static bool bWait = false;
38.     u_Interprocess Info;
39.     
40.     Info.df_Value = GlobalVariableGet(def_GlobalVariableReplay);
41.     if (!bWait)
42.     {
43.             if (Info.s_Infos.isWait)
44.             {
45.                     EventChartCustom(def_InfoTerminal.ID, C_Controls::ev_WaitOn, 1, 0, "");
46.                     bWait = true;
47.             }
48.     }else if (!Info.s_Infos.isWait)
49.     {
50.             EventChartCustom(def_InfoTerminal.ID, C_Controls::ev_WaitOff, 1, Info.df_Value, "");
51.             bWait = false;
52.     }
53.     
54.     return rates_total;
55. }
56. //+------------------------------------------------------------------+
57. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
58. {
59.     (*control).DispatchMessage(id, lparam, dparam, sparam);
60. }
61. //+------------------------------------------------------------------+
62. void OnDeinit(const int reason)
63. {
64.     switch (reason)
65.     {
66.             case REASON_REMOVE:
67.             case REASON_CHARTCLOSE:
68.                     if (def_InfoTerminal.szSymbol != def_SymbolReplay) break;
69.                     GlobalVariableDel(def_GlobalVariableReplay);
70.                     ChartClose(def_InfoTerminal.ID);
71.                     break;
72.     }
73.     delete control;
74.     delete terminal;
75. }
76. //+------------------------------------------------------------------+

Quellcode des Indikators Replay

Dieser Code ist nun vollständig korrigiert, aber wenn Sie versuchen, ihn zu kompilieren, werden Sie immer noch einige Fehler vom Compiler erhalten, die in Abbildung 09 zu sehen sind.

Abbildung 09

Abbildung 09. Versuch, den Kontrollindikator zu kompilieren

Nun müssen wir die Header-Datei C_Controls.mqh aufrufen und einige Korrekturen vornehmen. Aber diese Korrekturen sind recht einfach. Dazu müssen Sie lediglich alle Verweise auf u_Value entfernen. Sie erhalten also den folgenden Code:

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. #define def_AcessTerminal (*Terminal)
032. #define def_InfoTerminal def_AcessTerminal.GetInfoTerminal()
033. //+------------------------------------------------------------------+
034. class C_Controls : protected C_Mouse
035. {
036.    protected:
037.            enum EventCustom {ev_WaitOn, ev_WaitOff};
038.    private :
039. //+------------------------------------------------------------------+
040.            string  m_szBtnPlay;
041.            bool            m_bWait;
042.            struct st_00
043.            {
044.                    string  szBtnLeft,
045.                            szBtnRight,
046.                            szBtnPin,
047.                            szBarSlider,
048.                            szBarSliderBlock;
049.                    int     posPinSlider,
050.                            posY,
051.                            Minimal;
052.            }m_Slider;
053.            C_Terminal *Terminal;
054. //+------------------------------------------------------------------+
055. inline void CreateObjectBitMap(int x, int y, string szName, string Resource1, string Resource2 = NULL)
056.                    {
057.                            ObjectCreate(def_InfoTerminal.ID, szName, OBJ_BITMAP_LABEL, 0, 0, 0);
058.                            ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_XDISTANCE, def_PosXObjects + x);
059.                            ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_YDISTANCE, y);
060.                            ObjectSetString(def_InfoTerminal.ID, szName, OBJPROP_BMPFILE, 0, "::" + Resource1);
061.                            ObjectSetString(def_InfoTerminal.ID, szName, OBJPROP_BMPFILE, 1, "::" + (Resource2 == NULL ? Resource1 : Resource2));
062.                            ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_ZORDER, 1);
063.                    }
064. //+------------------------------------------------------------------+
065. inline void CreteBarSlider(int x, int size)
066.                    {
067.                            ObjectCreate(def_InfoTerminal.ID, m_Slider.szBarSlider, OBJ_RECTANGLE_LABEL, 0, 0, 0);
068.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSlider, OBJPROP_XDISTANCE, def_PosXObjects + x);
069.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSlider, OBJPROP_YDISTANCE, m_Slider.posY - 4);
070.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSlider, OBJPROP_XSIZE, size);
071.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSlider, OBJPROP_YSIZE, 9);
072.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSlider, OBJPROP_BGCOLOR, clrLightSkyBlue);
073.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSlider, OBJPROP_BORDER_COLOR, clrBlack);
074.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSlider, OBJPROP_WIDTH, 3);
075.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSlider, OBJPROP_BORDER_TYPE, BORDER_FLAT);
076. //---
077.                            ObjectCreate(def_InfoTerminal.ID, m_Slider.szBarSliderBlock, OBJ_RECTANGLE_LABEL, 0, 0, 0);
078.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSliderBlock, OBJPROP_XDISTANCE, def_PosXObjects + x);
079.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSliderBlock, OBJPROP_YDISTANCE, m_Slider.posY - 9);
080.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSliderBlock, OBJPROP_YSIZE, 19);
081.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSliderBlock, OBJPROP_BGCOLOR, clrRosyBrown);
082.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSliderBlock, OBJPROP_BORDER_TYPE, BORDER_RAISED);
083.                    }
084. //+------------------------------------------------------------------+
085.            void CreateBtnPlayPause(bool state)
086.                    {
087.                            m_szBtnPlay = def_PrefixObjectName + "Play";
088.                            CreateObjectBitMap(0, 25, m_szBtnPlay, (m_bWait ? def_ButtonWait : def_ButtonPause), (m_bWait ? def_ButtonWait : def_ButtonPlay));
089.                            ObjectSetInteger(def_InfoTerminal.ID, m_szBtnPlay, OBJPROP_STATE, state);
090.                    }
091. //+------------------------------------------------------------------+
092.            void CreteCtrlSlider(void)
093.                    {
094.                            u_Interprocess Info;
095.                            
096.                            m_Slider.szBarSlider      = def_NameObjectsSlider + " Bar";
097.                            m_Slider.szBarSliderBlock = def_NameObjectsSlider + " Bar Block";
098.                            m_Slider.szBtnLeft        = def_NameObjectsSlider + " BtnL";
099.                            m_Slider.szBtnRight       = def_NameObjectsSlider + " BtnR";
100.                            m_Slider.szBtnPin         = def_NameObjectsSlider + " BtnP";
101.                            m_Slider.posY = 40;
102.                            CreteBarSlider(77, 436);
103.                            CreateObjectBitMap(47, 25, m_Slider.szBtnLeft, def_ButtonLeft, def_ButtonLeftBlock);
104.                            CreateObjectBitMap(511, 25, m_Slider.szBtnRight, def_ButtonRight, def_ButtonRightBlock);
105.                            CreateObjectBitMap(0, m_Slider.posY, m_Slider.szBtnPin, def_ButtonPin);
106.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBtnPin, OBJPROP_ANCHOR, ANCHOR_CENTER);
107.                            if (GlobalVariableCheck(def_GlobalVariableReplay)) Info.df_Value = GlobalVariableGet(def_GlobalVariableReplay); else Info.df_Value = 0;
108.                            m_Slider.Minimal = Info.s_Infos.iPosShift;
109.                            PositionPinSlider(Info.s_Infos.iPosShift);
110.                    }
111. //+------------------------------------------------------------------+
112. inline void RemoveCtrlSlider(void)
113.                    {                       
114.                            ChartSetInteger(def_InfoTerminal.ID, CHART_EVENT_OBJECT_DELETE, false);
115.                            ObjectsDeleteAll(def_InfoTerminal.ID, def_NameObjectsSlider);
116.                            ChartSetInteger(def_InfoTerminal.ID, CHART_EVENT_OBJECT_DELETE, true);
117.                    }
118. //+------------------------------------------------------------------+
119. inline void PositionPinSlider(int p, const int minimal = 0)
120.                    {
121.                            m_Slider.posPinSlider = (p < minimal ? minimal : (p > def_MaxPosSlider ? def_MaxPosSlider : p));
122.                            m_Slider.posPinSlider = (p < m_Slider.Minimal ? m_Slider.Minimal : (p > def_MaxPosSlider ? def_MaxPosSlider : p));
123.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBtnPin, OBJPROP_XDISTANCE, m_Slider.posPinSlider + def_PosXObjects + 95);
124.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBtnLeft, OBJPROP_STATE, m_Slider.posPinSlider != minimal);
125.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBtnLeft, OBJPROP_STATE, m_Slider.posPinSlider != m_Slider.Minimal);
126.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBtnRight, OBJPROP_STATE, m_Slider.posPinSlider < def_MaxPosSlider);
127.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSliderBlock, OBJPROP_XSIZE, minimal + 2);
128.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSliderBlock, OBJPROP_XSIZE, m_Slider.Minimal + 2);
129.                            ChartRedraw();
130.                    }
131. //+------------------------------------------------------------------+
132.    public  :
133. //+------------------------------------------------------------------+
134.            C_Controls(C_Terminal *arg)
135.                    :C_Mouse(arg),
136.                     m_bWait(false)
137.                    {
138.                            if (CheckPointer(Terminal = arg) == POINTER_INVALID) SetUserError(C_Terminal::ERR_PointerInvalid);
139.                            m_szBtnPlay          = NULL;
140.                            m_Slider.szBarSlider = NULL;
141.                            m_Slider.szBtnPin    = NULL;
142.                            m_Slider.szBtnLeft   = NULL;
143.                            m_Slider.szBtnRight  = NULL;
144.                    }
145. //+------------------------------------------------------------------+
146.            ~C_Controls()
147.                    {
148.                            if (CheckPointer(Terminal) == POINTER_INVALID) return;
149.                            ChartSetInteger(def_InfoTerminal.ID, CHART_EVENT_OBJECT_DELETE, false);
150.                            ObjectsDeleteAll(def_InfoTerminal.ID, def_PrefixObjectName);
151.                    }
152. //+------------------------------------------------------------------+
153.            void Init(const bool state)
154.                    {
155.                            CreateBtnPlayPause(state);
156.                            GlobalVariableTemp(def_GlobalVariableReplay);
157.                            if (!state) CreteCtrlSlider();
158.                            ChartRedraw();
159.                    }
160. //+------------------------------------------------------------------+
161.            void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
162.                    {
163.                            u_Interprocess Info;
164.                            static int six = -1, sps;
165.                            int x, y, px1, px2;
166.                            
167.                            C_Mouse::DispatchMessage(id, lparam, dparam, sparam);
168.                            switch (id)
169.                            {
170.                                    case (CHARTEVENT_CUSTOM + C_Controls::ev_WaitOn):
171.                                            if (lparam == 0) break;
172.                                            m_bWait = true;
173.                                            CreateBtnPlayPause(true);
174.                                            break;
175.                                    case (CHARTEVENT_CUSTOM + C_Controls::ev_WaitOff):
176.                                            if (lparam == 0) break;
177.                                            m_bWait = false;
178.                                            Info.df_Value = dparam;
179.                                            CreateBtnPlayPause(Info.s_Infos.isPlay);
180.                                            break;
181.                                    case CHARTEVENT_OBJECT_DELETE:
182.                                            if (StringSubstr(sparam, 0, StringLen(def_PrefixObjectName)) == def_PrefixObjectName)
183.                                            {
184.                                                    if (StringSubstr(sparam, 0, StringLen(def_NameObjectsSlider)) == def_NameObjectsSlider)
185.                                                    {
186.                                                            RemoveCtrlSlider();
187.                                                            CreteCtrlSlider();
188.                                                    }else
189.                                                    {
190.                                                            Info.df_Value = GlobalVariableGet(def_GlobalVariableReplay);
191.                                                            CreateBtnPlayPause(Info.s_Infos.isPlay);
192.                                                    }
193.                                                    ChartRedraw();
194.                                            }
195.                                            break;
196.                                    case CHARTEVENT_OBJECT_CLICK:
197.                                            if (m_bWait) break;
198.                                            if (sparam == m_szBtnPlay)
199.                                            {
200.                                                    Info.s_Infos.isPlay = (bool) ObjectGetInteger(def_InfoTerminal.ID, m_szBtnPlay, OBJPROP_STATE);
201.                                                    if (!Info.s_Infos.isPlay) CreteCtrlSlider(); else
202.                                                    {
203.                                                            RemoveCtrlSlider();
204.                                                            m_Slider.szBtnPin = NULL;
205.                                                    }
206.                                                    Info.s_Infos.iPosShift = (ushort) m_Slider.posPinSlider;
207.                                                    GlobalVariableSet(def_GlobalVariableReplay, Info.df_Value);
208.                                                    ChartRedraw();
209.                                            }else   if (sparam == m_Slider.szBtnLeft) PositionPinSlider(m_Slider.posPinSlider - 1);
210.                                            else if (sparam == m_Slider.szBtnRight) PositionPinSlider(m_Slider.posPinSlider + 1);
211.                                            break;
212.                                    case CHARTEVENT_MOUSE_MOVE:
213.                                            if (GetInfoMouse().ExecStudy) return;
214.                                            if ((CheckClick(C_Mouse::eClickLeft)) && (m_Slider.szBtnPin != NULL))
215.                                            {
216.                                                    x = GetInfoMouse().Position.X;
217.                                                    y = GetInfoMouse().Position.Y;
218.                                                    px1 = m_Slider.posPinSlider + def_PosXObjects + 86;
219.                                                    px2 = m_Slider.posPinSlider + def_PosXObjects + 114;
220.                                                    if ((y >= (m_Slider.posY - 14)) && (y <= (m_Slider.posY + 14)) && (x >= px1) && (x <= px2) && (six == -1))
221.                                                    {
222.                                                            six = x;
223.                                                            sps = m_Slider.posPinSlider;
224.                                                            ChartSetInteger(def_InfoTerminal.ID, CHART_MOUSE_SCROLL, false);
225.                                                    }
226.                                                    if (six > 0) PositionPinSlider(sps + x - six);
227.                                            }else if (six > 0)
228.                                            {
229.                                                    six = -1;
230.                                                    ChartSetInteger(def_InfoTerminal.ID, CHART_MOUSE_SCROLL, true);
231.                                            }
232.                                            break;
233.                            }
234.                    }
235. //+------------------------------------------------------------------+
236. };
237. //+------------------------------------------------------------------+
238. #undef def_InfoTerminal
239. #undef def_AcessTerminal
240. #undef def_PosXObjects
241. #undef def_ButtonPlay
242. #undef def_ButtonPause
243. #undef def_ButtonLeft
244. #undef def_ButtonRight
245. #undef def_ButtonPin
246. #undef def_NameObjectsSlider
247. #undef def_PrefixObjectName
248. #undef def_PathBMP
249. //+------------------------------------------------------------------+

Quellcode der Datei C_Controls.mqh

Nachdem diese Korrekturen, Änderungen und Anpassungen vorgenommen wurden, können wir versuchen, den Replay/Simulator-Dienst erneut zu kompilieren. Daraus ergibt sich folgendes Bild:

Abbildung 10

Abbildung 10. Endgültige Kompilierung

Dies bedeutet, dass der Dienst erfolgreich kompiliert wurde. Beachten Sie, dass auch der Indikator kompiliert wurde. Das wiederum bedeutet, dass Sie die ausführbare Datei des Indikators jetzt einfach mit dem Datei-Explorer löschen können, da sie jetzt Teil der ausführbaren Datei des Replay-/Simulationsdienstes wird. Wenn Sie jedoch die ausführbare Datei des Indikators löschen und versuchen, das Replay-/Simulatorsystem in MetaTrader 5 auszuführen, werden Sie feststellen, dass das Chart geöffnet wird, der Kontrollindikator jedoch nicht erscheint. Warum?

Der Grund dafür ist, dass der Kontrollindikator tatsächlich durch die Vorlage und nicht durch den Dienst ausgelöst wird. In der Vorlage gibt es keinen Hinweis darauf, dass der Kontrollindikator Teil der ausführbaren Datei des Dienstes ist. Dies können Sie feststellen, wenn Sie sich den Inhalt der Vorlagendatei ansehen. Da es aber nicht darum geht, eine Vorlage zu verwenden, sondern einen Dienst zu nutzen, um einen Kontrollindikator im Chart auszuführen, werde ich nicht näher darauf eingehen, wie dieses Problem zu lösen ist. Wir sollten uns auf das konzentrieren, was wir wirklich tun wollen.

Da das, was wir tun müssen, nicht so einfach ist und ich in diesem Artikel nicht zu sehr ins Detail gehen möchte, werde ich die Änderungen für den nächsten Artikel aufheben. Das liegt daran, dass wir viele Dinge ändern müssen, um den Kontrollindikator funktionsfähig zu halten und gleichzeitig den Replay/Simulator-Dienst kostenlos, leichtgewichtig und offen zu halten, damit Sie Ihre Indikatoren, Strategien oder persönlichen Modelle verwenden können. Mit diesen Änderungen soll genau dies gefördert werden. Damit Sie das System mit Ihren eigenen Konzepten und Ideen nutzen können.

Aber der Hauptgrund dafür, dass wir die Demonstration all dessen, was wir tun müssen, damit der Kontrollindikator im Chart erscheint, weil es sich um die Ressource eines Dienstes handelt und wir keine Vorlage verwenden, auf den nächsten Artikel verschieben, ist die Tatsache, dass wir einige der Dinge entfernen müssen, die jetzt doppelt vorhanden sind. Eine solche Verdoppelung macht den Kontrollindikator extrem instabil, wenn er direkt über den Dienst verwendet wird.


Schlussfolgerung

Obwohl wir in diesem Artikel Änderungen am Kontrollindikator und am Dienst vorgenommen haben, sodass wir die ausführbare Datei des Indikators aus der Liste der für den Nutzer verfügbaren Dateien entfernen können, hat sich das System selbst aufgrund ungelöster Probleme als instabil erwiesen. Diese Fragen betreffen die Art und Weise, wie der Kontrollindikator und der Nutzer interagieren. Diese Instabilität wird durch die Notwendigkeit anderer Elemente verursacht, die auf dem Chart vorhanden sein müssen. Diese Elemente sind Teil der Vorlagendatei, die ich bei der Verwendung des Relay/Simulator-Dienstes nicht verwenden möchte. Ich möchte und werde zeigen, wie wir dies tun können, um den Dienst autark zu machen, sodass der Nutzer seine eigenen Einstellungen oder Vorlagen verwenden kann.

Gleichzeitig schaffen wir eine geeignete Möglichkeit, den Replay/Simulator-Dienst zu fördern, damit Sie Ihre Analysestrategie oder Ihr Modell üben können.

Wir haben noch viel zu tun, bevor dieses System die lang erwartete Funktionalität für die Arbeit mit Aufträgen und Positionen im Replay- und Simulator-Modus erhalten kann. Ich hoffe, dass Sie, liebe Leserinnen und Leser, das Niveau und den Grad der Komplexität verstehen, den wir erreicht haben. Und all dies geschieht nur mit Hilfe von MQL5, ohne externe Programmierung. Dieses System erwies sich in der Tat als recht komplex. Aber ich mag Herausforderungen, und diese hier inspiriert mich weiterhin.

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

Beigefügte Dateien |
Anexo.zip (420.65 KB)
Matrix-Faktorisierung: Ein praktikables Modell Matrix-Faktorisierung: Ein praktikables Modell
Sie haben vielleicht nicht bemerkt, dass die Matrixmodellierung etwas seltsam war, da nur Spalten und nicht Zeilen und Spalten angegeben wurden. Das sieht sehr seltsam aus, wenn man den Code liest, der die Matrixfaktorisierung durchführt. Wenn Sie erwartet haben, die Zeilen und Spalten aufgelistet zu sehen, könnten Sie beim Versuch, zu faktorisieren, verwirrt werden. Außerdem ist diese Matrixmodellierungsmethode nicht die beste. Denn wenn wir Matrizen auf diese Weise modellieren, stoßen wir auf einige Einschränkungen, die uns zwingen, andere Methoden oder Funktionen zu verwenden, die nicht notwendig wären, wenn die Modellierung auf eine angemessenere Weise erfolgen würde.
Entwicklung eines Replay Systems (Teil 48): Das Konzept eines Dienstes verstehen Entwicklung eines Replay Systems (Teil 48): Das Konzept eines Dienstes verstehen
Wie wäre es, etwas Neues zu lernen? In diesem Artikel erfahren Sie, wie Sie Skripte in Dienste umwandeln können und warum dies sinnvoll ist.
Neuronales Netz in der Praxis: Kleinste Quadrate Neuronales Netz in der Praxis: Kleinste Quadrate
In diesem Artikel werden wir uns einige Ideen ansehen, u. a. dass mathematische Formeln im Aussehen komplexer sind als bei der Implementierung in Code. Außerdem werden wir uns damit beschäftigen, wie man einen Chart-Quadranten einrichtet, sowie mit einem interessanten Problem, das in Ihrem MQL5-Code auftreten kann. Obwohl ich, um ehrlich zu sein, immer noch nicht ganz verstehe, wie ich es erklären soll. Wie auch immer, ich zeige Ihnen, wie Sie das im Code beheben können.
Entwicklung eines Wiedergabesystems (Teil 47): Chart Trade Projekt (VI) Entwicklung eines Wiedergabesystems (Teil 47): Chart Trade Projekt (VI)
Schließlich beginnt unser Indikator Chart Trade mit dem EA zu interagieren, sodass die Informationen interaktiv übertragen werden können. Daher werden wir in diesem Artikel den Indikator verbessern, sodass er funktional genug ist, um zusammen mit jedem EA verwendet zu werden. Dadurch können wir auf den Indikator Chart Trade zugreifen und mit ihm arbeiten, als ob er tatsächlich mit einem EA verbunden wäre. Aber wir werden es auf eine viel interessantere Weise tun als bisher.