
开发回放系统(第 47 部分):Chart Trade 项目(六)
概述
在上一篇文章开发回放系统(第 46 部分):Chart Trade Project (五)中,我展示了如何将数据添加到可执行文件中,这样就不必单独传输数据了。这些知识对于不久的将来要做的事情非常重要。但现在,我们将继续开发需要首先实现的内容。
在本文中,我们将改进 Chart Trade 指标,使其功能足以与一些 EA 配合使用。这样,我们就可以访问 Chart Trade 指标,并像实际连接 EA 一样使用它。但是,让我们把它变得比过去的一篇文章更有趣从零开始开发 EA 交易(第 30 部分):把 Chart Trader 作为指标? 。在那篇文章中,我们使用 Chart Trade 作为一个条件指标。而这一次,它将成为一个真正的指标。
为此,我们将以一种非常特殊的方式使其发挥作用,就像使用任何其他类型的指标一样。为此,我们将创建一个对应的数据缓冲区。这个过程在其他文章中有过描述,相关信息可在此处找到:
这三篇文章包含了我们实际要做的工作的基础。如果您还没有读过,我建议您读一读。否则,您可能会在阅读本文时感到困惑,并且由于缺乏作为本文基础的深层知识而难以理解本文。因此,您应该阅读上述文章,并充分理解其内容。
在开始改动之前,我们需要做一些小修改。它们都包含在现有的 C_ChartFloatingRAD 类代码中,以便于我们轻松、充分地访问所需的数据。因此,我们将进入 Chart Trade 指标工作的最后阶段。
小改变,大成果
将做出的修改很少,也很简单。当然,前提是您一直在关注本系列文章。现在,我们正处于回放/模拟系统文章的第二阶段。以下是 Chart Trade 指标的完整代码。请务必先复习这些代码,以便更轻松地解释类的代码。
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. //+------------------------------------------------------------------+
Chart Trade 指标源代码
请注意,上面的所有代码包含了 Chart Trade 指标运行所需的所有内容。不过,出于实用的考虑,大部分代码都被移到了一个类中,稍后将对此进行讨论。但这些代码是如何工作的呢?它是如何让我们向 EA 发送命令以执行操作的?等一下,让我们先弄清楚指标代码中发生了什么。
第 11 行包含我们需要的第一个阶段,在这一行中,我们定义将使用一个缓冲区。我们可以使用更多的缓冲区,但一个就足够了。
我们将使用的缓冲区在第 21 行中声明,但我们只是在第 31 行定义了如何使用它。由于我们不希望缓冲区被 "垃圾" 填满,我们在第 32 行将其初始化为只包含零值。
正如你所看到的,与之前的文章相比,指标代码没有发生任何重大变化。但它现在有了两行新代码:第 39 行和第 47 行。这两行都调用了类中的同一个函数,我们很快就会看到。由于数量和参数不同,您可以认为这些是不同的函数。不过,你很快就会发现,它们都是一样的。为了理解这一点,让我们看看该类的完整代码,如下所示。
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.//+------------------------------------------------------------------+
C_ChartFloatingRAD 类源代码
我在文章中公布源代码似乎是在开玩笑,但这不是玩笑。事实上,我想尽可能详细地解释一下正在发生的事情。这样做并不是为了让你真正使用这些代码,而是为了让你理解这些代码并创造出更好的内容。
所以,让我们来弄清楚这到底是怎么回事。如果不了解这个类是如何运作的,就无法理解接下来要开发的内容。
我们一开始就有一些变化。在第 12 行,我们声明了一个公共子句。我们用它来使数据可以在类外访问。你可能会认为,我们可以把同样的数据放在其他地方。在这里,我只声明了一个结构和一个枚举。枚举对我们来说已经很熟悉了,但结构却是新的。在第 14 行和第 26 行之间,我们可以看到一个用于组织所需数据的结构声明。
这种结构大多比较容易理解,几乎不需要解释。不过,这里有些东西看起来很奇怪。第 20 行和第 24 行之间的联合有什么作用?第 25 行是做什么用的?联合将使我们能够更容易地传递数据。而且,第 25 行将在一个非常特殊的时刻使用。我们稍后再谈。
第 38 行提到了相同的结构。请记住:类中的数据不能在类外直接访问,因此,我们需要一些方法来访问这些数据。实际上,我们不会直接解决这个问题。从该指标的源代码中可以看出,并没有这种访问。接下来是第 49 行,它做了一件有趣的事情。它将作为一种选择器,但这一点稍后会更清楚。
在第 106 行,我们收集并调整了第 38 行结构中声明的第一个变量。请注意,这与我们之前所做的基本相同,但是我们现在使用的模型略有不同。
因此,在第 109 行,我们调整了其中一个变量,这是获利值点数的变量。请注意,我说的不是金融数值,而是点数。不要混淆这两种东西。我也不是指基于当前交易价格的点数,我说的是一般的点数。价格并不重要,重要的是我们有多少个偏移点数。同样重要的是,这一点数值随后将在金融方面进行调整。这一调整是在第 107 行完成的。
在第 110 行,我们也有类似的操作,只不过这次是针对止损值。正如第 107 行所做的那样,在考虑到第 109 行的点数之前调整金融值。在第 108 行,我们在考虑止损点之前调整止损金融数据。了解此时此刻正在发生的事情非常重要,如果您不能理解这一点,您就很难理解稍后下订单时所作的调整。
为了让您更容易理解,附件中包含了 Mouse 和 Chart Trade 指标以及一个相当简单的 EA 交易,这样您就可以了解发生了什么。出于安全考虑,附件中的 EA 不会下订单,只会打印输入的数值。这可以帮助您了解系统的实际运作方式。
现在,在第 157 行和第 177 行,同样出于实际考虑,我们还要调整一个变量的值。迄今为止,还没有进行过访问,我们要做的就是设置和调整变量的值。但看第 203 行,似乎有些奇怪。为什么要将该值保存在全局终端变量中?我们真的需要这样做吗,还是在浪费时间?事实上,这样做是必要的。原因是删除 Chart Trade 指标并重新加载到图表上时,内存中的所有值都会丢失。但这一数值对我们来说非常重要。
因此,我们在这里恢复之前保存的值。因此,我们在第 203 行恢复的这个值实际上是在第 274 行保存的。您可能已经注意到,我在解释中跳过了一些函数。那么,让我们一起回头看看它们吧。让我们从构造函数开始。是的,现在我们有了两个、而非一个类构造函数。其原因与我们在前一篇文章中讨论的 Mouse 指标相同。我们需要一种从缓冲区传输数据的方法。
说实话,我们并不是真的需要它。我们可以直接在 EA 或指标中实现这一点。不过,为了方便,我更喜欢把所有东西放在一起。使用这种方法,如果我以后需要做任何更改,只需修改 C_ChartFloatingRAD 类即可。我不必为实现模块标准化而对每个程序进行单独配置。回到构造函数,我们有一个从第 221 行开始的旧构造函数。基本上,它会从第 223 行获取数据,我们在该行初始化了指标名称。请注意:该名称不是用于向缓冲区写入数据的,而是用于从缓冲区读取数据的。此外,我们在第 232 行至第 234 行引入了新变量。不是全部,因为其余的都是根据模板设置过程配置的。
第二个构造函数非常简单,从第 215 行开始。在这里,我们主要赋值默认值。这发生在转换阶段。与 Mouse 指标一样,这里也有两个阶段:一个阶段是向缓冲区写入数据,另一个阶段是读取缓冲区。但这并不是因为编程出了问题。如果你在这里看到了一些错误,那么你可能没有理解我们在做什么。事实上,当我们使用指标时,我们会将数据写入缓冲区。这些数据由其他程序(通常是 EA)通过 CopyBuffer 读取。这就是为什么我们有两个阶段,也是为什么我们有两个构造函数。
与构造函数不同,我们只能有一个析构函数,它从第 243 行开始。我们在这里添加的唯一内容是第 245 行的代码。如果我们在指标中使用该类,当调用析构函数时,第 245 行的检查将通过,从而可以删除创建的对象。但如果该类用于读取指标缓冲区,第 245 行中的测试就会失败,然后阻止删除任何对象。这是一种简单而实用的机制。
之前是简单的部分,现在到了我们解释的关键部分。让我们看看如何进行交互,以及如何将数据保存到缓冲区和从缓冲区读取数据。这部分内容对于刚入门的人来说可能会有些困惑。因此,理解前几篇文章中讨论的概念非常重要。为了方便起见,我们来看看图 01。
图 01 - 交互图
在图 01 中,我们可以看到一个用于从指标向 EA 传输信息的通信系统。请注意,缓冲区实际上不是指标的一部分。尽管缓冲区是在指标中声明的,但它不应被视为指标内存的一部分。事实上,MetaTrader 5 支持这种方法,当我们从图表中删除指标时,后者会释放缓冲区所用的内存。但危险就在这里,数据实际上并没有被销毁,只是释放了内存。这样做的全部危险是什么,我试着用简单的语言来解释一下。
如果在 MetaTrader 5 中缓冲区所在的内存被释放 - 因为指标已从图表中删除,则在写入缓冲区后可能会出现这种情况。如果将指标放回到图表上,缓冲区可能仍包含一些数据。最后更新的时候可能会出现这种情况。因此,当 EA 使用 CopyBuffer 读取这些数据时,可能会读取到错误的数据。
这是非常严重的问题,我们在访问缓冲区时必须格外小心。为了在写入和读取之间提供一些同步,我们需要修改 C_ChartFloatingRAD 类中的 DispatchMessage 方法。写入缓冲区和读取缓冲区之间的同步非常重要,如果操作不当,用户提交的执行内容与实际执行内容之间就会出现延迟。也就是说,用户可能提交买入指令,但该指令不会被执行。但是,如果您在此之后立即发送卖出指令,则又会执行买入指令。这类故障并非由 MetaTrader 5 平台造成,而是由于误解了如何编写代码以确保正确的事件同步。
事实上,EA 和 Chart Trade 指标之间的代码没有任何联系。这种通信是通过使用缓冲区来实现的。因此,EA 不知道 Chart Trade 在做什么,就像 Chart Trade 不知道 EA 实际在做什么一样。不过,MetaTrader 5 知道这两者在做什么。由于指标和 EA 之间的共同点是 MetaTrader 5,因此我们使用 MetaTrader 5 进行操作。当您或用户觉得 EA 和 Chart Trade 指标是同一个程序时,这就就像变魔术一样。
还有一点同样重要,要知道,如果没有前面文章中提到的 Mouse 指标,Chart Trade 指标将不起作用。您需要在图表上使用所有三个应用程序:Mouse 指标、Chart Trade 指标和 EA,以及我们将来要讨论的其他东西。没有这一点,整个系统将什么都不能做。
这一切是如何工作的呢?所有这些都由位于 C_ChartFloatingRAD 类第 353 行的 CHARTEVENT_MOUSE_MOVE 事件来保证。如果 Mouse 指标不在图表上,则可以点击、编辑和修改 Chart Trade 指标的值。这是因为此类事件不一定与 Mouse 指标有关。但是,除非将 Mouse 指标放在图表上,否则您将无法发送买入、卖出、平仓指令或移动 Chart Trade 指标。
但等等,与 Chart Trade 交互时,是否需要在图表上显示 Mouse 指标?是的。虽然可以移除这种依赖关系,但如果以后要使用我将要谈到的其他方法,就会产生另外的问题。如果你想像我一样工作,就必须承受这些代价。因此,您必须充分了解该系统的工作原理,否则,您最终将无法信任该系统。
但让我们回到代码上来,了解我是如何实现系统同步的。要完全理解这一点,你需要了解指标源代码的作用,但如果你读过我在本文开头链接的文章,就应该不会有任何问题。因此,我们假设您已经了解了指标代码如何工作,这样我就可以专注于类的代码。
每次点击或移动鼠标,MetaTrader 5 都会生成一个事件。点击事件通常使用 CHARTEVENT_OBJECT_CLICK 来处理。但是,使用该处理函数无法保持同步。其中的原因解释起来相当复杂,它与执行操作的顺序有关。因此,为了在点击三个 Chart Trade 按钮(买入按钮、卖出按钮和平仓按钮)中的一个按钮时同时在 EA 中生成事件,我们采用了一些不同的方法。
因此,如果将 DispatchMessage 方法中的代码与前一篇文章中的相同代码进行比较,就会发现两者略有不同。不同之处在于处理上述按钮点击的方式。在以前的版本中,这种点击是在 CHARTEVENT_OBJECT_CLICK 事件中处理的,而现在我们是在 CHARTEVENT_MOUSE_MOVE 事件中处理的。由于代码本身会告诉我们哪个对象被点击了,所以我创建了一个新变量,使事情变得更有条理。该变量在第 340 行声明,其值在第 354 行设置。
现在请仔细听我接下来的解释。在第 376 至 378 行中,我们放置了按钮代码。因此,当 Mouse 指标向我们发送数据时,我们就可以向 EA 发送执行命令。但这里有一个小细节。如何告知 EA 按了哪个按钮?这很简单。我们将按钮的代码发送给 EA。这是在第 380 行完成的。现在,我们在第 381 行记录分时报价的数量,以生成一个唯一的数字。这对 EA 是必需的,你很快就会明白它到底是如何工作的。
因此,对于生成的每个事件,除了调用 DispatchMessage 函数准备将数据发送到缓冲区外,我们还将调用另一个函数。就是将数据实际放入缓冲区的那个函数。您可以从该类的第 279 行开始查看其代码。现在让我们看看第 281 行,该行有一个静态变量。它将存储 OnCalculate 事件传递的值。也就是说,我们需要保存 rates_total 的值,原因前面已经解释过了。请阅读开头列出的文章,了解其中的原因。这样,当 OnChartEvent 处理函数被调用时,我们就知道该把数据放在缓冲区的哪个位置了。
请注意,在第 284 行,我们执行了一项检查,以确保只有 Chart Trade 指标会向内存写入数据。此外,只有按下所需的一个按钮时,才会向内存中做记录。这一切都非常好。但是,如果 EA 无法解释 Chart Trade 指标发送给它的数据,这些都没有任何价值。为了了解 EA 如何解释数据,我们需要考虑一些其他代码,这些代码将尽可能基础。
使用测试 EA 交易
既然一切都需要测试,我们就必须确保测试的正确性,清楚地展示实际发生的情况。在这种情况下,为了测试 Chart Trade 指标、Mouse 指标和 EA 之间的交互作用,我们需要使用一个非常简单的系统。但与此同时,我们需要这个系统除了简单之外,还能以我们将来实际使用的方式运行。
根据这一标准,我们将使用以下代码:
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. //+------------------------------------------------------------------+
测试 EA 交易的源代码
请注意,这段代码非常简单、紧凑,大部分内容不言自明。不过,有些读者,尤其是初学者,可能无法真正理解它的工作原理。不必为此难过,所有初学者都会遇到类似的困难。但是,只要你努力学习,投入时间,严于律己,不断进取,将来一定会成为一名优秀的专业人员。向困难屈服是不专业的。
现在让我们快速看看代码,因为只有几行,应该很容易理解。
上一主题中介绍的 C_ChartFloatingRAD 类中的所有代码在 EA 这里使用时都进行了总结。虽然我们在第 7 行完全包含了该类,但编译器并不完全这么认为。因此,在第 9 行,我们在全局声明了指针。它将用于访问 C_ChartFloatingRAD 类。这似乎令人困惑,但这只是因为你认为访问该类的方式与访问指标代码的方式相同。
事实上,这样做也是可行的,只是不太实用。原因是该类不打算在没有指标的情况下使用。C_Mouse 类也是如此,该类用于 Mouse 指标,不应在非指标代码中使用。我知道,很多人可能都想这么做。但你不应该这样做。因为所有预期的安全性、建模和性能都不是为了在原始代码之外的代码中使用类。也就是说,如果在 EA 中采用与指标相同的编码方法,实际上就可以达到无需使用 Chart Trade 指标的目的。这是事实。
但是,如果将指标代码转移到 EA 中,整个系统的稳定性和安全性可能会出现问题,因为 EA 的设计并不具备这种稳定性。万物皆有其位置。因此,当我们在第 13 行调用构造函数创建指针时,请注意我使用了与 Chart Trade 指标名称相同的名称。这样,指标和 EA 都能完成各自的工作 。每个组件都各就各位。如果其中一个出现问题,只需在图表上重新启动即可。
现在请注意,几乎所有的代码都可以归结为这一点。指定指标名称,然后在 OnChartEvent 事件处理程序中捕获 CHARTEVENT_OBJECT_CLICK 事件并分析发生了什么。这里有一点很重要。每次点击鼠标时,都会在对象上产生一个点击事件,即使你认为你没有点击任何重要的东西。原因是 Mouse 指标必须始终在图表上。该指标有一条水平线,这就是对象。因此,只要您点击鼠标,这条水平线就会产生一个事件。
但是,系统如何区分对这条线的点击和对另一个对象的点击呢?这是一个相当有趣的问题,很快将成为另一篇文章的主题。不过,在图表上使用不同对象时要注意这一点,主要是因为我们即将添加其他指标。但这是未来的事情。
问题在于第 33 行,对象点击事件将尝试读取 Chart Trade 指标缓冲区的内容。如果成功,我们将获得返回的数据。然后,在第 34 行,我们将检查这是否是与 Chart Trade 相关的事件,还是其他可以忽略的事件。
如果 Chart Trade 中确实产生了一个事件,而且该事件是由点击订单系统交互按钮引起的,我们就会像第 36 行那样更新静态变量的值。之后,我们将向终端输出一条信息,以便分析发生了什么。如果由于某种原因需要忽略该事件,我们就将执行第 38 行。
结论
视频 01 展示了该系统的实际运行情况。然而,亲眼目睹系统运行的机会是任何东西都无法替代的。因此,文章附件中包含了当前状态的系统。
视频 01 - 演示
本文由MetaQuotes Ltd译自葡萄牙语
原文地址: https://www.mql5.com/pt/articles/11760
注意: MetaQuotes Ltd.将保留所有关于这些材料的权利。全部或部分复制或者转载这些材料将被禁止。
This article was written by a user of the site and reflects their personal views. MetaQuotes Ltd is not responsible for the accuracy of the information presented, nor for any consequences resulting from the use of the solutions, strategies or recommendations described.

