
Entwicklung eines Replay Systems (Teil 54): Die Geburt des ersten Moduls
Einführung
Im vorherigen Artikel „Entwicklung eines Replay Systems (Teil 53): Die Dinge werden kompliziert (V)“ habe ich einige Konzepte erklärt, die von nun an Teil unserer Programmierung sein werden und die wir in MQL5 und der MetaTrader 5 Plattform verwenden werden. Mir ist klar, dass viele dieser Konzepte für die meisten Leser neu sind, und ich weiß auch, dass jeder, der Erfahrung in der Systemprogrammierung hat (z. B. diejenigen, die Windows-Systeme programmieren), mit diesen Konzepten vertraut sein wird.
Wenn Sie also wirklich eintauchen und verstehen wollen, warum und wie das, was ich Ihnen jetzt zeige, funktioniert, schlage ich vor, dass Sie ein wenig Windows-Programmierung lernen, um zu wissen, wie Nachrichten zwischen Programmen ausgetauscht werden. Diese Informationen in diesem Artikel würden uns weit weg von dem führen, was ich wirklich zeigen möchte: wie man in MQL5 und MetaTrader 5 auf einem fortgeschrittenen Niveau entwickelt und arbeitet.
Sie werden keine Probleme haben, Programme zu finden, die diese Nachrichtenübermittlung zur Kommunikation in der Windows-Umgebung verwenden, aber um sie richtig zu verstehen, sind einige Vorkenntnisse und eine solide Grundlage in der C-Programmierung erforderlich. Wenn Sie diese Kenntnisse nicht haben, rate ich Ihnen, zunächst die C-Programmierung zu erlernen und dann zu lernen, wie Nachrichten zwischen Programmen unter Windows ausgetauscht werden. Auf diese Weise wird es möglich sein, eine breite und solide Grundlage für das Verständnis unserer weiteren Arbeit zu schaffen.
Dinge ermöglichen
Wenn Sie den vorigen Artikel aufmerksam gelesen haben, ist Ihnen wahrscheinlich aufgefallen, dass ich über einen langen Zeitraum versucht habe, etwas zu tun. Doch auch wenn alle diese Elemente teilweise funktionierten, konnten sie in einem größeren System nicht nebeneinander bestehen, zumindest nicht so, wie sich die Dinge entwickelten.
Mein vielleicht größter Fehler war, den ich über mehrere Wochen ignoriert habe, dass unsere Anwendungen auf Ereignisse in der MetaTrader 5-Plattform reagieren, aber der Fehler war ein anderer: Ich betrachtete MetaTrader 5 nicht als Plattform, sondern als ein einfaches Programm, in dem andere Prozesse ablaufen würden.
Dieser Mangel in der Art und Weise, wie ich den MetaTrader 5 sehe, ist darauf zurückzuführen, dass andere Plattformen uns nicht die gleiche Flexibilität bieten wie der MetaTrader 5. Aus diesem Grund habe ich bei der Entwicklung fortgeschrittener Anwendungen etwas Zeit und Geschwindigkeit verloren. Da sich jedoch herausgestellt hat, dass dieses Replay-/Simulatorsystem besser in modularer Weise entwickelt werden sollte (anders als viele es normalerweise tun), bin ich auf einige Probleme gestoßen. Aber es waren nicht wirklich die Probleme, sondern etwas, das ich ignoriert habe.
Sie können in den Videos im vorherigen Artikel sehen, dass MetaTrader 5 viel mehr bietet, als viele von Ihnen erforscht haben. Aber heute werden wir das, was einst nur ein Traum war, in etwas Erreichbares verwandeln. Wir werden anfangen, weniger zu programmieren und mehr zu bauen. In diesem Artikel werden wir uns mit dem Austausch von Nachrichten befassen, um den MetaTrader 5 noch besser für uns arbeiten zu lassen, wobei wir uns darauf konzentrieren, dass die Anwendung harmonisch mit anderen Dingen auf dem Chart zusammenarbeitet.
Als erstes werden wir den Mausindikator anpassen, um diese neue Phase in der Entwicklung von MetaTrader 5-Anwendungen zu beginnen.
Da der Kodex erhebliche Änderungen erfahren wird, müssen viele Teile davon geändert werden. Wenn Sie jedoch alle Schritte befolgt haben, werden Sie keine Schwierigkeiten haben, diese Änderungen vorzunehmen.
Deshalb werden wir ab sofort eine gemeinsame Datei für alle künftig erscheinenden Anträge erstellen. So können wir Nachrichten adressieren, die von nun an von jedem Code verarbeitet werden können, den wir erstellen. Wir haben die Kontrolle darüber, was passiert, wenn sich zwei Anwendungen, die einen Message-Handler haben, im Chart befinden. Auf diese Weise ist jede Anwendung in der Lage, die Nachricht korrekt zu verarbeiten.
Der ursprüngliche Inhalt dieser Datei wird im Folgenden dargestellt. Sie sollte unter dem Namen Defines.mqh gespeichert werden. Sein Standort wird demnächst bekannt gegeben. Wenn Sie also absolut keine Ahnung vom Programmieren haben, tut es mir leid, aber von nun an werden Sie nicht in der Lage sein, dem zu folgen, was ich implementieren werde. Von diesem Moment an befindet sich eine Barriere vor Ihnen, die Sie daran hindert, weiterzugehen. Wenn Sie das, was wir heute behandeln, wirklich nutzen wollen, müssen Sie einige grundlegende Programmierkenntnisse haben.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #define def_VERSION_DEBUG 05. //+------------------------------------------------------------------+ 06. #ifdef def_VERSION_DEBUG 07. #define macro_DEBUG_MODE(A) \ 08. Print(__FILE__, " ", __LINE__, " ", __FUNCTION__ + " " + #A + " = " + (string)(A)); 09. #else 10. #define macro_DEBUG_MODE(A) 11. #endif 12. //+------------------------------------------------------------------+ 13. #define def_SymbolReplay "RePlay" 14. #define def_MaxPosSlider 400 15. //+------------------------------------------------------------------+ 16. union uCast_Double 17. { 18. double dValue; 19. long _long; // 1 Information 20. datetime _datetime; // 1 Information 21. int _int[sizeof(double) / sizeof(int)]; // 2 Informations 22. char _char[sizeof(double) / sizeof(char)]; // 8 Informations 23. }; 24. //+------------------------------------------------------------------+ 25. enum EnumEvents { 26. evHideMouse, //Hide mouse price line 27. evShowMouse, //Show mouse price line 28. evHideBarTime, //Hide bar time 29. evShowBarTime, //Show bar time 30. evHideDailyVar, //Hide daily variation 31. evShowDailyVar, //Show daily variation 32. evHidePriceVar, //Hide instantaneous variation 33. evShowPriceVar, //Show instantaneous variation 34. evSetServerTime, //Replay/simulation system timer 35. evCtrlReplayInit //Initialize replay control 36. }; 37. //+------------------------------------------------------------------+
Quellcode der Datei Defines.mqh
In Zeile 13 definieren wir den Namen, der in unserem nutzerdefinierten Symbol verwendet wird und der vom Replay-/Simulationsdienst verwendet wird, wie wir es seit Beginn dieser Artikelserie getan haben. In Zeile 14 deklarieren wir etwas, das nur vom Kontrollindikator verwendet werden soll.
Diese beiden Zeilen sowie das, was zwischen den Zeilen 16 und 23 steht, waren bereits Teil des zuvor geschriebenen Codes. Da die Header-Datei InterProcess.mqh jedoch nicht mehr existiert, war es notwendig, diese Informationen in die Definitionsdatei zu verschieben.
Was uns in dieser Datei wirklich interessiert, beginnt in Zeile 25. Wir geben hier eine Liste bekannt, die mit der Einführung und Hinzufügung neuer Veranstaltungen wachsen wird. Es besteht eine kleine Gefahr, die aber nicht so ernst ist, wenn Sie die entsprechenden Vorsichtsmaßnahmen treffen: Fügen Sie immer am Ende einer Enumeration etwas hinzu. Wenn Sie dies tun, müssen Sie alte Codes nicht neu kompilieren, aber wenn Sie etwas in der Mitte der Liste hinzufügen, dann müssen Sie alle alten Codes neu kompilieren. So lassen sich Probleme vermeiden.
Beachten Sie, dass wir in jeder Zeile einen Wert definieren und einen kurzen Kommentar zu dem entsprechenden Ereignis abgeben.
Sie werden sehen, dass die angezeigten Codes angeben, wo und wie jedes dieser Ereignisse eintreten wird. Aber das Wichtigste wird etwas sein, das nur im Code sichtbar sein wird. Wenn Sie also etwas von dem, was ich hier zeige, verwenden wollen, sollten Sie sich die Message-Handler ansehen.
Und damit beginnen wir, das ganze System vollständig modular zu gestalten. Achten Sie von nun an genau auf den Code und auf das, was auf dem Chart zu sehen sein wird: Es ist an der Zeit, den MetaTrader 5 wie ein echter Profi zu nutzen.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #include "Macros.mqh" 05. #include "..\Defines.mqh" 06. #include "Interprocess.mqh" 07. //+------------------------------------------------------------------+ 08. class C_Terminal 09. { 10. //+------------------------------------------------------------------+ 11. protected: 12. enum eErrUser {ERR_Unknown, ERR_FileAcess, ERR_PointerInvalid, ERR_NoMoreInstance}; 13. //+------------------------------------------------------------------+ 14. struct st_Terminal
Code aus der Datei C_Terminal.mqh
Schauen Sie sich Zeile 5 genau an, in der der Speicherort der oben erwähnten Header-Datei Defines.mqh relativ zur Header-Datei C_Terminal.mqh angegeben ist. Aber das ist nicht alles, was ich Ihnen ans Herz legen möchte. Beachten Sie, dass Zeile 6 aus dem Code entfernt wurde. Das bedeutet, dass Sie die Header-Datei InterProcess.mqh jetzt aus dem Projekt entfernen können, da sie nicht mehr verwendet wird.
Da diese Änderung recht einfach ist und keine weiteren Änderungen am Code der Datei C_Terminal.mqh vorgenommen wurden, sehe ich keine Notwendigkeit, die gesamte Datei zu duplizieren. Es gibt zwar keine größeren Änderungen, aber eine muss erwähnt werden, da es sonst zu Problemen bei der Kompilierung der angezeigten Codes kommen wird.
Zeile 12 enthält eine Enumeration, der ein neuer Wert zugewiesen wurde. Es sollte während des Tests verwendet werden, um zu prüfen, ob derselbe Indikator bereits auf dem Chart vorhanden ist. Daher ist es notwendig, eine weitere kleine Änderung an der gleichen Header-Datei C_Terminal.mqh vorzunehmen. Diese Änderung ist im folgenden Code dargestellt.
157. //+------------------------------------------------------------------+ 158. bool IndicatorCheckPass(const string szShortName) 159. { 160. string szTmp = szShortName + "_TMP"; 161. 162. if (_LastError != ERR_SUCCESS) return false; 163. IndicatorSetString(INDICATOR_SHORTNAME, szTmp); 164. if (ChartWindowFind(m_Infos.ID, szShortName) != -1) 165. { 166. ChartIndicatorDelete(m_Infos.ID, 0, szTmp); 167. Print("Only one instance is allowed..."); 168. SetUserError(C_Terminal::ERR_NoMoreInstance); 169. 170. return false; 171. } 172. IndicatorSetString(INDICATOR_SHORTNAME, szShortName); 173. ResetLastError(); 174. 175. return true; 176. } 177. //+------------------------------------------------------------------+
Der Pfad, der in der Datei C_Terminal.mqh geändert werden muss
Wir müssen die ursprüngliche Funktion in der Datei C_Terminal.mqh durch den in Fragment 2 dargestellten Code ersetzen. Daher wird das System während der Prüfung die Konstante _LastError korrekt setzen, was anzeigt, dass ein Fehler aufgetreten ist. Dieser Fehler wurde durch das Vorhandensein einer anderen Instanz desselben Indikators im Chart verursacht. Abgesehen von diesen beiden einfachen Änderungen wurden keine weiteren Modifikationen vorgenommen, sodass wir mit der Entwicklung der Mauszeigerklassen beginnen können.
Nachstehend finden Sie den vollständigen Code für die Header-Datei C_Mouse.mqh. Da es für einige schwierig sein kann, den Code richtig zu verstehen, werde ich kurz erklären, was vor sich geht. Dann können Sie besser erkennen, wie alles zu einem Baukasten wird.
001. //+------------------------------------------------------------------+ 002. #property copyright "Daniel Jose" 003. //+------------------------------------------------------------------+ 004. #include "C_Terminal.mqh" 005. //+------------------------------------------------------------------+ 006. #define def_MousePrefixName "MouseBase_" 007. #define def_NameObjectLineH def_MousePrefixName + "H" 008. #define def_NameObjectLineV def_MousePrefixName + "TV" 009. #define def_NameObjectLineT def_MousePrefixName + "TT" 010. #define def_NameObjectStudy def_MousePrefixName + "TB" 011. //+------------------------------------------------------------------+ 012. class C_Mouse : public C_Terminal 013. { 014. public : 015. enum eStatusMarket {eCloseMarket, eAuction, eInTrading, eInReplay}; 016. enum eBtnMouse {eKeyNull = 0x00, eClickLeft = 0x01, eClickRight = 0x02, eSHIFT_Press = 0x04, eCTRL_Press = 0x08, eClickMiddle = 0x10}; 017. struct st_Mouse 018. { 019. struct st00 020. { 021. int X_Adjusted, 022. Y_Adjusted, 023. X_Graphics, 024. Y_Graphics; 025. double Price; 026. datetime dt; 027. }Position; 028. uint ButtonStatus; 029. bool ExecStudy; 030. datetime TimeDevice; 031. }; 032. //+------------------------------------------------------------------+ 033. protected: 034. //+------------------------------------------------------------------+ 035. void CreateObjToStudy(int x, int w, string szName, color backColor = clrNONE) const 036. { 037. if (m_Mem.szShortName != NULL) return; 038. CreateObjectGraphics(szName, OBJ_BUTTON, clrNONE); 039. ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_STATE, true); 040. ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_BORDER_COLOR, clrBlack); 041. ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_COLOR, clrBlack); 042. ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_BGCOLOR, backColor); 043. ObjectSetString(GetInfoTerminal().ID, szName, OBJPROP_FONT, "Lucida Console"); 044. ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_FONTSIZE, 10); 045. ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_CORNER, CORNER_LEFT_UPPER); 046. ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_XDISTANCE, x); 047. ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_YDISTANCE, TerminalInfoInteger(TERMINAL_SCREEN_HEIGHT) + 1); 048. ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_XSIZE, w); 049. ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_YSIZE, 18); 050. } 051. //+------------------------------------------------------------------+ 052. private : 053. enum eStudy {eStudyNull, eStudyCreate, eStudyExecute}; 054. struct st01 055. { 056. st_Mouse Data; 057. color corLineH, 058. corTrendP, 059. corTrendN; 060. eStudy Study; 061. }m_Info; 062. struct st_Mem 063. { 064. bool CrossHair, 065. IsFull; 066. datetime dt; 067. string szShortName; 068. }m_Mem; 069. bool m_OK; 070. //+------------------------------------------------------------------+ 071. void GetDimensionText(const string szArg, int &w, int &h) 072. { 073. TextSetFont("Lucida Console", -100, FW_NORMAL); 074. TextGetSize(szArg, w, h); 075. h += 5; 076. w += 5; 077. } 078. //+------------------------------------------------------------------+ 079. void CreateStudy(void) 080. { 081. if (m_Mem.IsFull) 082. { 083. CreateObjectGraphics(def_NameObjectLineV, OBJ_VLINE, m_Info.corLineH); 084. CreateObjectGraphics(def_NameObjectLineT, OBJ_TREND, m_Info.corLineH); 085. ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectLineT, OBJPROP_WIDTH, 2); 086. CreateObjToStudy(0, 0, def_NameObjectStudy); 087. } 088. m_Info.Study = eStudyCreate; 089. } 090. //+------------------------------------------------------------------+ 091. void ExecuteStudy(const double memPrice) 092. { 093. double v1 = GetInfoMouse().Position.Price - memPrice; 094. int w, h; 095. 096. if (!CheckClick(eClickLeft)) 097. { 098. m_Info.Study = eStudyNull; 099. ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, true); 100. if (m_Mem.IsFull) ObjectsDeleteAll(GetInfoTerminal().ID, def_MousePrefixName + "T"); 101. }else if (m_Mem.IsFull) 102. { 103. string sz1 = StringFormat(" %." + (string)GetInfoTerminal().nDigits + "f [ %d ] %02.02f%% ", 104. MathAbs(v1), Bars(GetInfoTerminal().szSymbol, PERIOD_CURRENT, m_Mem.dt, GetInfoMouse().Position.dt) - 1, MathAbs((v1 / memPrice) * 100.0))); 105. GetDimensionText(sz1, w, h); 106. ObjectSetString(GetInfoTerminal().ID, def_NameObjectStudy, OBJPROP_TEXT, sz1); 107. ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectStudy, OBJPROP_BGCOLOR, (v1 < 0 ? m_Info.corTrendN : m_Info.corTrendP)); 108. ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectStudy, OBJPROP_XSIZE, w); 109. ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectStudy, OBJPROP_YSIZE, h); 110. ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectStudy, OBJPROP_XDISTANCE, GetInfoMouse().Position.X_Adjusted - w); 111. ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectStudy, OBJPROP_YDISTANCE, GetInfoMouse().Position.Y_Adjusted - (v1 < 0 ? 1 : h)); 112. ObjectMove(GetInfoTerminal().ID, def_NameObjectLineT, 1, GetInfoMouse().Position.dt, GetInfoMouse().Position.Price); 113. ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectLineT, OBJPROP_COLOR, (memPrice > GetInfoMouse().Position.Price ? m_Info.corTrendN : m_Info.corTrendP)); 114. } 115. m_Info.Data.ButtonStatus = eKeyNull; 116. } 117. //+------------------------------------------------------------------+ 118. public : 119. //+------------------------------------------------------------------+ 120. C_Mouse(const long id, const string szShortName) 121. :C_Terminal(id), 122. m_OK(false) 123. { 124. m_Mem.szShortName = szShortName; 125. } 126. //+------------------------------------------------------------------+ 127. C_Mouse(const long id, const string szShortName, color corH, color corP, color corN) 128. :C_Terminal(id) 129. { 130. if (!(m_OK = IndicatorCheckPass(szShortName))) SetUserError(C_Terminal::ERR_Unknown); 131. if (_LastError != ERR_SUCCESS) return; 132. m_Mem.szShortName = NULL; 133. m_Mem.CrossHair = (bool)ChartGetInteger(GetInfoTerminal().ID, CHART_CROSSHAIR_TOOL); 134. ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_MOUSE_MOVE, true); 135. ChartSetInteger(GetInfoTerminal().ID, CHART_CROSSHAIR_TOOL, false); 136. ZeroMemory(m_Info); 137. m_Info.corLineH = corH; 138. m_Info.corTrendP = corP; 139. m_Info.corTrendN = corN; 140. m_Info.Study = eStudyNull; 141. if (m_Mem.IsFull = (corP != clrNONE) && (corH != clrNONE) && (corN != clrNONE)) 142. CreateObjectGraphics(def_NameObjectLineH, OBJ_HLINE, m_Info.corLineH); 143. } 144. //+------------------------------------------------------------------+ 145. ~C_Mouse() 146. { 147. if (!m_OK) return; 148. ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, false); 149. ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_MOUSE_MOVE, false); 150. ChartSetInteger(GetInfoTerminal().ID, CHART_CROSSHAIR_TOOL, m_Mem.CrossHair); 151. ObjectsDeleteAll(GetInfoTerminal().ID, def_MousePrefixName); 152. } 153. //+------------------------------------------------------------------+ 154. inline bool CheckClick(const eBtnMouse value) 155. { 156. return (GetInfoMouse().ButtonStatus & value) == value; 157. } 158. //+------------------------------------------------------------------+ 159. inline const st_Mouse GetInfoMouse(void) 160. { 161. if (m_Mem.szShortName != NULL) 162. { 163. double Buff[]; 164. uCast_Double loc; 165. int handle = ChartIndicatorGet(GetInfoTerminal().ID, 0, m_Mem.szShortName); 166. 167. ZeroMemory(m_Info.Data); 168. if (CopyBuffer(handle, 0, 0, 6, Buff) == 6) 169. { 170. m_Info.Data.Position.Price = Buff[0]; 171. loc.dValue = Buff[1]; 172. m_Info.Data.Position.dt = loc._datetime; 173. loc.dValue = Buff[2]; 174. m_Info.Data.Position.X_Adjusted = loc._int[0]; 175. m_Info.Data.Position.Y_Adjusted = loc._int[1]; 176. loc.dValue = Buff[3]; 177. m_Info.Data.Position.X_Graphics = loc._int[0]; 178. m_Info.Data.Position.Y_Graphics = loc._int[1]; 179. loc.dValue = Buff[4]; 180. m_Info.Data.ButtonStatus = loc._char[0]; 181. m_Info.Data.TimeDevice = (datetime)Buff[5]; 182. IndicatorRelease(handle); 183. } 184. } 185. 186. return m_Info.Data; 187. } 188. //+------------------------------------------------------------------+ 189. void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam) 190. { 191. int w = 0; 192. static double memPrice = 0; 193. 194. if (m_Mem.szShortName == NULL) 195. { 196. C_Terminal::DispatchMessage(id, lparam, dparam, sparam); 197. switch (id) 198. { 199. case (CHARTEVENT_CUSTOM + evHideMouse): 200. if (m_Mem.IsFull) ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectLineH, OBJPROP_COLOR, clrNONE); 201. break; 202. case (CHARTEVENT_CUSTOM + evShowMouse): 203. if (m_Mem.IsFull) ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectLineH, OBJPROP_COLOR, m_Info.corLineH); 204. break; 205. case (CHARTEVENT_CUSTOM + evSetServerTime): 206. m_Info.Data.TimeDevice = (datetime)dparam; 207. break; 208. case CHARTEVENT_MOUSE_MOVE: 209. ChartXYToTimePrice(GetInfoTerminal().ID, m_Info.Data.Position.X_Graphics = (int)lparam, m_Info.Data.Position.Y_Graphics = (int)dparam, w, m_Info.Data.Position.dt, m_Info.Data.Position.Price); 210. if (m_Mem.IsFull) ObjectMove(GetInfoTerminal().ID, def_NameObjectLineH, 0, 0, m_Info.Data.Position.Price = AdjustPrice(m_Info.Data.Position.Price)); 211. m_Info.Data.Position.dt = AdjustTime(m_Info.Data.Position.dt); 212. ChartTimePriceToXY(GetInfoTerminal().ID, w, m_Info.Data.Position.dt, m_Info.Data.Position.Price, m_Info.Data.Position.X_Adjusted, m_Info.Data.Position.Y_Adjusted); 213. if ((m_Info.Study != eStudyNull) && (m_Mem.IsFull)) ObjectMove(GetInfoTerminal().ID, def_NameObjectLineV, 0, m_Info.Data.Position.dt, 0); 214. m_Info.Data.ButtonStatus = (uint) sparam; 215. if (CheckClick(eClickMiddle)) 216. if ((!m_Mem.IsFull) || ((color)ObjectGetInteger(GetInfoTerminal().ID, def_NameObjectLineH, OBJPROP_COLOR) != clrNONE)) CreateStudy(); 217. if (CheckClick(eClickLeft) && (m_Info.Study == eStudyCreate)) 218. { 219. ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, false); 220. if (m_Mem.IsFull) ObjectMove(GetInfoTerminal().ID, def_NameObjectLineT, 0, m_Mem.dt = GetInfoMouse().Position.dt, memPrice = GetInfoMouse().Position.Price); 221. m_Info.Study = eStudyExecute; 222. } 223. if (m_Info.Study == eStudyExecute) ExecuteStudy(memPrice); 224. m_Info.Data.ExecStudy = m_Info.Study == eStudyExecute; 225. break; 226. case CHARTEVENT_OBJECT_DELETE: 227. if ((m_Mem.IsFull) && (sparam == def_NameObjectLineH)) CreateObjectGraphics(def_NameObjectLineH, OBJ_HLINE, m_Info.corLineH); 228. break; 229. } 230. } 231. } 232. //+------------------------------------------------------------------+ 233. }; 234. //+------------------------------------------------------------------+ 235. #undef def_NameObjectLineV 236. #undef def_NameObjectLineH 237. #undef def_NameObjectLineT 238. #undef def_NameObjectStudy 239. //+------------------------------------------------------------------+
Quellcode der Datei C_Mouse.mqh
Im Gegensatz zu früheren Versionen wird jetzt nur noch die Datei C_Terminal.mqh als notwendig erachtet. Sie sollten jedoch auf andere Details achten, die in dieser Klasse geändert wurden, wie oben gezeigt.
Wie Sie sehen, haben wir jetzt eine neue Variable in Zeile 30. Sie ist nur sinnvoll, wenn der Replay/Simulator-Dienst verwendet wird. Über diese Variable haben wir Zugriff auf den vom Replay/Simulator-Dienst bereitgestellten Wert, der zuvor über die globale Terminal-Variable abgerufen wurde. Obwohl diese Variable in Zeile 30 deklariert wird, wird sie an anderer Stelle verwendet. Aber wegen eines anderen Problems, das wir später erklären werden, mussten wir es hier hinzufügen, in der Klasse C_Mouse.
Wie Sie sehen können, bleibt der größte Teil des Codes identisch mit dem vorherigen, bis zu Zeile 168, wo wir etwas anderes finden. Jetzt werden wir sechs Pufferpositionen verwenden. Die sechste Position sollte von der in Zeile 30 deklarierten Variablen belegt werden. In Zeile 181 geben wir also den Wert ein, wenn wir ihn lesen und herausfinden müssen, wie hoch der Wert dieser Variablen ist. Hier muss ich kurz innehalten und etwas erklären. Die einzige Prozedur, die den Inhalt der in Zeile 30 deklarierten Variablen tatsächlich verwendet, ist der Replay/Simulator-Dienst und der Mausanzeiger. Letzterer verwendet diesen Wert, um den Nutzer über die verbleibende Zeit bis zur Eröffnung des nächsten Taktes zu informieren. Wenn wir sie jedoch nicht verwenden wollen, können wir sie aus dem Mausanzeiger oder dem Replay-/Simulationsdienst entfernen. Es wird keine Probleme geben, wenn Sie dies tun. Wenn Sie diese Informationen verwenden wollen, sollten Sie sich darüber im Klaren sein, dass der Dienst nicht weiß, wann diese Daten wieder aktualisiert werden sollen, es sei denn, wir verwenden eine globale Terminalvariable oder ein anderes Mittel, um den Zeitwert im Replay-/Simulationsdienst zu speichern.
Aus diesem Grund legen wir den Wert in den Mauszeigerpuffer. Aber dann denkt man: „Wird dieser Wert nicht gelöscht und zurückgesetzt, wenn MetaTrader 5 den Indikator im Chart wiederherstellt, nachdem der Nutzer den Chart-Zeitrahmen geändert hat?“ Dann macht es keinen Sinn, sie im Indikatorpuffer zu speichern. Das ist genau das, was wir wollen. Wenn der Dienst feststellt, dass der Wert im Puffer nicht mehr gültig ist, sendet er ein Ereignis an den Mauszeiger, um den Wert erneut zu aktualisieren. Auf diese Weise wird alles so bleiben, wie wir es erwarten.
Das heißt, die Tatsache, dass MetaTrader 5 den Indikator zwingt, den Puffer auf Null zu setzen, gibt dem Replay/Simulator-Dienst zu verstehen, dass der Nutzer eine Änderung vorgenommen hat, die vom Replay/Simulator-Dienst neu bewertet werden muss. Auf diese Weise wird unsere Anwendung leicht erkennen, dass etwas passiert ist, und MetaTrader 5 wird alles für uns tun.
Aber die Frage wird noch interessanter, wenn wir uns den Code ab Zeile 189 ansehen, wo sich der Message-Handler der Klasse C_Mouse befindet.
In diesem Verfahren zur Behandlung von Nachrichten haben wir die ersten drei Nachrichten, mit deren Verarbeitung unser modulares System beginnen wird. Achten Sie also darauf, was passiert. Um sicherzustellen, dass die Nachrichtenbehandlung durch den Indikator und nicht durch einen anderen Code erfolgt, der die Klasse verwendet, müssen wir zunächst prüfen, ob wir genau den Indikator aufrufen. Diese Prüfung wird in Zeile 194 durchgeführt. Wenn die Prüfung erfolgreich ist, gehen wir davon aus, dass es sich um einen Indikator handelt, sodass wir nur einen Mausindikator auf dem Chart haben müssen, um einen möglichen Interessenkonflikt zu vermeiden. Ich habe dies bereits in anderen Artikeln behandelt, also lassen Sie uns fortfahren.
In Zeile 199 behandeln wir das Ereignis, bei dem der Mausindikator die als Preislinie verwendete Linie ausblenden soll.
In Zeile 202 behandeln wir das Ereignis, das dem Mausindikator mitteilt, dass die Kurslinie wieder im Chart angezeigt werden soll. Auf diese Weise kann jede Anwendung dem Mauszeiger mitteilen, wann er die von ihm verwendete Kurslinie ein- oder ausblenden soll.
In beiden Ereignissen müssen keine zusätzlichen Parameter angegeben werden, sodass jede Anwendung, die die Mauslinie ein- oder ausblenden möchte, lediglich ein nutzerdefiniertes Ereignis mit den angegebenen Werten erzeugen und dieses Ereignis an das Chart leiten muss, in dem sich der Mauszeiger befindet. Ich werde später mehr dazu sagen. Fürs Erste können wir es so verstehen: Wenn zu einem bestimmten Zeitpunkt ein Mausindikator im Chart erscheint und Sie die Mauslinie ausblenden möchten, müssen Sie ein nutzerdefiniertes Ereignis mit dem gewünschten Wert erzeugen, um die Mauslinie ein- oder auszublenden. Dieses Ereignis kann z.B. durch einen Expert Advisor ausgelöst werden.
Das dritte Ereignis, das wir auch hier implementieren werden, ist in Zeile 205 dargestellt. In diesem Fall geben wir den Wert an, der als Laufzeit des Dienstes platziert werden soll, d.h. es ist der Replay/Simulator-Dienst, der dies tatsächlich tun wird. Denn nur der Dienst kann einen Vorteil aus der Erzeugung eines solchen Ereignisses ziehen. Aber es gibt einen wichtigen Punkt: Wenn dieses Ereignis ausgelöst wird, muss es einen Zeitwert angeben. Dieser Wert muss in dem Parameter „dparam“ vom Typ „double“ enthalten sein.
Auch hier müssen Sie die Dinge aus einer breiteren Perspektive betrachten. Ein Double-Wert besteht aus 8 Bytes, genau wie ein Datetime-Wert, der ebenfalls aus 8 Bytes besteht. Wir führen also eine Typumwandlung durch, damit der Compiler versteht, was wir tun. Aber für den Prozessor ist alles, was wir tun, 8 Bytes in eine Variable zu stecken. Der Inhalt dieser Bytes spielt keine Rolle.
Es ist wichtig, dies zu verstehen, denn es gibt Situationen, in denen wir ganze Zeichenketten von Werten, manchmal auch ganze Strukturen, übergeben müssen, und da wir in MetaTrader 5, oder genauer gesagt in MQL5, keine Zeiger wie in C/C++ verwenden können, brauchen wir einen Trick, um diese Daten zwischen unseren in MetaTrader 5 laufenden Anwendungen zu übergeben.
Okay, der erste Teil der Arbeit ist erledigt. Aber wir werden die Dinge noch verbessern. Wenn Sie gut aufgepasst haben, ist Ihnen wahrscheinlich aufgefallen, dass es noch andere Ereignisse gibt, die mit dem Mauszeiger verbunden sind. Diese Ereignisse befinden sich jedoch nicht in der Header-Datei C_Mouse.mqh, sondern in der Datei C_Study.mqh. Um diese Ereignisse zu sehen und zu verstehen, was passieren wird, sehen wir uns diesen Code an. Der vollständige Code ist nachstehend aufgeführt:
001. //+------------------------------------------------------------------+ 002. #property copyright "Daniel Jose" 003. //+------------------------------------------------------------------+ 004. #include "..\C_Mouse.mqh" 005. //+------------------------------------------------------------------+ 006. #define def_ExpansionPrefix def_MousePrefixName + "Expansion_" 007. #define def_ExpansionBtn1 def_ExpansionPrefix + "B1" 008. #define def_ExpansionBtn2 def_ExpansionPrefix + "B2" 009. #define def_ExpansionBtn3 def_ExpansionPrefix + "B3" 010. //+------------------------------------------------------------------+ 011. class C_Study : public C_Mouse 012. { 013. private : 014. //+------------------------------------------------------------------+ 015. struct st00 016. { 017. eStatusMarket Status; 018. MqlRates Rate; 019. string szInfo; 020. color corP, 021. corN; 022. int HeightText; 023. bool bvT, bvD, bvP; 024. }m_Info; 025. //+------------------------------------------------------------------+ 026. const datetime GetBarTime(void) 027. { 028. datetime dt; 029. int i0 = PeriodSeconds(); 030. 031. if (m_Info.Status == eInReplay) 032. { 033. if ((dt = GetInfoMouse().TimeDevice) == ULONG_MAX) return ULONG_MAX; 034. }else dt = TimeCurrent(); 035. if (m_Info.Rate.time <= dt) 036. m_Info.Rate.time = (datetime)(((ulong) dt / i0) * i0) + i0; 037. 038. return m_Info.Rate.time - dt; 039. } 040. //+------------------------------------------------------------------+ 041. void Draw(void) 042. { 043. double v1; 044. 045. if (m_Info.bvT) 046. { 047. ObjectSetInteger(GetInfoTerminal().ID, def_ExpansionBtn1, OBJPROP_YDISTANCE, GetInfoMouse().Position.Y_Adjusted - 18); 048. ObjectSetString(GetInfoTerminal().ID, def_ExpansionBtn1, OBJPROP_TEXT, m_Info.szInfo); 049. } 050. if (m_Info.bvD) 051. { 052. v1 = NormalizeDouble(100.0 - ((m_Info.Rate.close / GetInfoMouse().Position.Price) * 100.0), 2); 053. ObjectSetInteger(GetInfoTerminal().ID, def_ExpansionBtn2, OBJPROP_YDISTANCE, GetInfoMouse().Position.Y_Adjusted - 1); 054. ObjectSetInteger(GetInfoTerminal().ID, def_ExpansionBtn2, OBJPROP_BGCOLOR, (v1 < 0 ? m_Info.corN : m_Info.corP)); 055. ObjectSetString(GetInfoTerminal().ID, def_ExpansionBtn2, OBJPROP_TEXT, StringFormat("%.2f%%", MathAbs(v1))); 056. } 057. if (m_Info.bvP) 058. { 059. v1 = NormalizeDouble(100.0 - ((m_Info.Rate.close / iClose(GetInfoTerminal().szSymbol, PERIOD_D1, 0)) * 100.0), 2); 060. ObjectSetInteger(GetInfoTerminal().ID, def_ExpansionBtn3, OBJPROP_YDISTANCE, GetInfoMouse().Position.Y_Adjusted - 1); 061. ObjectSetInteger(GetInfoTerminal().ID, def_ExpansionBtn3, OBJPROP_BGCOLOR, (v1 < 0 ? m_Info.corN : m_Info.corP)); 062. ObjectSetString(GetInfoTerminal().ID, def_ExpansionBtn3, OBJPROP_TEXT, StringFormat("%.2f%%", MathAbs(v1))); 063. } 064. } 065. //+------------------------------------------------------------------+ 066. inline void CreateObjInfo(EnumEvents arg) 067. { 068. switch (arg) 069. { 070. case evShowBarTime: 071. C_Mouse::CreateObjToStudy(2, 110, def_ExpansionBtn1, clrPaleTurquoise); 072. m_Info.bvT = true; 073. break; 074. case evShowDailyVar: 075. C_Mouse::CreateObjToStudy(2, 53, def_ExpansionBtn2); 076. m_Info.bvD = true; 077. break; 078. case evShowPriceVar: 079. C_Mouse::CreateObjToStudy(58, 53, def_ExpansionBtn3); 080. m_Info.bvP = true; 081. break; 082. } 083. } 084. //+------------------------------------------------------------------+ 085. inline void RemoveObjInfo(EnumEvents arg) 086. { 087. string sz; 088. 089. switch (arg) 090. { 091. case evHideBarTime: 092. sz = def_ExpansionBtn1; 093. m_Info.bvT = false; 094. break; 095. case evHideDailyVar: 096. sz = def_ExpansionBtn2; 097. m_Info.bvD = false; 098. break; 099. case evHidePriceVar: 100. sz = def_ExpansionBtn3; 101. m_Info.bvP = false; 102. break; 103. } 104. ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, false); 105. ObjectDelete(GetInfoTerminal().ID, sz); 106. ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, true); 107. } 108. //+------------------------------------------------------------------+ 109. public : 110. //+------------------------------------------------------------------+ 111. C_Study(long IdParam, string szShortName, color corH, color corP, color corN) 112. :C_Mouse(IdParam, szShortName, corH, corP, corN) 113. { 114. if (_LastError != ERR_SUCCESS) return; 115. ZeroMemory(m_Info); 116. m_Info.Status = eCloseMarket; 117. m_Info.Rate.close = iClose(GetInfoTerminal().szSymbol, PERIOD_D1, ((GetInfoTerminal().szSymbol == def_SymbolReplay) || (macroGetDate(TimeCurrent()) != macroGetDate(iTime(GetInfoTerminal().szSymbol, PERIOD_D1, 0))) ? 0 : 1)); 118. m_Info.corP = corP; 119. m_Info.corN = corN; 120. CreateObjInfo(evShowBarTime); 121. CreateObjInfo(evShowDailyVar); 122. CreateObjInfo(evShowPriceVar); 123. } 124. //+------------------------------------------------------------------+ 125. void Update(const eStatusMarket arg) 126. { 127. datetime dt; 128. 129. switch (m_Info.Status = (m_Info.Status != arg ? arg : m_Info.Status)) 130. { 131. case eCloseMarket : 132. m_Info.szInfo = "Closed Market"; 133. break; 134. case eInReplay : 135. case eInTrading : 136. if ((dt = GetBarTime()) < ULONG_MAX) 137. { 138. m_Info.szInfo = TimeToString(dt, TIME_SECONDS); 139. break; 140. } 141. case eAuction : 142. m_Info.szInfo = "Auction"; 143. break; 144. default : 145. m_Info.szInfo = "ERROR"; 146. } 147. Draw(); 148. } 149. //+------------------------------------------------------------------+ 150. virtual void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam) 151. { 152. C_Mouse::DispatchMessage(id, lparam, dparam, sparam); 153. switch (id) 154. { 155. case CHARTEVENT_CUSTOM + evHideBarTime: 156. RemoveObjInfo(evHideBarTime); 157. break; 158. case CHARTEVENT_CUSTOM + evShowBarTime: 159. CreateObjInfo(evShowBarTime); 160. break; 161. case CHARTEVENT_CUSTOM + evHideDailyVar: 162. RemoveObjInfo(evHideDailyVar); 163. break; 164. case CHARTEVENT_CUSTOM + evShowDailyVar: 165. CreateObjInfo(evShowDailyVar); 166. break; 167. case CHARTEVENT_CUSTOM + evHidePriceVar: 168. RemoveObjInfo(evHidePriceVar); 169. break; 170. case CHARTEVENT_CUSTOM + evShowPriceVar: 171. CreateObjInfo(evShowPriceVar); 172. break; 173. case CHARTEVENT_MOUSE_MOVE: 174. Draw(); 175. break; 176. } 177. ChartRedraw(GetInfoTerminal().ID); 178. } 179. //+------------------------------------------------------------------+ 180. }; 181. //+------------------------------------------------------------------+ 182. #undef def_ExpansionBtn3 183. #undef def_ExpansionBtn2 184. #undef def_ExpansionBtn1 185. #undef def_ExpansionPrefix 186. #undef def_MousePrefixName 187. //+------------------------------------------------------------------+
Quellcode der Datei C_Study.mqh
Im Gegensatz zu C_Mouse.mqh gibt es hier viele Unterschiede. Beginnen wir mit der Tatsache, dass wir neue Variablen, Prüfungen und andere Dinge haben, die jetzt implementiert werden. Da die meisten von ihnen jedoch einfach sind, werden wir uns nur mit einigen der „ungewöhnlichsten“ befassen. Dazu gehört auch Zeile 33, in der wir sehen, dass die Funktion für den Zugriff auf die globale Variable im Terminal nicht mehr verwendet wird. Jetzt fragen wir den Mauszeiger nach etwas, das zuvor in der globalen Variable des Terminals gesucht wurde. Das ist genau die Art und Weise, wie wir Dinge tun wollen: Wir wollen Dinge tun, ohne etwas zu nutzen, das der Nutzer kontrollieren kann und ohne auf externe Programmierung zurückzugreifen. Die Idee ist, alles in reinem MQL5 zu implementieren.
Wenn man sich den Code ansieht, ist es vielleicht nicht ganz klar, dass unsere Implementierung viel mehr Einstellungen hat als die vorherige. Wir haben dies getan, um unseren Mauszeiger zu einer Art Standard zu machen, der in anderen Anwendungen auf der Plattform verwendet werden kann. Hier gibt es einen wichtigen Punkt: Als Programmierer können Sie Nachrichten an den Mauszeiger senden, um etwas ein- oder auszuschalten, das bereits in ihm definiert ist.
Dazu müssen wir bestimmte Teile des Codes isolieren, damit das Objekt, auf das wir zugreifen, die gewünschten Änderungen erfährt, wenn ein Ereignis eintritt, bei dem etwas ein- oder ausgeschaltet werden muss. In diesem Fall geht es um etwas Einfaches, z. B. darum, wie etwas im Chart angezeigt oder nicht angezeigt werden kann. Nun, es hätte auch etwas noch Komplizierteres sein können. Der Freiheitsgrad, den wir nach und nach einführen, macht viele Dinge möglich. Um sie zu implementieren, müssen Sie nur geringfügige Änderungen an dem vornehmen, was bereits fertig ist. Dies bedeutet auch, dass das Sicherheitsniveau und die Zuverlässigkeit der Anwendung immer auf dem höchstmöglichen Niveau bleiben.
Der Großteil des Codes in der Datei C_Study.mqh tut genau dies. Aber jetzt sind wir wirklich daran interessiert, was ab Zeile 150 passiert, wo wir das implementieren werden, was in dieser Header-Datei beschrieben ist. Beachten Sie, dass die ausstehenden Teile, die wir hier nicht behandeln wollen, an die Klasse C_Mouse übergeben werden, damit dort Ereignisse behandelt werden können. Dies ist aus Zeile 152 ersichtlich.
Achten Sie jetzt auf eine Sache. Jedes der Nutzerereignisse, die in dieser Ereignisbehandlung zu sehen sind, schaltet etwas in der Mausanzeige ein oder aus, sodass sie ein wenig anders aussieht, aber dies geschieht zur Laufzeit, ohne dass der Nutzer den Code neu kompilieren oder eine große Liste von Optionen konfigurieren muss. Bitte beachten Sie diese Tatsache. Sie können einfach kleine Skriptdateien erstellen, die nutzerdefinierte Ereignisse an den Mauszeiger senden, sodass sich sein Aussehen während der Verwendung ändert.
Wenn Sie kreativ genug sind, werden Sie schon beim Anblick dieses Codes und seiner Funktionsweise an verschiedene Tricks denken und diese planen. Aber ich rate Ihnen, nichts zu überstürzen, denn zum jetzigen Zeitpunkt ist noch nicht ganz klar, in welche Richtung sich das Replay/Simulator-System entwickeln wird. Der Grund dafür ist, dass die Umwandlung verschiedener Elemente in Module eine Vielzahl von Möglichkeiten eröffnet. Und die Art und Weise, wie das System wachsen kann, zwingt uns dazu, vorsichtig zu sein, wenn wir über neue Möglichkeiten nachdenken. Aber im Moment konzentriere ich mich darauf, das System ohne die Verwendung von globalen Terminalvariablen zum Laufen zu bringen, während ich immer noch die gleichen Eigenschaften beibehalte, die es hatte, als die Module zum ersten Mal erstellt wurden.
Also gut. Nun, da dies alles gezeigt wird, können wir mit dem Code fortfahren, der den Mauszeiger tatsächlich erstellt. Sie können es unten sehen.
001. //+------------------------------------------------------------------+ 002. #property copyright "Daniel Jose" 003. #property description "This is an indicator for graphical studies using the mouse." 004. #property description "This is an integral part of the Replay / Simulator system." 005. #property description "However it can be used in the real market." 006. #property version "1.54" 007. #property icon "/Images/Market Replay/Icons/Indicators.ico" 008. #property link "https://www.mql5.com/pt/articles/11971" 009. #property indicator_chart_window 010. #property indicator_plots 0 011. #property indicator_buffers 1 012. //+------------------------------------------------------------------+ 013. #include <Market Replay\Auxiliar\Study\C_Study.mqh> 014. //+------------------------------------------------------------------+ 015. C_Study *Study = NULL; 016. //+------------------------------------------------------------------+ 017. input long user00 = 0; //ID 018. input C_Study::eStatusMarket user01 = C_Study::eAuction; //Market Status 019. input color user02 = clrBlack; //Price Line 020. input color user03 = clrPaleGreen; //Positive Study 021. input color user04 = clrLightCoral; //Negative Study 022. //+------------------------------------------------------------------+ 023. C_Study::eStatusMarket m_Status; 024. int m_posBuff = 0; 025. double m_Buff[]; 026. //+------------------------------------------------------------------+ 027. int OnInit() 028. { 029. ResetLastError(); 030. Study = new C_Study(user00, "Indicator Mouse Study", user02, user03, user04); 031. if (_LastError != ERR_SUCCESS) return INIT_FAILED; 032. if ((*Study).GetInfoTerminal().szSymbol != def_SymbolReplay) 033. { 034. MarketBookAdd((*Study).GetInfoTerminal().szSymbol); 035. OnBookEvent((*Study).GetInfoTerminal().szSymbol); 036. m_Status = C_Study::eCloseMarket; 037. }else 038. m_Status = user01; 039. SetIndexBuffer(0, m_Buff, INDICATOR_DATA); 040. ArrayInitialize(m_Buff, EMPTY_VALUE); 041. 042. return INIT_SUCCEEDED; 043. } 044. //+------------------------------------------------------------------+ 045. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 046. { 047. m_posBuff = rates_total - 6; 048. (*Study).Update(m_Status); 049. 050. return rates_total; 051. } 052. //+------------------------------------------------------------------+ 053. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 054. { 055. (*Study).DispatchMessage(id, lparam, dparam, sparam); 056. SetBuffer(); 057. 058. ChartRedraw((*Study).GetInfoTerminal().ID); 059. } 060. //+------------------------------------------------------------------+ 061. void OnBookEvent(const string &symbol) 062. { 063. MqlBookInfo book[]; 064. C_Study::eStatusMarket loc = m_Status; 065. 066. if (symbol != (*Study).GetInfoTerminal().szSymbol) return; 067. MarketBookGet((*Study).GetInfoTerminal().szSymbol, book); 068. m_Status = (ArraySize(book) == 0 ? C_Study::eCloseMarket : C_Study::eInTrading); 069. for (int c0 = 0; (c0 < ArraySize(book)) && (m_Status != C_Study::eAuction); c0++) 070. if ((book[c0].type == BOOK_TYPE_BUY_MARKET) || (book[c0].type == BOOK_TYPE_SELL_MARKET)) m_Status = C_Study::eAuction; 071. if (loc != m_Status) (*Study).Update(m_Status); 072. } 073. //+------------------------------------------------------------------+ 074. void OnDeinit(const int reason) 075. { 076. if (reason != REASON_INITFAILED) 077. { 078. if ((*Study).GetInfoTerminal().szSymbol != def_SymbolReplay) 079. MarketBookRelease((*Study).GetInfoTerminal().szSymbol); 080. } 081. delete Study; 082. } 083. //+------------------------------------------------------------------+ 084. inline void SetBuffer(void) 085. { 086. uCast_Double Info; 087. 088. m_posBuff = (m_posBuff < 0 ? 0 : m_posBuff); 089. m_Buff[m_posBuff + 0] = (*Study).GetInfoMouse().Position.Price; 090. Info._datetime = (*Study).GetInfoMouse().Position.dt; 091. m_Buff[m_posBuff + 1] = Info.dValue; 092. Info._int[0] = (*Study).GetInfoMouse().Position.X_Adjusted; 093. Info._int[1] = (*Study).GetInfoMouse().Position.Y_Adjusted; 094. m_Buff[m_posBuff + 2] = Info.dValue; 095. Info._int[0] = (*Study).GetInfoMouse().Position.X_Graphics; 096. Info._int[1] = (*Study).GetInfoMouse().Position.Y_Graphics; 097. m_Buff[m_posBuff + 3] = Info.dValue; 098. Info._char[0] = ((*Study).GetInfoMouse().ExecStudy == C_Mouse::eStudyNull ? (char)(*Study).GetInfoMouse().ButtonStatus : 0); 099. m_Buff[m_posBuff + 4] = Info.dValue; 100. m_Buff[m_posBuff + 5] = (double)(*Study).GetInfoMouse().TimeDevice; 101. } 102. //+------------------------------------------------------------------+
Quellcode des Mauszeigers
Der Kodex hat keine wesentlichen Änderungen erfahren. Es gibt nur einen neuen Zusatz, der in Zeile 100 zu sehen ist, wo die Daten in den Indikatorpuffer gestellt werden. Auch diese Daten sind nur in der Anfangsphase für den Replay/Simulator-Dienst nützlich. Es hat keine andere Verwendung. Zumindest habe ich keine anderen bemerkt.
Schlussfolgerung
Damit Sie besser verstehen, was hier vor sich geht, füge ich die ausführbare Datei des Indikators sowie einige Skripte bei, mit denen der Indikator im Chart geändert werden kann. Es gibt aber auch diejenigen, die ein bereits kompiliertes Programm nicht auf ihrer Plattform ausführen wollen. Nun, ich verstehe warum. Im Video am Ende dieses Artikels können Sie sehen, was passiert, wenn Skripte ausgeführt werden.
Bitte beachten Sie, dass dies nur ein kleiner Teil dessen ist, was wir zu leisten imstande sind. Diejenigen, die eine engere Sichtweise haben, werden sagen, dass dies Unsinn ist und anders hätte gemacht werden können, aber ich werde ihnen keine Beachtung schenken. Ich sehe lieber das Funkeln in den Augen derjenigen, die über die Möglichkeiten dessen nachdenken, was ich zeige: was aus diesem modularen System werden kann und wie sich alles weiter entwickeln wird. Für sie schreibe ich diese Artikel. Darin zeige ich, dass viele nur an der Oberfläche dessen kratzen, was wir mit MQL5 tatsächlich tun können, und dass MetaTrader 5 tatsächlich eine großartige Plattform ist, die in den richtigen Händen unglaubliche Dinge leisten kann.
Ich entschuldige mich jedoch nochmals bei denjenigen, die keine Programmierkenntnisse haben. Es tut mir leid, aber ohne richtige Kenntnisse über das, was hier gezeigt wurde, wird der Zugang zum Quellcode dieses Replay/Simulator-Dienstes zu etwas Gefährlichem. Aber ich verspreche, Sie mit dem Problem nicht allein zu lassen, ich werde Ihnen die ausführbare Datei zur Verfügung stellen, sobald die Modulversion fertig ist und stabil wird, und werde die ausführbare Datei hier in den Artikeln bereitstellen. Wenn Sie jedoch den Quellcode kompilieren möchten, ist dieser immer im Artikel verfügbar. Ich weiß jedoch, dass Sie ohne die entsprechenden Kenntnisse nicht in der Lage sein werden, die für die Kompilierung des Systems erforderliche Verzeichnisstruktur zu erstellen.
Das ist genau das, was ich tun wollte, weil ich aus eigener Erfahrung gesehen habe, dass in früheren Artikeln Leute versucht haben, etwas zusammenzustellen, ohne zu verstehen, worüber sie reden. Solche Dinge sind nicht nur riskant, sondern auch gefährlich. Man sollte Dinge nicht nutzen, ohne ihren Zweck zu verstehen.
Video 01 - Demonstration der Funktionsweise des Moduls
Übersetzt aus dem Portugiesischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/pt/articles/11971





- 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.