
Entwicklung eins Replay Systems (Teil 49): Die Dinge werden kompliziert (I)
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. 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. 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. 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
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. 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. 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. 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. 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. 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. 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





- Freie Handelsapplikationen
- Über 8.000 Signale zum Kopieren
- Wirtschaftsnachrichten für die Lage an den Finanzmärkte
Sie stimmen der Website-Richtlinie und den Nutzungsbedingungen zu.