
Entwicklung eines Wiedergabesystems (Teil 47): Chart Trade Projekt (VI)
Einführung
Im vorherigen Artikel Entwicklung eines Replay Systems (Teil 46): Chart Trade Project (V) habe ich gezeigt, wie Sie Daten zu einer ausführbaren Datei hinzufügen können, damit sie nicht separat übertragen werden müssen. Dieses Wissen ist sehr wichtig für das, was in naher Zukunft getan werden soll. Doch zunächst werden wir weiter daran arbeiten, was zuerst umgesetzt werden muss.
In diesem Artikel werden wir den Indikator Chart Trade verbessern und ihn so funktional machen, dass er mit einigen EAs verwendet werden kann. Dadurch können wir auf den Indikator Chart Trade zugreifen und mit ihm arbeiten, als ob er tatsächlich mit einem EA verbunden wäre. Aber lassen Sie es uns viel interessanter gestalten als in einem der vergangenen Artikel „Einen Trading Expert Advisor von Grund auf entwickeln (Teil 30): CHART TRADE als Indikator?!“. In diesem Artikel haben wir Chart Trade als bedingten Indikator verwendet. Dieses Mal wird es ein echter Indikator sein.
Um dies zu tun, werden wir ihn auf eine ganz bestimmte Art und Weise funktionieren lassen, genau wie bei der Arbeit mit jedem anderen Indikatortyp. Dazu werden wir einen entsprechenden Datenpuffer anlegen. Dieses Verfahren wurde bereits in anderen Artikeln beschrieben, Informationen finden Sie hier:
- Entwicklung eines Replay Systems (Teil 37): Den Weg ebnen (I)
- Entwicklung eines Replay Systems (Teil 38): Den Weg ebnen (II)
- Entwicklung eines Replay Systems (Teil 39): Den Weg ebnen (III)
Diese 3 Artikel bilden die Grundlage für das, was wir tatsächlich tun werden. Falls Sie sie noch nicht gelesen haben, möchte ich Sie ermutigen, dies zu tun. Andernfalls besteht die Gefahr, dass Sie beim Lesen dieses Artikels verwirrt sind und Schwierigkeiten haben, ihn zu verstehen, weil Ihnen die tieferen Kenntnisse fehlen, auf denen er beruht. Sie sollten also die genannten Artikel lesen und ihren Inhalt gut verstehen.
Bevor wir mit den Änderungen beginnen, müssen noch ein paar kleine Anpassungen vorgenommen werden. Sie sind alle im Code der bestehenden Klasse C_ChartFloatingRAD enthalten, um einen einfachen und angemessenen Zugriff auf die benötigten Daten zu ermöglichen. Wir gehen also in die letzte Phase der Arbeit am Indikator Chart Trade über.
Kleine Änderungen, große Ergebnisse
Die Änderungen, die vorgenommen werden, sind gering und einfach. Natürlich nur, wenn Sie diese Artikelserie aufmerksam verfolgt haben. Wir befinden uns jetzt in der zweiten Phase der Artikel über das Replay/Simulator-System. Nachfolgend finden Sie den vollständigen Code des Chart Trade Indikators. Es ist wichtig, diesen Code zuerst zu überprüfen, um die Erklärung des Klassencodes zu erleichtern.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. #property description "Chart Trade base indicator." 04. #property description "This version communicates via buffer with the EA." 05. #property description "See the articles for more details." 06. #property version "1.47" 07. #property icon "/Images/Market Replay/Icons/Indicators.ico" 08. #property link "https://www.mql5.com/es/articles/11760" 09. #property indicator_chart_window 10. #property indicator_plots 0 11. #property indicator_buffers 1 12. //+------------------------------------------------------------------+ 13. #include <Market Replay\Chart Trader\C_ChartFloatingRAD.mqh> 14. //+------------------------------------------------------------------+ 15. C_ChartFloatingRAD *chart = NULL; 16. //+------------------------------------------------------------------+ 17. input int user01 = 1; //Leverage 18. input double user02 = 100.1; //Finance Take 19. input double user03 = 75.4; //Finance Stop 20. //+------------------------------------------------------------------+ 21. double m_Buff[]; 22. //+------------------------------------------------------------------+ 23. int OnInit() 24. { 25. bool bErr; 26. 27. chart = new C_ChartFloatingRAD("Indicator Chart Trade", new C_Mouse("Indicator Mouse Study"), user01, user02, user03); 28. 29. if (bErr = (_LastError != ERR_SUCCESS)) Print(__FILE__, " - [Error]: ", _LastError); 30. 31. SetIndexBuffer(0, m_Buff, INDICATOR_DATA); 32. ArrayInitialize(m_Buff, EMPTY_VALUE); 33. 34. return (bErr ? INIT_FAILED : INIT_SUCCEEDED); 35. } 36. //+------------------------------------------------------------------+ 37. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 38. { 39. (*chart).MountBuffer(m_Buff, rates_total); 40. 41. return rates_total; 42. } 43. //+------------------------------------------------------------------+ 44. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 45. { 46. (*chart).DispatchMessage(id, lparam, dparam, sparam); 47. (*chart).MountBuffer(m_Buff); 48. 49. ChartRedraw(); 50. } 51. //+------------------------------------------------------------------+ 52. void OnDeinit(const int reason) 53. { 54. if (reason == REASON_CHARTCHANGE) (*chart).SaveState(); 55. 56. delete chart; 57. } 58. //+------------------------------------------------------------------+
Quellcode des Indikators Chart Trade
Bitte beachten Sie, dass der obige Code alles enthält, was für das Funktionieren des Indikators Chart Trade erforderlich ist. Aus praktischen Gründen wurde der größte Teil des Codes jedoch in eine Klasse verlagert, auf die wir später noch eingehen werden. Aber wie funktioniert dieser Code? Wie ermöglicht er uns, Befehle an den EA zu senden, um Operationen durchzuführen? Warten Sie, lassen Sie uns zunächst herausfinden, was hier im Code des Indikators vor sich geht.
Zeile 11 enthält die erste der benötigten Stufen. In dieser Zeile legen wir fest, dass wir 1 Puffer verwenden werden. Wir könnten mehr Puffer gebrauchen, aber einer wird ausreichen.
Der Puffer, den wir verwenden werden, wird in Zeile 21 deklariert. Aber wir definieren erst in Zeile 31, wie sie zu verwenden ist. Da wir nicht wollen, dass der Puffer mit „Müll“ gefüllt wird, initialisieren wir ihn in Zeile 32 so, dass er nur Nullwerte enthält.
Wie Sie sehen können, hat sich der Code des Indikators im Vergleich zu den vorherigen Artikeln nicht wesentlich geändert. Aber er hat jetzt zwei neue Linien: 39 und 47. Beide Zeilen rufen dieselbe Funktion innerhalb der Klasse auf, die wir uns gleich ansehen werden. Man könnte annehmen, dass es sich aufgrund der unterschiedlichen Anzahl und Parameter um verschiedene Funktionen handelt. Sie werden jedoch bald feststellen, dass sie beide gleich sind. Um dies zu verstehen, sehen wir uns den vollständigen Code der Klasse an, der unten angegeben ist.
001.//+------------------------------------------------------------------+ 002.#property copyright "Daniel Jose" 003.//+------------------------------------------------------------------+ 004.#include "../Auxiliar/C_Mouse.mqh" 005.#include "../Auxiliar/Interprocess.mqh" 006.#include "C_AdjustTemplate.mqh" 007.//+------------------------------------------------------------------+ 008.#define macro_NameGlobalVariable(A) StringFormat("ChartTrade_%u%s", GetInfoTerminal().ID, A) 009.//+------------------------------------------------------------------+ 010.class C_ChartFloatingRAD : private C_Terminal 011.{ 012. public : 013. 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}; 014. struct stData 015. { 016. int Leverage; 017. double PointsTake, 018. PointsStop; 019. bool IsDayTrade; 020. union u01 021. { 022. ulong TickCount; 023. double dValue; 024. }uCount; 025. eObjectsIDE Msg; 026. }; 027. private : 028. struct st00 029. { 030. int x, y, minx, miny; 031. string szObj_Chart, 032. szObj_Editable, 033. szFileNameTemplate; 034. long WinHandle; 035. double FinanceTake, 036. FinanceStop; 037. bool IsMaximized; 038. stData ConfigChartTrade; 039. struct st01 040. { 041. int x, y, w, h; 042. color bgcolor; 043. int FontSize; 044. string FontName; 045. }Regions[MSG_NULL]; 046. }m_Info; 047.//+------------------------------------------------------------------+ 048. C_Mouse *m_Mouse; 049. string m_szShortName; 050.//+------------------------------------------------------------------+ 051. void CreateWindowRAD(int w, int h) 052. { 053. m_Info.szObj_Chart = "Chart Trade IDE"; 054. m_Info.szObj_Editable = m_Info.szObj_Chart + " > Edit"; 055. ObjectCreate(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJ_CHART, 0, 0, 0); 056. ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_XDISTANCE, m_Info.x); 057. ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YDISTANCE, m_Info.y); 058. ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_XSIZE, w); 059. ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YSIZE, h); 060. ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_DATE_SCALE, false); 061. ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_PRICE_SCALE, false); 062. m_Info.WinHandle = ObjectGetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_CHART_ID); 063. }; 064.//+------------------------------------------------------------------+ 065. void AdjustEditabled(C_AdjustTemplate &Template, bool bArg) 066. { 067. for (eObjectsIDE c0 = 0; c0 <= MSG_STOP_VALUE; c0++) 068. if (bArg) 069. { 070. Template.Add(EnumToString(c0), "bgcolor", NULL); 071. Template.Add(EnumToString(c0), "fontsz", NULL); 072. Template.Add(EnumToString(c0), "fontnm", NULL); 073. } 074. else 075. { 076. m_Info.Regions[c0].bgcolor = (color) StringToInteger(Template.Get(EnumToString(c0), "bgcolor")); 077. m_Info.Regions[c0].FontSize = (int) StringToInteger(Template.Get(EnumToString(c0), "fontsz")); 078. m_Info.Regions[c0].FontName = Template.Get(EnumToString(c0), "fontnm"); 079. } 080. } 081.//+------------------------------------------------------------------+ 082.inline void AdjustTemplate(const bool bFirst = false) 083. { 084.#define macro_AddAdjust(A) { \ 085. (*Template).Add(A, "size_x", NULL); \ 086. (*Template).Add(A, "size_y", NULL); \ 087. (*Template).Add(A, "pos_x", NULL); \ 088. (*Template).Add(A, "pos_y", NULL); \ 089. } 090.#define macro_GetAdjust(A) { \ 091. m_Info.Regions[A].x = (int) StringToInteger((*Template).Get(EnumToString(A), "pos_x")); \ 092. m_Info.Regions[A].y = (int) StringToInteger((*Template).Get(EnumToString(A), "pos_y")); \ 093. m_Info.Regions[A].w = (int) StringToInteger((*Template).Get(EnumToString(A), "size_x")); \ 094. m_Info.Regions[A].h = (int) StringToInteger((*Template).Get(EnumToString(A), "size_y")); \ 095. } 096.#define macro_PointsToFinance(A) A * (GetInfoTerminal().VolumeMinimal + (GetInfoTerminal().VolumeMinimal * (m_Info.ConfigChartTrade.Leverage - 1))) * GetInfoTerminal().AdjustToTrade 097. 098. C_AdjustTemplate *Template; 099. 100. if (bFirst) 101. { 102. Template = new C_AdjustTemplate(m_Info.szFileNameTemplate = IntegerToString(GetInfoTerminal().ID) + ".tpl", true); 103. for (eObjectsIDE c0 = 0; c0 <= MSG_CLOSE_POSITION; c0++) macro_AddAdjust(EnumToString(c0)); 104. AdjustEditabled(Template, true); 105. }else Template = new C_AdjustTemplate(m_Info.szFileNameTemplate); 106. m_Info.ConfigChartTrade.Leverage = (m_Info.ConfigChartTrade.Leverage <= 0 ? 1 : m_Info.ConfigChartTrade.Leverage); 107. m_Info.FinanceTake = macro_PointsToFinance(FinanceToPoints(MathAbs(m_Info.FinanceTake), m_Info.ConfigChartTrade.Leverage)); 108. m_Info.FinanceStop = macro_PointsToFinance(FinanceToPoints(MathAbs(m_Info.FinanceStop), m_Info.ConfigChartTrade.Leverage)); 109. m_Info.ConfigChartTrade.PointsTake = FinanceToPoints(m_Info.FinanceTake, m_Info.ConfigChartTrade.Leverage); 110. m_Info.ConfigChartTrade.PointsStop = FinanceToPoints(m_Info.FinanceStop, m_Info.ConfigChartTrade.Leverage); 111. (*Template).Add("MSG_NAME_SYMBOL", "descr", GetInfoTerminal().szSymbol); 112. (*Template).Add("MSG_LEVERAGE_VALUE", "descr", IntegerToString(m_Info.ConfigChartTrade.Leverage)); 113. (*Template).Add("MSG_TAKE_VALUE", "descr", DoubleToString(m_Info.FinanceTake, 2)); 114. (*Template).Add("MSG_STOP_VALUE", "descr", DoubleToString(m_Info.FinanceStop, 2)); 115. (*Template).Add("MSG_DAY_TRADE", "state", (m_Info.ConfigChartTrade.IsDayTrade ? "1" : "0")); 116. (*Template).Add("MSG_MAX_MIN", "state", (m_Info.IsMaximized ? "1" : "0")); 117. (*Template).Execute(); 118. if (bFirst) 119. { 120. for (eObjectsIDE c0 = 0; c0 <= MSG_CLOSE_POSITION; c0++) macro_GetAdjust(c0); 121. m_Info.Regions[MSG_TITLE_IDE].w = m_Info.Regions[MSG_MAX_MIN].x; 122. AdjustEditabled(Template, false); 123. }; 124. ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YSIZE, (m_Info.IsMaximized ? 210 : m_Info.Regions[MSG_TITLE_IDE].h + 6)); 125. ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_XDISTANCE, (m_Info.IsMaximized ? m_Info.x : m_Info.minx)); 126. ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YDISTANCE, (m_Info.IsMaximized ? m_Info.y : m_Info.miny)); 127. 128. delete Template; 129. 130. ChartApplyTemplate(m_Info.WinHandle, "/Files/" + m_Info.szFileNameTemplate); 131. ChartRedraw(m_Info.WinHandle); 132. 133.#undef macro_PointsToFinance 134.#undef macro_GetAdjust 135.#undef macro_AddAdjust 136. } 137.//+------------------------------------------------------------------+ 138. eObjectsIDE CheckMousePosition(const int x, const int y) 139. { 140. int xi, yi, xf, yf; 141. 142. for (eObjectsIDE c0 = 0; c0 <= MSG_CLOSE_POSITION; c0++) 143. { 144. xi = (m_Info.IsMaximized ? m_Info.x : m_Info.minx) + m_Info.Regions[c0].x; 145. yi = (m_Info.IsMaximized ? m_Info.y : m_Info.miny) + m_Info.Regions[c0].y; 146. xf = xi + m_Info.Regions[c0].w; 147. yf = yi + m_Info.Regions[c0].h; 148. if ((x > xi) && (y > yi) && (x < xf) && (y < yf)) return c0; 149. } 150. return MSG_NULL; 151. } 152.//+------------------------------------------------------------------+ 153.inline void DeleteObjectEdit(void) 154. { 155. ChartRedraw(); 156. ObjectsDeleteAll(GetInfoTerminal().ID, m_Info.szObj_Editable); 157. m_Info.ConfigChartTrade.Msg = MSG_NULL; 158. } 159.//+------------------------------------------------------------------+ 160. template <typename T > 161. void CreateObjectEditable(eObjectsIDE arg, T value) 162. { 163. long id = GetInfoTerminal().ID; 164. 165. DeleteObjectEdit(); 166. CreateObjectGraphics(m_Info.szObj_Editable, OBJ_EDIT, clrBlack, 0); 167. ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_XDISTANCE, m_Info.Regions[arg].x + m_Info.x + 3); 168. ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_YDISTANCE, m_Info.Regions[arg].y + m_Info.y + 3); 169. ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_XSIZE, m_Info.Regions[arg].w); 170. ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_YSIZE, m_Info.Regions[arg].h); 171. ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_BGCOLOR, m_Info.Regions[arg].bgcolor); 172. ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_ALIGN, ALIGN_CENTER); 173. ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_FONTSIZE, m_Info.Regions[arg].FontSize - 1); 174. ObjectSetString(id, m_Info.szObj_Editable, OBJPROP_FONT, m_Info.Regions[arg].FontName); 175. ObjectSetString(id, m_Info.szObj_Editable, OBJPROP_TEXT, (typename(T) == "double" ? DoubleToString(value, 2) : (string) value)); 176. ChartRedraw(); 177. m_Info.ConfigChartTrade.Msg = MSG_NULL; 178. } 179.//+------------------------------------------------------------------+ 180. bool RestoreState(void) 181. { 182. uCast_Double info; 183. bool bRet; 184. 185. if (bRet = GlobalVariableGet(macro_NameGlobalVariable("P"), info.dValue)) 186. { 187. m_Info.x = info._int[0]; 188. m_Info.y = info._int[1]; 189. } 190. if (bRet = (bRet ? GlobalVariableGet(macro_NameGlobalVariable("M"), info.dValue) : bRet)) 191. { 192. m_Info.minx = info._int[0]; 193. m_Info.miny = info._int[1]; 194. } 195. if (bRet = (bRet ? GlobalVariableGet(macro_NameGlobalVariable("B"), info.dValue) : bRet)) 196. { 197. m_Info.ConfigChartTrade.IsDayTrade = info._char[0]; 198. m_Info.IsMaximized = info._char[1]; 199. m_Info.ConfigChartTrade.Msg = (eObjectsIDE)info._char[2]; 200. } 201. if (bRet = (bRet ? GlobalVariableGet(macro_NameGlobalVariable("L"), info.dValue) : bRet)) 202. m_Info.ConfigChartTrade.Leverage = info._int[0]; 203. bRet = (bRet ? GlobalVariableGet(macro_NameGlobalVariable("Y"), m_Info.ConfigChartTrade.uCount.dValue) : bRet); 204. bRet = (bRet ? GlobalVariableGet(macro_NameGlobalVariable("T"), m_Info.FinanceTake) : bRet); 205. bRet = (bRet ? GlobalVariableGet(macro_NameGlobalVariable("S"), m_Info.FinanceStop) : bRet); 206. 207. 208. GlobalVariablesDeleteAll(macro_NameGlobalVariable("")); 209. 210. return bRet; 211. } 212.//+------------------------------------------------------------------+ 213. public : 214.//+------------------------------------------------------------------+ 215. C_ChartFloatingRAD(const string szShortName) 216. :m_Mouse(NULL), 217. m_szShortName(szShortName) 218. { 219. } 220.//+------------------------------------------------------------------+ 221. C_ChartFloatingRAD(string szShortName, C_Mouse *MousePtr, const int Leverage, const double FinanceTake, const double FinanceStop) 222. :C_Terminal(), 223. m_szShortName(NULL) 224. { 225. if (!IndicatorCheckPass(szShortName)) SetUserError(C_Terminal::ERR_Unknown); 226. m_Mouse = MousePtr; 227. if (!RestoreState()) 228. { 229. m_Info.ConfigChartTrade.Leverage = Leverage; 230. m_Info.FinanceTake = FinanceTake; 231. m_Info.FinanceStop = FinanceStop; 232. m_Info.ConfigChartTrade.IsDayTrade = true; 233. m_Info.ConfigChartTrade.uCount.TickCount = 0; 234. m_Info.ConfigChartTrade.Msg = MSG_NULL; 235. m_Info.IsMaximized = true; 236. m_Info.minx = m_Info.x = 115; 237. m_Info.miny = m_Info.y = 64; 238. } 239. CreateWindowRAD(170, 210); 240. AdjustTemplate(true); 241. } 242.//+------------------------------------------------------------------+ 243. ~C_ChartFloatingRAD() 244. { 245. if (m_Mouse == NULL) return; 246. ChartRedraw(); 247. ObjectsDeleteAll(GetInfoTerminal().ID, m_Info.szObj_Chart); 248. FileDelete(m_Info.szFileNameTemplate); 249. 250. delete m_Mouse; 251. } 252.//+------------------------------------------------------------------+ 253. void SaveState(void) 254. { 255.#define macro_GlobalVariable(A, B) if (GlobalVariableTemp(A)) GlobalVariableSet(A, B); 256. 257. uCast_Double info; 258. 259. if (m_Mouse == NULL) return; 260. info._int[0] = m_Info.x; 261. info._int[1] = m_Info.y; 262. macro_GlobalVariable(macro_NameGlobalVariable("P"), info.dValue); 263. info._int[0] = m_Info.minx; 264. info._int[1] = m_Info.miny; 265. macro_GlobalVariable(macro_NameGlobalVariable("M"), info.dValue); 266. info._char[0] = m_Info.ConfigChartTrade.IsDayTrade; 267. info._char[1] = m_Info.IsMaximized; 268. info._char[2] = (char)m_Info.ConfigChartTrade.Msg; 269. macro_GlobalVariable(macro_NameGlobalVariable("B"), info.dValue); 270. info._int[0] = m_Info.ConfigChartTrade.Leverage; 271. macro_GlobalVariable(macro_NameGlobalVariable("L"), info.dValue); 272. macro_GlobalVariable(macro_NameGlobalVariable("T"), m_Info.FinanceTake); 273. macro_GlobalVariable(macro_NameGlobalVariable("S"), m_Info.FinanceStop); 274. macro_GlobalVariable(macro_NameGlobalVariable("Y"), m_Info.ConfigChartTrade.uCount.dValue); 275. 276.#undef macro_GlobalVariable 277. } 278.//+------------------------------------------------------------------+ 279.inline void MountBuffer(double &Buff[], const int iPos = -1) 280. { 281. static int posBuff = 0; 282. uCast_Double info; 283. 284. if ((m_szShortName != NULL) || (m_Info.ConfigChartTrade.Msg == MSG_NULL)) return; 285. posBuff = (iPos > 5 ? iPos - 5 : posBuff); 286. Buff[posBuff + 0] = m_Info.ConfigChartTrade.uCount.dValue; 287. info._char[0] = (char)m_Info.ConfigChartTrade.IsDayTrade; 288. info._char[1] = (char)m_Info.ConfigChartTrade.Msg; 289. Buff[posBuff + 1] = info.dValue; 290. info._int[0] = m_Info.ConfigChartTrade.Leverage; 291. Buff[posBuff + 2] = info.dValue; 292. Buff[posBuff + 3] = m_Info.ConfigChartTrade.PointsTake; 293. Buff[posBuff + 4] = m_Info.ConfigChartTrade.PointsStop; 294. } 295.//+------------------------------------------------------------------+ 296.inline const stData GetDataBuffer(void) 297. { 298. double Buff[]; 299. int handle; 300. uCast_Double info; 301. stData data; 302. 303. ZeroMemory(data); 304. if (m_szShortName == NULL) return data; 305. if ((handle = ChartIndicatorGet(ChartID(), 0, m_szShortName)) == INVALID_HANDLE) return data; 306. if (CopyBuffer(handle, 0, 0, 5, Buff) == 5) 307. { 308. data.uCount.dValue = Buff[0]; 309. info.dValue = Buff[1]; 310. data.IsDayTrade = (bool)info._char[0]; 311. data.Msg = (C_ChartFloatingRAD::eObjectsIDE) info._char[1]; 312. info.dValue = Buff[2]; 313. data.Leverage = info._int[0]; 314. data.PointsTake = Buff[3]; 315. data.PointsStop = Buff[4]; 316. } 317. if (handle != INVALID_HANDLE) IndicatorRelease(handle); 318. 319. return data; 320. }; 321.//+------------------------------------------------------------------+ 322. void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam) 323. { 324.#define macro_AdjustMinX(A, B) { \ 325. B = (A + m_Info.Regions[MSG_TITLE_IDE].w) > x; \ 326. mx = x - m_Info.Regions[MSG_TITLE_IDE].w; \ 327. A = (B ? (mx > 0 ? mx : 0) : A); \ 328. } 329.#define macro_AdjustMinY(A, B) { \ 330. B = (A + m_Info.Regions[MSG_TITLE_IDE].h) > y; \ 331. my = y - m_Info.Regions[MSG_TITLE_IDE].h; \ 332. A = (B ? (my > 0 ? my : 0) : A); \ 333. } 334. 335. static int sx = -1, sy = -1; 336. int x, y, mx, my; 337. static eObjectsIDE obj = MSG_NULL; 338. double dvalue; 339. bool b1, b2, b3, b4; 340. eObjectsIDE tmp; 341. 342. if (m_szShortName == NULL) switch (id) 343. { 344. case CHARTEVENT_CHART_CHANGE: 345. x = (int)ChartGetInteger(GetInfoTerminal().ID, CHART_WIDTH_IN_PIXELS); 346. y = (int)ChartGetInteger(GetInfoTerminal().ID, CHART_HEIGHT_IN_PIXELS); 347. macro_AdjustMinX(m_Info.x, b1); 348. macro_AdjustMinY(m_Info.y, b2); 349. macro_AdjustMinX(m_Info.minx, b3); 350. macro_AdjustMinY(m_Info.miny, b4); 351. if (b1 || b2 || b3 || b4) AdjustTemplate(); 352. break; 353. case CHARTEVENT_MOUSE_MOVE: 354. if ((*m_Mouse).CheckClick(C_Mouse::eClickLeft)) switch (tmp = CheckMousePosition(x = (int)lparam, y = (int)dparam)) 355. { 356. case MSG_TITLE_IDE: 357. if (sx < 0) 358. { 359. DeleteObjectEdit(); 360. ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, false); 361. sx = x - (m_Info.IsMaximized ? m_Info.x : m_Info.minx); 362. sy = y - (m_Info.IsMaximized ? m_Info.y : m_Info.miny); 363. } 364. if ((mx = x - sx) > 0) ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_XDISTANCE, mx); 365. if ((my = y - sy) > 0) ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YDISTANCE, my); 366. if (m_Info.IsMaximized) 367. { 368. m_Info.x = (mx > 0 ? mx : m_Info.x); 369. m_Info.y = (my > 0 ? my : m_Info.y); 370. }else 371. { 372. m_Info.minx = (mx > 0 ? mx : m_Info.minx); 373. m_Info.miny = (my > 0 ? my : m_Info.miny); 374. } 375. break; 376. case MSG_BUY_MARKET: 377. case MSG_SELL_MARKET: 378. case MSG_CLOSE_POSITION: 379. DeleteObjectEdit(); 380. m_Info.ConfigChartTrade.Msg = tmp; 381. m_Info.ConfigChartTrade.uCount.TickCount = GetTickCount64(); 382. break; 383. }else if (sx > 0) 384. { 385. ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, true); 386. sx = sy = -1; 387. } 388. break; 389. case CHARTEVENT_OBJECT_ENDEDIT: 390. switch (obj) 391. { 392. case MSG_LEVERAGE_VALUE: 393. case MSG_TAKE_VALUE: 394. case MSG_STOP_VALUE: 395. dvalue = StringToDouble(ObjectGetString(GetInfoTerminal().ID, m_Info.szObj_Editable, OBJPROP_TEXT)); 396. if (obj == MSG_TAKE_VALUE) 397. m_Info.FinanceTake = (dvalue <= 0 ? m_Info.FinanceTake : dvalue); 398. else if (obj == MSG_STOP_VALUE) 399. m_Info.FinanceStop = (dvalue <= 0 ? m_Info.FinanceStop : dvalue); 400. else 401. m_Info.ConfigChartTrade.Leverage = (dvalue <= 0 ? m_Info.ConfigChartTrade.Leverage : (int)MathFloor(dvalue)); 402. AdjustTemplate(); 403. obj = MSG_NULL; 404. ObjectDelete(GetInfoTerminal().ID, m_Info.szObj_Editable); 405. break; 406. } 407. break; 408. case CHARTEVENT_OBJECT_CLICK: 409. if (sparam == m_Info.szObj_Chart) switch (obj = CheckMousePosition(x = (int)lparam, y = (int)dparam)) 410. { 411. case MSG_DAY_TRADE: 412. m_Info.ConfigChartTrade.IsDayTrade = (m_Info.ConfigChartTrade.IsDayTrade ? false : true); 413. DeleteObjectEdit(); 414. break; 415. case MSG_MAX_MIN: 416. m_Info.IsMaximized = (m_Info.IsMaximized ? false : true); 417. DeleteObjectEdit(); 418. break; 419. case MSG_LEVERAGE_VALUE: 420. CreateObjectEditable(obj, m_Info.ConfigChartTrade.Leverage); 421. break; 422. case MSG_TAKE_VALUE: 423. CreateObjectEditable(obj, m_Info.FinanceTake); 424. break; 425. case MSG_STOP_VALUE: 426. CreateObjectEditable(obj, m_Info.FinanceStop); 427. break; 428. } 429. if (obj != MSG_NULL) AdjustTemplate(); 430. break; 431. } 432. } 433.//+------------------------------------------------------------------+ 434.}; 435.//+------------------------------------------------------------------+ 436.#undef macro_NameGlobalVariable 437.//+------------------------------------------------------------------+
Quellcode der Klasse C_ChartFloatingRAD
Es mag den Anschein haben, dass ich mit der Veröffentlichung des Quellcodes in diesem Artikel scherze, aber es ist kein Scherz. Eigentlich möchte ich so detailliert wie möglich erklären, was hier passiert. Dies geschieht nicht, damit Sie diesen Code tatsächlich verwenden können, sondern damit Sie ihn verstehen und etwas BESSERES erstellen können.
Lassen Sie uns also herausfinden, was hier vor sich geht. Wenn Sie nicht verstehen, wie diese Klasse funktioniert, werden Sie nicht verstehen, was als Nächstes entwickelt wird.
Wir haben einige Änderungen gleich zu Beginn. In Zeile 12 deklarieren wir eine öffentliche Klausel. Wir verwenden es, um die Daten außerhalb der Klasse zugänglich zu machen. Man könnte meinen, dass wir die gleichen Daten auch anderswo unterbringen könnten. Nun, hier deklariere ich nur eine Struktur und eine Enumeration. Die Enumeration ist uns bereits bekannt, aber die Struktur ist neu. Zwischen den Zeilen 14 und 26 finden wir die Deklaration einer Struktur, die zur Organisation der benötigten Daten dient.
Der größte Teil dieser Struktur ist recht einfach zu verstehen und bedarf kaum einer Erklärung. Allerdings gibt es hier einige Dinge, die recht merkwürdig aussehen. Was ist der Zweck dieser Verbindung zwischen den Zeilen 20 und 24? Und wofür ist die Zeile 25 gedacht? Eine „union“ wird es uns ermöglichen, Dinge leichter zu übertragen. Außerdem wird die Zeile 25 zu einem ganz bestimmten Zeitpunkt verwendet. Dazu kommen wir etwas später.
Die gleiche Struktur wird in Zeile 38 erwähnt. Denken Sie daran: Auf Daten, die in einer Klasse vorhanden sind, kann außerhalb der Klasse nicht direkt zugegriffen werden. Wir brauchen also eine Möglichkeit, auf solche Daten zuzugreifen. Wir werden das Thema nicht direkt ansprechen. Wie aus dem Quellcode des Indikators hervorgeht, gibt es keinen solchen Zugang. Als Nächstes kommt Zeile 49, die etwas Interessantes tut. Er wird als eine Art Selektor dienen, aber das wird später noch deutlicher werden.
In Zeile 106 wird die erste der in der Struktur in Zeile 38 deklarierten Variablen gesammelt und angepasst. Beachten Sie, dass dies im Wesentlichen dasselbe ist wie vorher, aber wir arbeiten jetzt mit einem etwas anderen Modell.
In Zeile 109 passen wir also eine der Variablen an. Dies ist die Variable für die Anzahl der Punkte des Take-Profit-Wertes. Bitte beachten Sie, dass ich NICHT von finanziellen Werten spreche, sondern von PUNKTEN. Verwechseln Sie diese beiden Dinge nicht. Ich meine auch nicht die Anzahl der Punkte auf der Grundlage des aktuellen Kurses. Ich spreche von den Punkten im Allgemeinen. Der Preis spielt keine Rolle, es kommt darauf an, wie viele Schaltpunkte wir haben. Es ist auch wichtig zu beachten, dass dieser Wert in Punkten dann in finanzieller Hinsicht angepasst wird. Diese Anpassung wird in Zeile 107 vorgenommen.
In Zeile 110 haben wir etwas Ähnliches, nur dass wir es dieses Mal für den Stop-Loss-Wert tun. Genauso wie in Zeile 107, um die finanziellen Werte anzupassen, bevor die Punkte in Zeile 109 berücksichtigt werden. In Zeile 108 passen wir die finanziellen Stopps an, bevor wir die Stop-Loss-Punkte berücksichtigen. Es ist sehr wichtig, dass Sie verstehen, was in diesem Moment geschieht. Wenn Sie dies nicht verstehen, werden Sie Schwierigkeiten haben, die Anpassung zu verstehen, die später bei der Auftragserteilung vorgenommen wird.
Um Ihnen das Leben ein wenig zu erleichtern, enthält der Anhang die Indikatoren Mouse und Chart Trade sowie einen recht einfachen Expert Advisor, damit Sie verstehen, was vor sich geht. Aus Sicherheitsgründen wird der EA im Anhang keine Aufträge erteilen, sondern nur die eingegebenen Werte ausdrucken. So können Sie besser verstehen, wie das System tatsächlich funktioniert.
In den Zeilen 157 und 177 passen wir nun, ebenfalls aus praktischen Gründen, den Wert einer weiteren Variablen an. Bislang wurde noch kein Zugang gewährt. Alles, was wir tun, ist die Einstellung und Anpassung der Werte von Variablen. Aber wenn ich mir Zeile 203 ansehe, kommt mir etwas seltsam vor. Warum speichern wir diesen Wert in einer globalen Terminalvariablen? Müssen wir das wirklich tun oder ist das nur Zeitverschwendung? Dies ist in der Tat notwendig. Der Grund dafür ist, dass alle Werte im Speicher verloren gehen, wenn der Indikator Chart Trade gelöscht und er erneut in den Chart geladen wird. Aber dieser Wert ist für uns sehr wichtig.
Hier stellen wir also die zuvor gespeicherten Werte wieder her. Derselbe Wert, den wir in Zeile 203 wiederherstellen, wurde also tatsächlich in Zeile 274 gespeichert. Sie haben vielleicht bemerkt, dass ich in dieser Erklärung einige Funktionen übersprungen habe. Also, lasst uns zu ihnen zurückkehren. Beginnen wir mit dem Konstruktor. Ja, wir haben jetzt nicht nur einen, sondern zwei Klassenkonstruktoren. Der Grund dafür ist derselbe wie in dem früheren Artikel, in dem wir den Mauszeiger besprochen haben. Wir brauchen eine Methode, um Daten aus dem Puffer zu übertragen.
Um ehrlich zu sein, brauchen wir das nicht wirklich. Wir könnten dies direkt im EA oder im Indikator tun. Der Einfachheit halber ziehe ich es jedoch vor, alles zusammenzustellen. Auf diese Weise muss ich, wenn ich später Änderungen vornehmen muss, nur die Klasse C_ChartFloatingRAD ändern. Ich muss nicht mehr jedes Programm einzeln konfigurieren, um eine Standardisierung der Module zu erreichen. Zurück zu den Konstruktoren: Wir haben einen alten Konstruktor, der in Zeile 221 beginnt. Grundsätzlich werden die Daten aus Zeile 223 abgerufen, wo wir den Indikatornamen initialisieren. Anmerkung: Dieser Name wird nicht zum Schreiben in den Puffer verwendet, sondern zum Lesen aus dem Puffer. Außerdem gibt es die Zeilen 232 bis 234, in denen wir neue Variablen einführen. Nicht alle, denn die übrigen werden nach dem Verfahren zur Einrichtung der Vorlage konfiguriert.
Der zweite Konstruktor ist recht einfach und beginnt in Zeile 215. Dort weisen wir meist Standardwerte zu. Dies geschieht während der Übersetzungsphase. Wie bei der Mausanzeige gibt es auch hier zwei Phasen: eine, in der wir Daten in den Puffer schreiben, und eine, in der wir den Puffer lesen. Das liegt aber nicht daran, dass die Programmierung schief läuft. Wenn Sie hier etwas Falsches sehen, dann haben Sie vielleicht nicht verstanden, was wir tun. Wenn wir den Indikator verwenden, schreiben wir nämlich Daten in den Puffer. Diese Daten werden über CopyBuffer von einem anderen Programm, normalerweise einem EA, gelesen. Deshalb haben wir zwei Phasen, und deshalb haben wir zwei Konstruktoren.
Im Gegensatz zu den Konstruktoren können wir nur einen Destruktor haben, und der beginnt in Zeile 243. Das einzige, was wir hier hinzugefügt haben, ist Zeile 245. Wenn wir diese Klasse in einem Indikator verwenden, wird beim Aufruf des Destruktors die Prüfung in Zeile 245 bestanden, sodass die erstellten Objekte gelöscht werden können. Wenn die Klasse jedoch zum Lesen des Indikatorpuffers verwendet wird, schlägt der Test in Zeile 245 fehl und verhindert, dass ein Objekt gelöscht wird. Ein einfacher, aber funktioneller Mechanismus.
Das war der einfache Teil. Jetzt kommen wir zum entscheidenden Teil unserer Erklärung. Schauen wir uns an, wie die Interaktion erfolgt und wie Daten im Puffer gespeichert und aus ihm gelesen werden. Dieser Teil mag für diejenigen, die gerade erst anfangen, ein wenig verwirrend sein. Deshalb ist es so wichtig, die in den vorangegangenen Artikeln erörterten Konzepte zu verstehen. Zur Veranschaulichung sehen wir uns Abbildung 01 an.
Abbildung 01 - Interaktionsdiagramm
In Abbildung 01 sehen wir ein Kommunikationssystem zur Übermittlung von Informationen vom Indikator an den EA. Beachten Sie, dass der Puffer nicht wirklich Teil des Indikators ist. Auch wenn der Puffer im Indikator deklariert ist, sollte er nicht als Teil des Indikatorspeichers betrachtet werden. Er wird von MetaTrader 5 unterstützt, und wenn wir den Indikator aus dem Chart entfernen, wird der Speicher freigegeben, der vom Puffer verwendet wurde. Darin liegt aber auch eine der Gefahren. Die Daten werden nicht wirklich zerstört, nur der Speicher wird freigegeben. Worin besteht die volle Gefahr? Ich werde versuchen, es in einfacher Sprache zu erklären.
Wenn der Speicher, in dem sich der Puffer in MetaTrader 5 befand, freigegeben wurde, weil der Indikator aus dem Chart entfernt wurde, kann dies nach dem Schreiben in den Puffer passieren. Wenn der Indikator wieder auf dem Chart platziert wird, ist es möglich, dass der Puffer noch Daten enthält. Dies kann mit dem neuesten Update passieren. Wenn der EA diese Daten mit CopyBuffer liest, kann er sie daher falsch lesen.
Die Angelegenheit ist so ernst, dass wir beim Zugriff auf den Puffer äußerst vorsichtig sein müssen. Um eine gewisse Synchronisierung zwischen Schreiben und Lesen zu ermöglichen, müssen wir die Methode DispatchMessage in der Klasse C_ChartFloatingRAD ändern. Diese Synchronisierung zwischen dem Schreiben in den Puffer und dem Lesen ist sehr wichtig. Wenn dies nicht korrekt geschieht, kommt es zu einer Verzögerung zwischen dem, was der Nutzer zur Ausführung übermittelt hat, und dem, was tatsächlich ausgeführt wird. Das heißt, ein Nutzer kann einen Kaufauftrag erteilen, der nicht ausgeführt wird. Wenn Sie jedoch unmittelbar danach einen Verkaufsauftrag erteilen, wird ein Kauf ausgeführt. Diese Art von Fehlern wird nicht durch die MetaTrader 5 Plattform verursacht, sondern eher durch ein Missverständnis, wie man Code schreibt, um eine korrekte Ereignissynchronisation zu gewährleisten.
Tatsächlich gibt es keine Verbindung zwischen dem EA und dem Chart Trade Indikator, was den Code betrifft. Diese Kommunikation wird durch die Verwendung eines Puffers realisiert. Der EA weiß also nicht, was Chart Trade tut, genauso wie Chart Trade nicht weiß, was der EA tatsächlich tut. Der MetaTrader 5 weiß jedoch, was beide tun. Und da der gemeinsame Punkt zwischen dem Indikator und dem EA der MetaTrader 5 ist, nutzen wir ihn, um die Dinge zu realisieren. Es ist wie Magie, wenn Sie oder der Nutzer den Eindruck bekommen, dass der EA und der Chart Trade Indikator das gleiche Programm sind.
Es gibt noch einen weiteren, ebenso wichtigen Punkt. Sie sollten wissen, dass der Indikator Chart Trade ohne den in den vorangegangenen Artikeln erwähnten Mausindikator NICHT funktionieren wird. Sie müssen alle drei Anwendungen auf dem Chart haben: den Maus-Indikator, den Indikator Chart Trade und den EA, sowie andere Dinge, über die wir in Zukunft sprechen werden. Ohne diese wird das gesamte System nichts ausrichten.
Wie funktioniert das alles? All dies wird durch das Ereignis CHARTEVENT_MOUSE_MOVE gewährleistet, das sich in Zeile 353 der Klasse C_ChartFloatingRAD befindet. Wenn sich der Mausindikator nicht im Chart befindet, können Sie die Werte des Chart Trade Indikators anklicken, bearbeiten und ändern. Das liegt daran, dass solche Ereignisse nicht unbedingt mit dem Mauszeiger zusammenhängen. Sie können jedoch keine Kauf-, Verkaufs- oder Schließaufträge senden oder den Indikator Chart Trade verschieben, wenn der Mausindikator nicht auf dem Chart platziert ist.
Doch halt. Muss der Mauszeiger auf dem Chart sein, um mit Chart Trade zu interagieren? Ja: Es ist möglich, diese Abhängigkeit zu beseitigen, aber es wird später zu anderen Problemen führen, wenn Sie die anderen Methoden verwenden wollen, über die ich sprechen werde. Mit diesem Aufwand muss man leben, wenn man so arbeiten will wie ich. Deshalb ist es wichtig, dass Sie gut verstehen, wie das System funktioniert, sonst haben Sie am Ende ein System, dem Sie nicht vertrauen können.
Aber zurück zum Code, um zu verstehen, wie ich es geschafft habe, das System zu synchronisieren. Um dies vollständig zu verstehen, müssen Sie wissen, was der Quellcode des Indikators tut, aber wenn Sie die Artikel gelesen haben, die ich zu Beginn dieses Artikels verlinkt habe, sollten Sie damit keine Probleme haben. Gehen wir also davon aus, dass Sie bereits wissen, wie der Indikatorcode funktioniert. So kann ich mich auf den Klassencode konzentrieren.
Jedes Mal, wenn Sie mit der Maus klicken oder sie bewegen, erzeugt MetaTrader 5 ein Ereignis. Klick-Ereignisse werden normalerweise mit CHARTEVENT_OBJECT_CLICK behandelt. Die Verwendung dieses Handlers ermöglicht es uns jedoch nicht, die Synchronisation aufrechtzuerhalten. Der Grund dafür ist recht kompliziert zu erklären; er hat mit der Reihenfolge zu tun, in der die Operationen durchgeführt werden. Damit ein Ereignis im EA gleichzeitig generiert wird, wenn eine der drei Chart Trade-Schaltflächen (Kauf-Schaltfläche, Verkaufs-Schaltfläche und die Schaltfläche zum Schließen einer Position) angeklickt wird, gehen wir ein wenig anders vor.
Wenn Sie diesen Code in der DispatchMessage-Methode mit dem gleichen Code aus dem vorherigen Artikel vergleichen, können Sie sehen, dass sie sich leicht unterscheiden. Der Unterschied liegt in der Art und Weise, wie die Klicks auf die genannten Schaltflächen verarbeitet werden. In der vorherigen Version wurde dieser Klick im Ereignis CHARTEVENT_OBJECT_CLICK behandelt, jetzt wird er im Ereignis CHARTEVENT_MOUSE_MOVE verarbeitet. Da der Code selbst angibt, welches Objekt angeklickt wurde, habe ich eine neue Variable erstellt, um die Dinge besser zu organisieren. Diese Variable wird in Zeile 340 deklariert und ihr Wert wird in Zeile 354 gesetzt.
Hören Sie gut zu, was ich Ihnen jetzt erklären werde. In den Zeilen 376 bis 378 platzieren wir die Schaltflächencodes. Wenn also der Mausindikator Daten an uns sendet, können wir einen Befehl an den EA senden, der ausgeführt werden soll. Allerdings gibt es hier ein kleines Detail. Wie kann ich dem EA mitteilen, welche Taste gedrückt wurde? Es ist ganz einfach. Wir senden den Schaltflächencode an den EA. Dies geschieht in Zeile 380. In Zeile 381 wird nun die Anzahl der Ticks aufgezeichnet, um eine eindeutige Nummer zu erzeugen. Dies wird für den EA notwendig sein. Sie werden bald verstehen, wie genau das funktioniert.
Für jedes Ereignis, das erzeugt wird, erfolgt also zusätzlich zu diesem Aufruf der Funktion DispatchMessage, die die in den Puffer zu sendenden Daten vorbereitet, ein weiterer Aufruf. Derjenige, der die Daten tatsächlich in den Puffer einspeist. Sie können den Code ab Zeile 279 in der Klasse sehen. Schauen wir uns nun die Zeile 281 an, die eine statische Variable enthält. Er speichert den von OnCalculate übergebenen Wert. Das heißt, wir müssen den Wert rates_total speichern, der Grund dafür wurde bereits weiter oben erläutert. Lesen Sie die eingangs aufgeführten Artikel, um zu verstehen, warum. Auf diese Weise wissen wir beim Aufruf des OnChartEvent-Handlers, wo die Daten im Puffer abgelegt werden sollen.
Beachten Sie, dass wir in Zeile 284 eine Prüfung durchführen, um sicherzustellen, dass nur der Indikator Chart Trade Daten in den Speicher schreibt. Außerdem erfolgt die Aufnahme in den Speicher nur, wenn eine der erforderlichen Tasten gedrückt wird. All dies ist sehr gut. All dies nützt jedoch nichts, wenn der EA die Daten, die ihm der Chart Trade Indikator übermittelt, nicht interpretieren kann. Um zu verstehen, wie der EA die Daten interpretieren kann, müssen wir einen anderen, möglichst einfachen Code betrachten.
Verwendung des Test-Expertenberaters
Da alles getestet werden muss, müssen wir dafür sorgen, dass die Tests ordnungsgemäß durchgeführt werden und deutlich zeigen, was tatsächlich passiert. In diesem Fall müssen wir ein sehr einfaches System verwenden, um die Interaktion zwischen dem Chart Trade Indikator, dem Mausindikator und dem EA zu testen. Aber gleichzeitig muss dieses System nicht nur einfach sein, sondern auch so funktionieren, dass wir es in Zukunft tatsächlich nutzen können.
Auf der Grundlage dieser Kriterien werden wir den folgenden Code verwenden:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. #property description "Demo version between interaction of Chart Trade and EA" 04. #property version "1.47" 05. #property link "https://www.mql5.com/es/articles/11760" 06. //+------------------------------------------------------------------+ 07. #include <Market Replay\Chart Trader\C_ChartFloatingRAD.mqh> 08. //+------------------------------------------------------------------+ 09. C_ChartFloatingRAD *chart = NULL; 10. //+------------------------------------------------------------------+ 11. int OnInit() 12. { 13. chart = new C_ChartFloatingRAD("Indicator Chart Trade"); 14. 15. return (CheckPointer(chart) != POINTER_INVALID ? INIT_SUCCEEDED : INIT_FAILED); 16. } 17. //+------------------------------------------------------------------+ 18. void OnDeinit(const int reason) 19. { 20. delete chart; 21. } 22. //+------------------------------------------------------------------+ 23. void OnTick() {} 24. //+------------------------------------------------------------------+ 25. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 26. { 27. static ulong st_uTime = 0; 28. C_ChartFloatingRAD::stData info; 29. 30. switch (id) 31. { 32. case CHARTEVENT_OBJECT_CLICK: 33. info = (*chart).GetDataBuffer(); 34. if (st_uTime != info.uCount.TickCount) 35. { 36. st_uTime = info.uCount.TickCount; 37. PrintFormat("%u -- %s [%s] %d : %f <> %f", info.uCount.TickCount, info.IsDayTrade ? "DT" : "SW", EnumToString(info.Msg), info.Leverage, info.PointsTake, info.PointsStop); 38. }else Print("IGNORADO..."); 39. break; 40. } 41. } 42. //+------------------------------------------------------------------+
Quellcode des getesteten Expert Advisors
Beachten Sie, dass dieser Code sehr einfach, kompakt und größtenteils selbsterklärend ist. Einige Leser, vor allem Anfänger, können jedoch nicht wirklich verstehen, wie es funktioniert. Seien Sie darüber nicht verärgert. Alle Anfänger stehen vor ähnlichen Schwierigkeiten. Aber wenn Sie sich anstrengen, lernen, Zeit investieren, diszipliniert bleiben und sich ständig verbessern wollen, werden Sie in Zukunft mit Sicherheit ein großer Profi werden. Nachgeben bei Schwierigkeiten ist unprofessionell.
Schauen wir uns nun schnell den Code an, denn er besteht nur aus wenigen Zeilen und sollte leicht zu verstehen sein.
Der gesamte Code, der im vorhergehenden Thema vorgestellt wurde und in der Klasse C_ChartFloatingRAD vorhanden ist, wird zusammengefasst, wenn er hier im EA verwendet wird. Obwohl wir die Klasse in Zeile 7 vollständig einbinden, ist dies nicht genau das, was der Compiler denkt. In Zeile 9 deklarieren wir also den Zeiger global. Sie wird für den Zugriff auf die Klasse C_ChartFloatingRAD verwendet. Dies mag verwirrend erscheinen. Aber nur, weil Sie sich vorstellen, dass auf die Klasse auf dieselbe Weise zugegriffen wird wie auf den Indikatorcode.
Es wäre sogar möglich, es auf diese Weise zu tun, es ist nur nicht praktisch. Der Grund dafür ist, dass die Klasse nicht ohne einen Indikator verwendet werden soll. Dasselbe gilt für die Klasse C_Mouse, die im Mausindikator verwendet wird und nicht in Code verwendet werden sollte, der kein Indikator ist. Ich weiß, dass viele Menschen in Versuchung geraten könnten, dies zu tun. Sie sollten dies jedoch NICHT tun. Denn alle erwarteten Sicherheits-, Modellierungs- und Leistungsmerkmale sind nicht für die Verwendung von Klassen in anderem Code als dem ursprünglichen vorgesehen. Das heißt, wenn Sie die gleiche Kodierungsmethode in den EA einbauen wie in den Indikator, können Sie tatsächlich den Punkt erreichen, an dem Sie den Indikator Chart Trade nicht mehr benötigen. Das ist eine Tatsache.
Wenn Sie jedoch den Code des Indikators auf den EA übertragen, kann es zu Problemen mit der Stabilität und Sicherheit des gesamten Systems kommen, da dieser nicht für eine solche Stabilität ausgelegt ist. Alles hat seinen Platz. Wenn wir also den Zeiger in Zeile 13 durch Aufruf des Konstruktors erstellen, beachten Sie, dass ich denselben Namen verwende, der als Name des Chart Trade Indikators angegeben wurde. Auf diese Weise werden sowohl der Indikator als auch der EA ihre Aufgaben erfüllen. Jeder ist an seinem Platz. Wenn in einem von ihnen etwas schief geht, müssen Sie ihn nur auf dem Chart neu starten.
Beachten Sie nun, dass fast der gesamte Code auf genau dies hinausläuft. Geben Sie den Namen des Indikators an, und erfassen Sie dann in der Ereignisbehandlung von OnChartEvent das Ereignis CHARTEVENT_OBJECT_CLICK und analysieren Sie, was passiert ist. Es gibt einen wichtigen Punkt, der hier zu beachten ist. Jedes Mal, wenn Sie mit der Maus klicken, wird ein Klick-Ereignis für das Objekt erzeugt, auch wenn Sie denken, dass Sie auf nichts Wichtiges geklickt haben. Der Grund dafür ist, dass der Mauszeiger immer auf dem Chart sein muss. Dieser Indikator hat eine horizontale Linie, die das Objekt darstellt. Wenn Sie also mit der Maus klicken, wird diese horizontale Linie ein Ereignis erzeugen.
Aber wie soll das System dann einen Klick auf diese Zeile von einem Klick auf ein anderes Objekt unterscheiden können? Dies ist eine interessante Frage, die bald Gegenstand eines weiteren Artikels sein wird. Dies sollte jedoch bei der Verwendung verschiedener Objekte im Chart beachtet werden, vor allem weil wir bald weitere Indikatoren hinzufügen werden. Aber das ist eine Frage für die Zukunft.
Das Problem besteht darin, dass das Ereignis eines Objektklicks in Zeile 33 versucht, den Inhalt des Puffers des Indikators Chart Trade zu lesen. Wenn dies erfolgreich ist, erhalten wir die zurückgegebenen Daten. In Zeile 34 wird dann geprüft, ob es sich um ein Ereignis handelt, das mit dem Chart Trade zusammenhängt, oder um etwas anderes, das ignoriert werden kann.
Wenn in Chart Trade tatsächlich ein Ereignis erzeugt wurde, das durch einen Klick auf eine der Schaltflächen für die Interaktion mit dem Bestellsystem ausgelöst wurde, aktualisieren wir den Wert der statischen Variablen, wie wir es in Zeile 36 getan haben. Danach geben wir eine Meldung auf dem Terminal aus, damit wir analysieren können, was passiert ist. Wenn das Ereignis aus irgendeinem Grund ignoriert werden muss, wird Zeile 38 ausgeführt.
Schlussfolgerung
Video 01 zeigt, wie das System in der Praxis funktioniert. Nichts kann jedoch die Gelegenheit ersetzen, das System persönlich in Aktion zu sehen. Daher enthält der Anhang zu diesem Artikel das System in seinem derzeitigen Zustand.
Video 01 - Demonstration
Übersetzt aus dem Portugiesischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/pt/articles/11760





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