
Entwicklung eines Replay-Systems (Teil 75): Neuer Chart-Handel (II)
Einführung
Im vorherigen Artikel Entwicklung eines Replay-Systems (Teil 74): Neuer Chart-Handel (I) habe ich mich in erster Linie darauf konzentriert, den Code für den Chart Trade Indikator zu erklären. Wir haben erörtert, warum Sie bei der Programmierung des Chart Trades einen Ansatz dem anderen vorziehen sollten. Wir haben auch verschiedene Aspekte des Codes behandelt. Allerdings habe ich einen wichtigen Teil ausgelassen: den Kernel-Code selbst.
Einer der Gründe, warum ich den Hauptcode nicht früher aufgenommen habe, ist, dass über 100 Zeilen daraus entfernt wurden. Also musste ich mir eine einfache Methode ausdenken, um zu zeigen, was sich im Code geändert hat. Der beste Ansatz, den ich gefunden habe, ist der, den ich in diesem Artikel vorstelle.
Im Folgenden werden wir uns den Hauptcode für den Chart Trade genauer ansehen. Beachten Sie, dass Sie diesen Code auf Wunsch direkt in einen Expert Advisor einbetten können. Allerdings sind dafür einige Anpassungen erforderlich, die ich heute erläutern werde. Vergessen Sie nicht die Gründe, die im vorangegangenen Artikel besprochen wurden, warum der Code in einem Indikator und nicht in einem Expert Advisor platziert wird. Es steht Ihnen jedoch frei, den Code so zu verwenden, wie es Ihren Bedürfnissen am besten entspricht.
Also, ohne weitere Verzögerung, fangen wir an.
Verstehen des Quellcodes der Klasse C_ChartFloatingRAD
Der Quellcode für die Klasse C_ChartFloatingRAD befindet sich in der gleichnamigen Header-Datei. Im Folgenden finden Sie fast den gesamten Code für diese Klasse - alles außer der Prozedur DispatchMessage.
001. //+------------------------------------------------------------------+ 002. #property copyright "Daniel Jose" 003. //+------------------------------------------------------------------+ 004. #include "../Auxiliar/C_Mouse.mqh" 005. #include "C_AdjustTemplate.mqh" 006. //+------------------------------------------------------------------+ 007. #define macro_NameGlobalVariable(A) StringFormat("ChartTrade_%u%s", GetInfoTerminal().ID, A) 008. #define macro_CloseIndicator(A) { \ 009. OnDeinit(REASON_INITFAILED); \ 010. SetUserError(A); \ 011. return; \ 012. } 013. //+------------------------------------------------------------------+ 014. class C_ChartFloatingRAD : private C_Terminal 015. { 016. private : 017. enum eObjectsIDE {MSG_LEVERAGE_VALUE, MSG_TAKE_VALUE, MSG_STOP_VALUE, MSG_MAX_MIN, MSG_TITLE_IDE, MSG_DAY_TRADE, MSG_BUY_MARKET, MSG_SELL_MARKET, MSG_CLOSE_POSITION, MSG_NULL}; 018. struct st00 019. { 020. short x, y, minx, miny, 021. Leverage; 022. string szObj_Chart, 023. szObj_Editable, 024. szFileNameTemplate; 025. long WinHandle; 026. double FinanceTake, 027. FinanceStop; 028. bool IsMaximized, 029. IsDayTrade, 030. IsSaveState; 031. struct st01 032. { 033. short x, y, w, h; 034. color bgcolor; 035. int FontSize; 036. string FontName; 037. }Regions[MSG_NULL]; 038. }m_Info; 039. C_Mouse *m_Mouse; 040. //+------------------------------------------------------------------+ 041. void CreateWindowRAD(int w, int h) 042. { 043. m_Info.szObj_Chart = "Chart Trade IDE"; 044. m_Info.szObj_Editable = m_Info.szObj_Chart + " > Edit"; 045. ObjectCreate(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJ_CHART, 0, 0, 0); 046. ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_XDISTANCE, m_Info.x); 047. ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YDISTANCE, m_Info.y); 048. ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_XSIZE, w); 049. ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YSIZE, h); 050. ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_DATE_SCALE, false); 051. ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_PRICE_SCALE, false); 052. m_Info.WinHandle = ObjectGetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_CHART_ID); 053. }; 054. //+------------------------------------------------------------------+ 055. void AdjustEditabled(C_AdjustTemplate &Template, bool bArg) 056. { 057. for (eObjectsIDE c0 = 0; c0 <= MSG_STOP_VALUE; c0++) 058. if (bArg) 059. { 060. Template.Add(EnumToString(c0), "bgcolor", NULL); 061. Template.Add(EnumToString(c0), "fontsz", NULL); 062. Template.Add(EnumToString(c0), "fontnm", NULL); 063. } 064. else 065. { 066. m_Info.Regions[c0].bgcolor = (color) StringToInteger(Template.Get(EnumToString(c0), "bgcolor")); 067. m_Info.Regions[c0].FontSize = (int) StringToInteger(Template.Get(EnumToString(c0), "fontsz")); 068. m_Info.Regions[c0].FontName = Template.Get(EnumToString(c0), "fontnm"); 069. } 070. } 071. //+------------------------------------------------------------------+ 072. inline void AdjustTemplate(const bool bFirst = false) 073. { 074. #define macro_PointsToFinance(A) A * (GetInfoTerminal().VolumeMinimal + (GetInfoTerminal().VolumeMinimal * (m_Info.Leverage - 1))) * GetInfoTerminal().AdjustToTrade 075. 076. C_AdjustTemplate *Template; 077. 078. if (bFirst) 079. { 080. Template = new C_AdjustTemplate(m_Info.szFileNameTemplate = IntegerToString(GetInfoTerminal().ID) + ".tpl", true); 081. for (eObjectsIDE c0 = 0; c0 <= MSG_CLOSE_POSITION; c0++) 082. { 083. (*Template).Add(EnumToString(c0), "size_x", NULL); 084. (*Template).Add(EnumToString(c0), "size_y", NULL); 085. (*Template).Add(EnumToString(c0), "pos_x", NULL); 086. (*Template).Add(EnumToString(c0), "pos_y", NULL); 087. } 088. AdjustEditabled(Template, true); 089. }else Template = new C_AdjustTemplate(m_Info.szFileNameTemplate); 090. if (_LastError >= ERR_USER_ERROR_FIRST) 091. { 092. delete Template; 093. 094. return; 095. } 096. m_Info.Leverage = (m_Info.Leverage <= 0 ? 1 : m_Info.Leverage); 097. m_Info.FinanceTake = macro_PointsToFinance(FinanceToPoints(MathAbs(m_Info.FinanceTake), m_Info.Leverage)); 098. m_Info.FinanceStop = macro_PointsToFinance(FinanceToPoints(MathAbs(m_Info.FinanceStop), m_Info.Leverage)); 099. (*Template).Add("MSG_NAME_SYMBOL", "descr", GetInfoTerminal().szSymbol); 100. (*Template).Add("MSG_LEVERAGE_VALUE", "descr", IntegerToString(m_Info.Leverage)); 101. (*Template).Add("MSG_TAKE_VALUE", "descr", DoubleToString(m_Info.FinanceTake, 2)); 102. (*Template).Add("MSG_STOP_VALUE", "descr", DoubleToString(m_Info.FinanceStop, 2)); 103. (*Template).Add("MSG_DAY_TRADE", "state", (m_Info.IsDayTrade ? "1" : "0")); 104. (*Template).Add("MSG_MAX_MIN", "state", (m_Info.IsMaximized ? "1" : "0")); 105. if (!(*Template).Execute()) 106. { 107. delete Template; 108. 109. macro_CloseIndicator(C_Terminal::ERR_FileAcess); 110. }; 111. if (bFirst) 112. { 113. for (eObjectsIDE c0 = 0; c0 <= MSG_CLOSE_POSITION; c0++) 114. { 115. m_Info.Regions[c0].x = (short) StringToInteger((*Template).Get(EnumToString(c0), "pos_x")); 116. m_Info.Regions[c0].y = (short) StringToInteger((*Template).Get(EnumToString(c0), "pos_y")); 117. m_Info.Regions[c0].w = (short) StringToInteger((*Template).Get(EnumToString(c0), "size_x")); 118. m_Info.Regions[c0].h = (short) StringToInteger((*Template).Get(EnumToString(c0), "size_y")); 119. } 120. m_Info.Regions[MSG_TITLE_IDE].w = m_Info.Regions[MSG_MAX_MIN].x; 121. AdjustEditabled(Template, false); 122. }; 123. ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YSIZE, (m_Info.IsMaximized ? 210 : m_Info.Regions[MSG_TITLE_IDE].h + 6)); 124. ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_XDISTANCE, (m_Info.IsMaximized ? m_Info.x : m_Info.minx)); 125. ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YDISTANCE, (m_Info.IsMaximized ? m_Info.y : m_Info.miny)); 126. 127. delete Template; 128. 129. ChartApplyTemplate(m_Info.WinHandle, "/Files/" + m_Info.szFileNameTemplate); 130. ChartRedraw(m_Info.WinHandle); 131. 132. #undef macro_PointsToFinance 133. } 134. //+------------------------------------------------------------------+ 135. eObjectsIDE CheckMousePosition(const short x, const short y) 136. { 137. int xi, yi, xf, yf; 138. 139. for (eObjectsIDE c0 = 0; c0 <= MSG_CLOSE_POSITION; c0++) 140. { 141. xi = (m_Info.IsMaximized ? m_Info.x : m_Info.minx) + m_Info.Regions[c0].x; 142. yi = (m_Info.IsMaximized ? m_Info.y : m_Info.miny) + m_Info.Regions[c0].y; 143. xf = xi + m_Info.Regions[c0].w; 144. yf = yi + m_Info.Regions[c0].h; 145. if ((x > xi) && (y > yi) && (x < xf) && (y < yf)) return c0; 146. } 147. return MSG_NULL; 148. } 149. //+------------------------------------------------------------------+ 150. inline void DeleteObjectEdit(void) 151. { 152. ChartRedraw(); 153. ObjectsDeleteAll(GetInfoTerminal().ID, m_Info.szObj_Editable); 154. } 155. //+------------------------------------------------------------------+ 156. template <typename T > 157. void CreateObjectEditable(eObjectsIDE arg, T value) 158. { 159. long id = GetInfoTerminal().ID; 160. 161. DeleteObjectEdit(); 162. CreateObjectGraphics(m_Info.szObj_Editable, OBJ_EDIT, clrBlack, 0); 163. ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_XDISTANCE, m_Info.Regions[arg].x + m_Info.x + 3); 164. ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_YDISTANCE, m_Info.Regions[arg].y + m_Info.y + 3); 165. ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_XSIZE, m_Info.Regions[arg].w); 166. ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_YSIZE, m_Info.Regions[arg].h); 167. ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_BGCOLOR, m_Info.Regions[arg].bgcolor); 168. ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_ALIGN, ALIGN_CENTER); 169. ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_FONTSIZE, m_Info.Regions[arg].FontSize - 1); 170. ObjectSetString(id, m_Info.szObj_Editable, OBJPROP_FONT, m_Info.Regions[arg].FontName); 171. ObjectSetString(id, m_Info.szObj_Editable, OBJPROP_TEXT, (typename(T) == "double" ? DoubleToString(value, 2) : (string) value)); 172. ChartRedraw(); 173. } 174. //+------------------------------------------------------------------+ 175. bool RestoreState(void) 176. { 177. uCast_Double info; 178. bool bRet; 179. C_AdjustTemplate *Template; 180. 181. if (bRet = GlobalVariableGet(macro_NameGlobalVariable("POST"), info.dValue)) 182. { 183. m_Info.x = (short) info._16b[0]; 184. m_Info.y = (short) info._16b[1]; 185. m_Info.minx = (short) info._16b[2]; 186. m_Info.miny = (short) info._16b[3]; 187. Template = new C_AdjustTemplate(m_Info.szFileNameTemplate = IntegerToString(GetInfoTerminal().ID) + ".tpl"); 188. if (_LastError >= ERR_USER_ERROR_FIRST) bRet = false; else 189. { 190. (*Template).Add("MSG_LEVERAGE_VALUE", "descr", NULL); 191. (*Template).Add("MSG_TAKE_VALUE", "descr", NULL); 192. (*Template).Add("MSG_STOP_VALUE", "descr", NULL); 193. (*Template).Add("MSG_DAY_TRADE", "state", NULL); 194. (*Template).Add("MSG_MAX_MIN", "state", NULL); 195. if (!(*Template).Execute()) bRet = false; else 196. { 197. m_Info.IsDayTrade = (bool) StringToInteger((*Template).Get("MSG_DAY_TRADE", "state")) == 1; 198. m_Info.IsMaximized = (bool) StringToInteger((*Template).Get("MSG_MAX_MIN", "state")) == 1; 199. m_Info.Leverage = (short)StringToInteger((*Template).Get("MSG_LEVERAGE_VALUE", "descr")); 200. m_Info.FinanceTake = (double) StringToDouble((*Template).Get("MSG_TAKE_VALUE", "descr")); 201. m_Info.FinanceStop = (double) StringToDouble((*Template).Get("MSG_STOP_VALUE", "descr")); 202. } 203. }; 204. delete Template; 205. }; 206. 207. GlobalVariablesDeleteAll(macro_NameGlobalVariable("")); 208. 209. return bRet; 210. } 211. //+------------------------------------------------------------------+ 212. public : 213. //+------------------------------------------------------------------+ 214. C_ChartFloatingRAD(string szShortName, C_Mouse *MousePtr, const short Leverage, const double FinanceTake, const double FinanceStop) 215. :C_Terminal(0) 216. { 217. m_Mouse = MousePtr; 218. m_Info.IsSaveState = false; 219. if (!IndicatorCheckPass(szShortName)) return; 220. if (!RestoreState()) 221. { 222. m_Info.Leverage = Leverage; 223. m_Info.IsDayTrade = true; 224. m_Info.FinanceTake = FinanceTake; 225. m_Info.FinanceStop = FinanceStop; 226. m_Info.IsMaximized = true; 227. m_Info.minx = m_Info.x = 115; 228. m_Info.miny = m_Info.y = 64; 229. } 230. CreateWindowRAD(170, 210); 231. AdjustTemplate(true); 232. } 233. //+------------------------------------------------------------------+ 234. ~C_ChartFloatingRAD() 235. { 236. ChartRedraw(); 237. ObjectsDeleteAll(GetInfoTerminal().ID, m_Info.szObj_Chart); 238. if (!m_Info.IsSaveState) 239. FileDelete(m_Info.szFileNameTemplate); 240. 241. delete m_Mouse; 242. } 243. //+------------------------------------------------------------------+ 244. void SaveState(void) 245. { 246. #define macro_GlobalVariable(A, B) if (GlobalVariableTemp(A)) GlobalVariableSet(A, B); 247. 248. uCast_Double info; 249. 250. info._16b[0] = m_Info.x; 251. info._16b[1] = m_Info.y; 252. info._16b[2] = m_Info.minx; 253. info._16b[3] = m_Info.miny; 254. macro_GlobalVariable(macro_NameGlobalVariable("POST"), info.dValue); 255. m_Info.IsSaveState = true; 256. 257. #undef macro_GlobalVariable 258. } 259. //+------------------------------------------------------------------+ 260. void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam) 261. { .... The internal code of this procedure will be seen in the next article 386. } 387. //+------------------------------------------------------------------+ 388. }; 389. //+------------------------------------------------------------------+ 390. #undef macro_NameGlobalVariable 391. #undef macro_CloseIndicator 392. //+------------------------------------------------------------------+
Quellcode der Datei C_ChartFloatingRAD.mqh
In dieser Version des Codes sind die überflüssigen Zeilen bereits entfernt worden. Es enthält auch die notwendigen Aktualisierungen, um sicherzustellen, dass alles wie erwartet funktioniert. Da viele von Ihnen die Originalversion wahrscheinlich nicht kennen, empfehle ich Ihnen, die Erklärungen in diesem Artikel aufmerksam zu lesen, damit Sie nicht nur verstehen, wie der Code funktioniert, sondern vor allem, wie er die Funktion und die Darstellung des Chart Trade im Chart ermöglicht. Auf den ersten Blick sehen Sie vielleicht keine der visuellen Elemente, die Sie von Chart Trade erwarten würden. Wie ist dies also möglich? Wie kann Code, der praktisch keine grafischen Elemente zu enthalten scheint, Grafiken im Chart anzeigen? Was für ein Zauber geht hier vor sich?
Nun, es ist keine Magie. Es sind die Programmierung und der geschickte Einsatz verschiedener Techniken, die zusammengenommen dafür sorgen, dass der Chart Trade wie erwartet funktioniert. Außerdem funktioniert der Chart Trade dadurch zuverlässig und sicher.
In der ursprünglichen Version waren einige Dinge öffentlich zugänglich, wie Strukturen und eine Enumeration in Zeile 17. Es wurde jedoch darauf geachtet, dass keine Variablen außerhalb des Codes durchsickern. Die Kapselung war also bereits ein wichtiges Gestaltungsprinzip. Aber warum sind diese öffentlichen Elemente nicht mehr sichtbar? Das liegt daran, dass die Idee nun darin besteht, ein Protokoll einzurichten und es zu verwenden, ohne die Klasse C_ChartFloatingRAD zur Dekodierung der Daten zu benötigen. Darüber werden wir später in unserem Artikel sprechen.
Alles, was wir an Variablen brauchen, ist nun in der Struktur ab Zeile 18 deklariert. Achten Sie genau darauf, welche Typen verwendet werden. In den meisten Fällen verwenden wir keine 32-Bit-Typen, genauer gesagt, keine INT-Typen. Warum? Auch Variablen für Bildschirmkoordinaten verwenden 16-Bit-Typen (SHORT-Gruppe).
Im Gegensatz zu der üblichen Praxis, auf INT zu setzen, sehe ich hier keine Notwendigkeit dafür. Was die Bildschirmauflösung betrifft, so sind 16 Bit für moderne Technologien mehr als ausreichend. Maximal 65.535 sind für das Plotten auf einem 4K-Bildschirm mehr als ausreichend. Offen gesagt, würde dies sogar 8K-Displays verarbeiten. Es gibt also keinen Vorteil, INT hier zu verwenden. Außerdem hilft die Verwendung von SHORT bei der Datenkompression. Wir werden das später sehen.
Lassen Sie uns jetzt weitermachen. In Zeile 31 sehen Sie eine weitere Struktur, die innerhalb der ersten definiert ist. Diese interne Struktur enthält die relativen Positionen der grafischen Elemente. Dies sind die Elemente, mit denen die Nutzer auf der Chart Trade-Schnittstelle interagieren.
Am Anfang des Codes sind zwei Makros definiert. Kümmern Sie sich vorerst nicht um sie, sondern achten Sie nur auf ihren Standort. Wir werden sie später verwenden. Eine davon, die in Zeile 8 definiert ist, mag auf den ersten Blick etwas verwirrend erscheinen, aber Sie werden ihren Zweck bald verstehen.
Wie auch immer, hier ist der Kern der Sache. In Zeile 14 erben wir privat von der Klasse C_Terminal, um ihre Funktionalität zu erweitern und den Chart Trade zu erstellen. Zwischen den Zeilen 20 und 39 deklarieren wir die privaten internen Variablen der Klasse. Jetzt können wir mit der Überprüfung des Codes beginnen. Der Einfachheit halber werden wir sie Funktion für Funktion analysieren, so wie sie im Code erscheinen. Wir beginnen mit der Prozedur CreateWindowRAD, die in Zeile 41 deklariert wird.
Diese Prozedur wird nur einmal ab Zeile 230 aufgerufen. Nirgendwo sonst. Seine Rolle ist sehr einfach: Er erstellt ein OBJ_CHART-Objekt auf dem Chart. Das war's. Aber warum dieses OBJ_CHART-Objekt hinzufügen? Auf diese Weise müssen wir nicht alle anderen grafischen Objekte manuell erstellen. Für diejenigen, die es gewohnt sind, Charts per Code zu zeichnen, mag dies verwirrend erscheinen, aber ich habe dies bereits in früheren Artikeln erklärt. Am Ende dieses Artikels finden Sie Links zu diesen Quellen, damit Sie sich eingehender mit ihnen befassen können. Sie erklären genau, warum dieser Ansatz gewählt wird. Wenn dieser Artikel das Thema für Sie nicht vollständig klärt, werden diese Referenzen eine wertvolle Hilfe sein.
Der kritische Teil für unsere aktuelle Aufgabe ist Zeile 52. Hier wird die ID gespeichert, die von MetaTrader 5 zurückgegeben wird, sobald das Objekt OBJ_CHART erstellt wurde. Wir brauchen diese ID, da sie später wichtig sein wird. Nun können wir zum nächsten Verfahren übergehen: AdjustEditabled, beginnend in Zeile 55. Was bewirkt dieses Verfahren? So seltsam es auch klingen mag, sein Zweck ist es, den Status der Objekte, die im OBJ_CHART erscheinen, anzupassen und zu erfassen. Aber wie funktioniert das? Um das zu verstehen, müssen wir uns genauer ansehen, wie die AdjustTemplate-Prozedur funktioniert, denn dort wird AdjustEditabled verwendet. Schauen wir uns an, was zwischen den Zeilen 72 und 133 passiert. Dies ist einer der komplexesten Teile des Kurses und der Ort, an dem die „Magie“ wirklich passiert.
In Zeile 74 definieren wir ein Makro, das nur innerhalb dieses Verfahrens verwendet wird. Beachten Sie, dass das Makro am Ende der Prozedur, in Zeile 132, undefiniert ist und daher nicht anderweitig verwendet werden kann. In Zeile 78 wird geprüft, welche Maßnahmen erforderlich sind. Wenn wir den Chart Trade aufbauen, ist diese Bedingung erfüllt, und es folgt eine ganze Reihe von Schritten. Wenn der Chart Trade bereits existiert, wird stattdessen Zeile 89 ausgeführt. An dieser Stelle können Sie den vorangegangenen Artikel noch einmal lesen, um die Art der in der Vorlage durchgeführten Arbeiten besser zu verstehen, da entweder Zeile 80 oder 89 die Erstellung der Vorlage einleitet. Dies hängt wiederum vom Ergebnis der Prüfungen in Zeile 78 ab.
Jetzt kommt der erste schwierige Teil. Wenn wir die Vorlage erstellen, bereitet die Schleife in Zeile 81 die Suche nach den Objekten vor. Aber was sind diese Objekte? Dies sind Objekte, die zu Chart Trade hinzugefügt werden. Hinweis: Wir wissen noch nicht, wo diese Objekte sind, aber wir wissen, wie viele es sind, dank der Enumeration in Zeile 17. In Zeile 88 wird AdjustEditabled erneut aufgerufen, um sicherzustellen, dass die Prüfung in Zeile 58 erfolgreich war. In diesem Schritt werden weitere Daten aus der Vorlage hinzugefügt.
Wenn die Klasse C_AdjustTemplate dies erfolgreich verarbeitet, kann der Code in Zeile 90 fortgesetzt werden. Schlägt dies fehl, kehrt die Ausführung zum aufrufenden Code zurück, wie im vorherigen Artikel gezeigt. Aber atmen Sie durch, wir haben eigentlich noch nichts Wesentliches getan. Bis Zeile 90 haben wir lediglich C_AdjustTemplate aufgefordert, die Vorlagendatei zu öffnen. Mehr nicht.
Zwischen den Zeilen 96 und 98 passen wir einige Anzeigewerte an. Dann, zwischen den Zeilen 99 und 104, bereiten wir die Daten für die Verarbeitung in Zeile 105 vor. Das ist der Moment, in dem die Magie wirklich passiert. Um dies zu verstehen, sollten Sie sich die Funktion Execute der Klasse C_AdjustTemplate aus dem vorherigen Artikel ansehen. Hier geht es darum, die ursprünglich in der Vorlagendatei gespeicherten Werte zu ändern. Aber wir verändern das Original nicht - wir arbeiten an einer Kopie. Aus diesem Grund ist die Kontrolle in Zeile 78 so wichtig. In Zeile 105 wird angegeben, welche Datei geändert werden soll.
Ich bin mir nicht sicher, ob Sie verstanden haben, was hier vor sich geht - aber lassen Sie mich versuchen, es zu erklären. Alles, was von Zeile 78 bis 104 getan wird, bereitet die Daten für C_AdjustTemplate vor, um die Datei mit Chart Trade zu bearbeiten. Jedes Element, das Sie später im Chart Trade sehen, wird zwischen den Zeilen 99 und 104 definiert. So seltsam es klingen mag, es funktioniert.
Daher mein Rat: Studieren Sie diesen Abschnitt des Kodex genau, um wirklich zu verstehen, wie er funktioniert. Jedes Objekt wird mit der gleichen Technik erstellt und verwaltet.
Nehmen wir an, Sie haben alles bis zur Zeile 105 verstanden. Wenn diese Zeile erfolgreich ist, wird der Code fortgesetzt. Wenn sie scheitert, geschieht etwas ganz Ungewöhnliches. Achten Sie genau darauf, was als Nächstes kommt.
Wenn der Aufruf in Zeile 105 fehlschlägt, müssen wir die Vorlage zerstören und den Chart Trade schließen. Das Zerstören der Vorlage ist einfach: Zeile 107 regelt dies. Das ist kein Geheimnis. Dadurch wird der Indikator jedoch nicht aus dem Chart entfernt. Zu diesem Zweck müssen wir die Prozedur OnDeInit aufrufen. Und jetzt kommt der nächste komplexe Teil. Schauen wir uns das folgende Codefragment aus OnDeInit an:
41. void OnDeinit(const int reason) 42. { 43. switch (reason) 44. { 45. case REASON_INITFAILED: 46. ChartIndicatorDelete(ChartID(), 0, def_ShortName); 47. break; 48. case REASON_CHARTCHANGE: 49. (*chart).SaveState(); 50. break; 51. } 52. 53. delete chart; 54. }
Das Codefragment des Indikators Chart Trade
Dieser Ausschnitt dient lediglich dazu, den Vorgang zu erklären. Lassen Sie uns also Folgendes klarstellen: Wenn Zeile 109 ausgeführt wird, springt die Steuerung in den Makrocode. Dieses Makro ist am Anfang des Codes zu sehen, ab Zeile 8. Wenn in diesem Makro die Zeile 9 ausgeführt wird, wird der Inhalt von Zeile 46 des Snippets ausgeführt.
Gleichzeitig wird auch die Zeile 53 des Auszugs ausgeführt, die dafür sorgt, dass der Indikator Chart Trade entfernt wird. Der Code kehrt dann jedoch zurück, und die Zeile 10 des Makros wird ausgeführt. Diese Zeile sorgt dafür, dass ein Fehlerflag gesetzt wird, das verhindert, dass der Code weiterläuft. In Zeile 11 kehren wir dann zum Aufrufer zurück, unabhängig davon, wer die Prozedur AdjustTemplate aufgerufen hat. Wenn die Änderung der Vorlage nicht erfolgreich durchgeführt werden kann, wird der Indikator ohne weitere Probleme entfernt. Dies ist einer der komplexeren Teile des Systems. Aber wir sind noch nicht fertig, denn es gibt noch mehr zu berichten.
Nehmen wir nun an, dass die Änderung der Vorlage erfolgreich war. In diesem Fall gehen wir zu einer neuen Prüfung in Zeile 111 über. Mit dieser Prüfung werden die Positionen der anklickbaren Objekte erfasst. Diese Art von Aktion wird nur bei der Erstellung von Vorlagen durchgeführt. Die in Zeile 113 beginnende Schleife übernimmt diese Aufgabe: Sie erfasst die Positionen der anklickbaren Objekte, und die Zeilen 120 und 121 ergänzen diesen Prozess.
Als Nächstes positionieren wir das Objekt OBJ_CHART auf dem Chart. Dies geschieht zwischen den Zeilen 123 und 125. Mit Zeile 127 ist die Vorlagenmanipulation abgeschlossen, sodass in Zeile 129 die entsprechende Vorlage OBJ_CHART zugewiesen werden kann. Dann erzwingt Zeile 130 die Aktualisierung des Objektinhalts. Damit ist die gesamte Struktur des OBJ_CHART-Objekts abgeschlossen, und alle Komponenten werden angezeigt, wodurch die Schnittstelle Chart Trade entsteht.
Wenn Sie sich immer noch nicht im Klaren darüber sind, wie die Vorlage so aktualisiert wird, dass Sie die Schnittstelle Chart Trade nicht manuell codieren müssen, gehen Sie zurück und lesen Sie das gesamte AdjustTemplate-Verfahren. Dies ist der Hauptprozess, der den Chart Trade konfiguriert und anzeigt. Jedes Objekt, das im Chart Trade erscheint, wird in diesem Verfahren erstellt, positioniert und angepasst.
Es macht keinen Sinn, den Code nach expliziten Objektdeklarationen wie Schaltflächen, Symbolen, Bildern oder anderen Dingen zu durchsuchen, da sie im Code nicht vorhanden sind. Der Code sucht nach vorhandenen Objekten und macht sie für den Nutzer zugänglich. Versuchen Sie nicht, dies als ein herkömmliches Muster für die Erstellung von Objekten zu verstehen. Was ich hier tue, ist genau das Gegenteil: Zuerst entwerfe ich das gewünschte Erscheinungsbild, und erst dann weise ich den Code an, dieses Konzept funktionsfähig zu machen. Wenn Sie diese Idee nicht verstehen, werden Sie sich wahrscheinlich völlig verloren vorkommen.
Dieses System ist so ausgeklügelt, dass Sie das Layout, die Struktur und sogar die Schriftarten der Objekte ändern können, ohne eine einzige Zeile Code zu schreiben. Alles, was Sie tun müssen, ist MetaTrader 5 zu öffnen, die Chart Trade-Vorlage zu laden und die visuelle Darstellung im Chart zu ändern. Der Rest wird vollständig von der Prozedur AdjustTemplate erledigt.
Aber natürlich gibt es immer etwas, das die Dinge verkompliziert. Um dieses RAD-Konzept (Rapid Application Development) zu unterstützen, benötigen wir einige zusätzliche Verfahren. Als Nächstes sehen wir uns die Funktion CheckMousePosition an, die sich in Zeile 135 befindet. Diese Funktion ist sehr einfach. Sie prüft, ob sich der Mauszeiger derzeit innerhalb eines anklickbaren oder auswählbaren Objekts befindet. Ist dies der Fall, gibt Zeile 145 das entsprechende Objekt zurück. Wenn nicht, meldet Zeile 147, dass sich die Maus nicht über einem interaktiven Element befindet.
Danach gibt es zwei Verfahren, die einen eindeutigen Fall unterstützen. Das ist etwas, was das AdjustTemplate-Verfahren allein nicht in der erforderlichen Qualität leisten kann. In diesem Fall handelt es sich um das Objekt OBJ_EDIT, das dem Nutzer die Eingabe von beliebigem Text ermöglicht.
Die Prozedur in Zeile 150 ist dafür zuständig, eventuell erstellte OBJ_EDIT-Objekte zu entfernen. In der Zwischenzeit ist die in Zeile 156 definierte Prozedur für die Erstellung eines einzelnen OBJ_EDIT-Objekts verantwortlich, das es dem Nutzer ermöglicht, einen Wert in die Chart Trade-Schnittstelle einzugeben.
Die Deklaration der Prozedur CreateObjectEditable mag etwas seltsam anmuten, aber sie hat nichts Besonderes an sich. Sie führt eine Überladung ein, die es der Zeile 171 ermöglicht, das Objekt OBJ_EDIT an den Typ der bearbeiteten Variablen anzupassen. Wenn die Variable beispielsweise vom Typ double ist, wird der Wert mit zwei Dezimalstellen angezeigt. Andernfalls wird der Wert wie eine Zeichenkette behandelt. In diesem Fall wird der angezeigte Text genau so sein, wie er vom Aufrufer konstruiert wurde.
Es gibt hier nichts wirklich Schwieriges, zumindest nicht für jemanden, der mit der MQL5-Programmierung vertraut ist. Alles, was getan wird, liegt im Rahmen der allgemeinen MQL5-Kenntnisse, selbst für diejenigen, die nur wenig Erfahrung mit grafischen Objekten haben.
Gehen wir nun zur nächsten Funktion über: RestoreState, deklariert in Zeile 175 und implementiert in Zeile 210. Diese Prozedur funktioniert nicht allein - sie benötigt Unterstützung durch die Prozedur SaveState, die in Zeile 244 beginnt. Beide Funktionen arbeiten außerhalb der üblichen Regeln. Sie arbeiten zusammen, um die wichtigsten Informationen vorübergehend zu speichern. Dazu gehört auch die letzte bekannte Position der Schnittstelle Chart Trade. Genauer gesagt, speichern sie die letzte bekannte Position des OBJ_CHART-Objekts, bevor das Chartaktualisiert wurde.
Um diese Informationen zu speichern, verwenden wir globale Terminalvariablen. Beachten Sie, dass wir nur eine einzige globale Variable verwenden, um die Werte zu speichern. Ich möchte Sie jedoch auf ein weiteres wichtiges Detail aufmerksam machen. In Zeile 187 verwenden wir wieder die Klasse C_AdjustTemplate. Warum? Ändern wir die Vorlage erneut? Die Antwort ist nein. Diesmal verwenden wir die Vorlage auf eine andere Weise.
Erinnern Sie sich daran, dass ich bei der Erstellung der Vorlage in der AdjustTemplate-Prozedur ab Zeile 72 gesagt habe, dass wir Elemente aus der Vorlage hinzufügen oder entfernen können? Das ist genau das, was wir hier tun. Wir verwenden die vorhandenen, geänderten Vorlagenwerte, um die Daten im Chart Trade wiederherzustellen. Warum ist dies notwendig? Lädt das OBJ_CHART-Objekt nicht die Vorlage, um die Chart Trade-Schnittstelle zu erstellen? Ja, das tut sie. Wir stellen diese Daten jedoch wieder her, um mögliche Nutzerinteraktionen mit anklickbaren Objekten zu berücksichtigen, insbesondere die Schaltflächen „Kaufen“ und „Verkaufen“ des Marktes. Diese Schaltflächen lösen Ereignisse aus, die später erklärt werden. Um unnötige Verzögerungen oder fehlende Daten zu vermeiden, wenn diese Ereignisse eintreten, stellen wir die erforderlichen Werte bei jedem Neustart des Chart Trade im Voraus wieder her.
Um diese Informationen aus der Vorlage korrekt wiederherzustellen, müssen wir sie zunächst öffnen - dies geschieht in Zeile 187. Wenn die Prüfung in Zeile 188 erfolgreich war, können die Zeilen 190 bis 194 ausgeführt werden. Dadurch werden sowohl die Objektnamen als auch die entsprechenden Wertepositionen hinzugefügt, die abgerufen werden sollen. In Zeile 195 versuchen wir dann, die Umwandlung durchzuführen. Da wir nichts an der Vorlage ändern oder hinzufügen, ruft diese Zeile einfach die angeforderten Daten ab. Wenn diese Ausführung erfolgreich ist, werden in den Zeilen 197 bis 201 die erforderlichen privaten Variablen der Klasse C_ChartFloatingRAD wiederhergestellt. Es muss nicht alles wiederhergestellt werden, sondern nur die Schlüsselwerte, die für die Bearbeitung künftiger Ereignisse erforderlich sind. In Zeile 204 wird die Vorlage geschlossen, und in Zeile 207 werden die temporären globalen Terminalvariablen gelöscht.
Es ist wichtig zu verstehen, dass diese Variablen eine extrem kurze Lebensdauer haben - normalerweise weniger als eine Sekunde. Sie werden nur verwendet, um die Koordinaten des Nutzs vorübergehend zu speichern, an denen sich OBJ_CHART vor einem Zeitrahmenwechsel befand.
Dieser Artikel ist bereits ziemlich dicht an Informationen. Bevor wir jedoch zum Schluss kommen, wollen wir noch kurz drei weitere Verfahren durchgehen. Sie sind einfach und leicht zu verstehen.
Die Prozedur in Zeile 214 ist der Klassenkonstruktor. Es ist größtenteils selbsterklärend, da es alles, was in diesem Artikel besprochen wurde, ausnutzt. Zeile 218 gibt an, ob der aktuelle Zustand gespeichert werden soll. Standardmäßig wird nichts gespeichert. In Zeile 219 wird versucht, den Indikator zu erstellen. Schlägt das fehl, kehrt die Kontrolle zum Aufrufer zurück, und der Indikator wird beendet. In Zeile 220 versuchen wir, den vorherigen Zustand wiederherzustellen. Gelingt dies nicht, werden stattdessen die Eingabewerte des Nutzers verwendet. In Zeile 230 wird das Objekt OBJ_CHART erstellt, und in Zeile 231 werden die letzten Anpassungen vorgenommen.
Die in Zeile 234 beginnende Prozedur ist der Destruktor der Klasse. Es ist sogar noch einfacher. Zeile 237 entfernt alle mit OBJ_CHART verbundenen Objekte. Dies ist ein wichtiger Schritt, um zu vermeiden, dass die Karte mit unnötigen Resten überladen wird. In Zeile 238 wird geprüft, ob der aktuelle Zustand gespeichert werden soll. Wenn nicht, wird die Vorlagendatei in Zeile 239 von der Festplatte gelöscht. Das bedeutet, dass die Funktion RestoreState (Zeilen 175-210) nicht funktionieren wird, da die Prüfung in Zeile 188 fehlschlägt und die Werte standardmäßig die zuletzt vom Nutzer angegebenen sind.
Dann folgt die Funktion SaveState, die in Zeile 244 beginnt. Er verwendet ein internes Makro, das in Zeile 246 definiert und in Zeile 257 entfernt wird. Dieses Makro ist nicht unbedingt notwendig, aber da der Name der globalen Variable zweimal verwendet wird (einmal zur vorübergehenden Erstellung und einmal zur Speicherung des Wertes), wird der Code durch die Verwendung eines Makros sauberer. Die Schlüsselzeile hier ist 255 - sie sorgt dafür, dass die Prüfung in Zeile 238 fehlschlägt, wodurch die Vorlagendatei erhalten bleibt.
Abschließende Überlegungen
Obwohl ich die DispatchMessage-Prozedur, die in Zeile 260 deklariert und bis Zeile 386 implementiert wird, nicht behandelt habe, werde ich sie in diesem Artikel nicht erklären. Der Grund dafür ist einfach: Es ist viel zu komplex, um in diesem begrenzten Raum erörtert zu werden. Man versteht es besser, wenn man es in Verbindung mit einem anderen Programm sieht. Das liegt daran, dass dieses Verfahren für die Erzeugung der Ereignisse verantwortlich ist, die es dem Nutzer ermöglichen, mit Chart Trade zu interagieren, um Marktpositionen zu öffnen oder zu schließen.
Dieses Thema verdient eine viel ausführlichere und gründlichere Erklärung. Und damit Sie motiviert bleiben, den nächsten Artikel zu lesen, habe ich die internen Details dieser Funktion absichtlich zurückgehalten. Dieser Teil wird im nächsten Artikel ausführlich behandelt. Speichern Sie den Code für die Klasse C_ChartFloatingRAD zunächst in einer separaten Datei und studieren Sie ihn gründlich, denn er ist bereits funktionsfähig. Und bleiben Sie dran für den nächsten Artikel, in dem ich den Code zwischen den Zeilen 260 und 386 durchgehen werde.
Wir sehen uns im nächsten Artikel wieder. Bis dahin finden Sie unten Hinweise zur RAD-Programmierung in MQL5. Dies sind Artikel, die ich in der Vergangenheit geschrieben habe und in denen ich die Konzepte erläutert habe, die zur Konstruktion von Chart Trade verwendet werden, wie hier gezeigt. Wir deklarieren die Objekte nicht explizit, können sie aber dennoch im Chart anzeigen und mit ihnen interagieren. Wenn Sie jedoch das Fenster „Objekte“ mit der Tastenkombination STRG + B öffnen, wird nur ein einziges Objekt angezeigt.
Der Anhang enthält alles, was Sie brauchen, um den Indikator in Aktion zu sehen und mit ihm zu interagieren, so wie es in dem beigefügten Video unten gezeigt wird.
Links zu RAD-Artikeln in MQL5
Übersetzt aus dem Portugiesischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/pt/articles/12442





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