
开发回放系统(第 41 部分):启动第二阶段(二)
概述
在上一篇文章 开发回放系统(第 40 部分):启动第二阶段(一) 中,我们开始创建一个支持研究系统和鼠标的指标。但在开始之前,我想问大家一个问题:你们能想象我们为什么要创建这个指标吗?我们演示如何制作这样的东西,背后的意图是什么?我为鼠标创建指标的目的是什么?
也许这些问题没有多大意义,但你是否曾在某些时候停下来观察过自己的代码?你有没有注意到,你用不同的代码重复了多少次同样的事情?然而,不同代码之间没有一致性或稳定性。请不要误会。我不应该告诉有多年编程经验的人该做什么或不该做什么。我不会说你以某种方式错误地使用了MQL5语言或你所擅长的任何其他语言。
我只是想让拥有多年编程经验的读者停止一遍又一遍地做同样的事情。也许,是时候仔细审视一下自己的工作了。不要再因为缺乏周密计划而重复做同样的事情。
事实上,在回放/模拟系统中,新阶段的开始背后隐藏着一个原因。我们已经在另一篇文章中讨论过这个问题。我已经厌倦了在 MQL5 中用老方法做所有事情。我的个人的代码遵循一定的规则,不过,这些文章中所附的代码不同。我总是尽量让它们简单明了,但我经常看到有人说一些 MQL5 或 MetaTrader 5 的意见。
我想证明,我们有可能比其它语言做得更多。所以,准备好,别再开玩笑了。从现在开始,我们来看看我的代码到底是什么样的。我不会演示有关某些资产或指标的具体内容,但我会向您展示,MQL5 和 MetaTrader 5 一样,其功能远远超出您的想象。
在上一篇文章中,我提到有一个问题还需要解决。那么,我们就从解决这个问题开始吧。
修改 C_Mouse 类
这里的问题是如何与鼠标指针交互。上一篇文章中显示的代码已最终确定,至少在相当长的一段时间内,该指标不会发生任何变化。然而,当我们需要使用我们的数据时,有一个问题使事情变得更加复杂。
为了让你深入了解我要做的事情,你必须放弃你对 MetaTrader 5 和 MQL5 语言所了解的想法和概念。忘掉你所知道的使用它们的一切,让我们换一种思维方式。如果您能理解这一点,我们就能进行一种编码,使您的代码越来越高效、稳定。
在前几篇文章中,我们谈到在 MetaTrader 5 中运行的进程不应视为指标、EA 交易、脚本或服务,而应视为函数。函数的概念可能不是最好的说法,不过,有一个定义是最合适的:DLL(动态链接库)。是的,将 MetaTrader 5 中运行的所有进程(尤其是指标)视为 DLL。
我为什么要这么说?因为 DLL 所涉及的概念同样适用于我将在这里展示的内容。目前,我们有的唯一指标就是鼠标。这样你是无法完整、全面地了解将会发生什么的。然而,任何伟大的建筑总是从奠定基石开始的。我们现在马上就去做。但是,由于一些问题,我们必须完全简化工作,以免造成完全混乱。
如果您阅读了开发回放系统(第 31 部分):EA 交易项目 - C_Mouse 类 (五) 这篇文章,你会注意到 C_Mouse 类与 C_Study 类一样,以同样方式工作。但是,这些类的工作方式并不允许我们使用简单的编程来使用这些类开发指标。注意:我不是说我们不能充分利用它,只是说这需要极其复杂的编程。
我们的想法是简化这一程序。为此,我们将从 C_Study 类中删除一些元素,并将它们移到 C_Mouse 类中。不过,我们还将在编程和结构方面对 C_Mouse 类进行补充,以使指标更易于使用,我们将在接下来的文章中看到这一点。
我们将要在 C_Study 类中删除的内容可以从下面的代码中看到。
C_Study 类的一部分代码:
01. #property copyright "Daniel Jose" 02. //+------------------------------------------------------------------+ 03. #include "..\C_Mouse.mqh" 04. #include "..\..\Auxiliar\C_Mouse.mqh" 05. #include "..\..\Auxiliar\Interprocess.mqh" 06. //+------------------------------------------------------------------+ 07. #define def_ExpansionPrefix "MouseExpansion_" 08. #define def_ExpansionBtn1 def_ExpansionPrefix + "B1" 09. #define def_ExpansionBtn2 def_ExpansionPrefix + "B2" 10. #define def_ExpansionBtn3 def_ExpansionPrefix + "B3" 11. //+------------------------------------------------------------------+ 12. #define def_AcessTerminal (*Terminal) 13. #define def_InfoTerminal def_AcessTerminal.GetInfoTerminal() 14. //+------------------------------------------------------------------+ 15. class C_Study : public C_Mouse 16. { 17. protected: 18. private : 19. //+------------------------------------------------------------------+ 20. enum eStatusMarket {eCloseMarket, eAuction, eInTrading, eInReplay}; 21. //+------------------------------------------------------------------+ 22. struct st00 23. { 24. eStatusMarket Status; 25. MqlRates Rate; 26. string szInfo; 27. color corP, 28. corN; 29. int HeightText; 30. }m_Info;
头文件 C_Study.mqh 的位置已更改。我们用第 03 行替换了第 04 行,由于不再引用 InterProcess.mqh 头文件,因此也删除了第 05 行。
我们还删除了第 20 行,并将其包含在 C_Mouse 类中。迁移的目的是为了方便后期编程。现在,我们已经看到了 C_Study 类所做的更改,接下来让我们看看 C_Mouse 类,这里的更改要广泛得多。
下面是新的 C_Mouse 类的全部代码。
C_Mouse.mqh 文件代码:
001. //+------------------------------------------------------------------+ 002. #property copyright "Daniel Jose" 003. //+------------------------------------------------------------------+ 004. #include "C_Terminal.mqh" 005. #include "Interprocess.mqh" 006. //+------------------------------------------------------------------+ 007. #define def_MousePrefixName "MouseBase_" 008. #define def_NameObjectLineH def_MousePrefixName + "H" 009. #define def_NameObjectLineV def_MousePrefixName + "TV" 010. #define def_NameObjectLineT def_MousePrefixName + "TT" 011. #define def_NameObjectStudy def_MousePrefixName + "TB" 012. //+------------------------------------------------------------------+ 013. #define def_AcessTerminal (*Terminal) 014. #define def_InfoTerminal def_AcessTerminal.GetInfoTerminal() 015. //+------------------------------------------------------------------+ 016. class C_Mouse 017. { 018. public : 019. enum eStatusMarket {eCloseMarket, eAuction, eInTrading, eInReplay}; 020. enum eBtnMouse {eKeyNull = 0x00, eClickLeft = 0x01, eClickRight = 0x02, eSHIFT_Press = 0x04, eCTRL_Press = 0x08, eClickMiddle = 0x10}; 021. struct st_Mouse 022. { 023. struct st00 024. { 025. int X, 026. Y; 027. double Price; 028. datetime dt; 029. }Position; 030. uint ButtonStatus; 031. bool ExecStudy; 032. }; 033. //+------------------------------------------------------------------+ 034. protected: 035. enum eEventsMouse {ev_HideMouse, ev_ShowMouse}; 036. //+------------------------------------------------------------------+ 037. void CreateObjectInfo(int x, int w, string szName, color backColor = clrNONE) const 038. { 039. if (m_Mem.IsTranslator) return; 040. def_AcessTerminal.CreateObjectGraphics(szName, OBJ_BUTTON, clrNONE); 041. ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_STATE, true); 042. ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_BORDER_COLOR, clrBlack); 043. ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_COLOR, clrBlack); 044. ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_BGCOLOR, backColor); 045. ObjectSetString(def_InfoTerminal.ID, szName, OBJPROP_FONT, "Lucida Console"); 046. ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_FONTSIZE, 10); 047. ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_CORNER, CORNER_LEFT_UPPER); 048. ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_XDISTANCE, x); 049. ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_YDISTANCE, TerminalInfoInteger(TERMINAL_SCREEN_HEIGHT) + 1); 050. ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_XSIZE, w); 051. ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_YSIZE, 18); 052. } 053. //+------------------------------------------------------------------+ 054. private : 055. enum eStudy {eStudyNull, eStudyCreate, eStudyExecute}; 056. struct st01 057. { 058. st_Mouse Data; 059. color corLineH, 060. corTrendP, 061. corTrendN; 062. eStudy Study; 063. }m_Info; 064. struct st_Mem 065. { 066. bool CrossHair, 067. IsFull, 068. IsTranslator; 069. datetime dt; 070. string szShortName; 071. }m_Mem; 072. //+------------------------------------------------------------------+ 073. C_Terminal *Terminal; 074. //+------------------------------------------------------------------+ 075. void GetDimensionText(const string szArg, int &w, int &h) 076. { 077. TextSetFont("Lucida Console", -100, FW_NORMAL); 078. TextGetSize(szArg, w, h); 079. h += 5; 080. w += 5; 081. } 082. //+------------------------------------------------------------------+ 083. void CreateStudy(void) 084. { 085. if (m_Mem.IsFull) 086. { 087. def_AcessTerminal.CreateObjectGraphics(def_NameObjectLineV, OBJ_VLINE, m_Info.corLineH); 088. def_AcessTerminal.CreateObjectGraphics(def_NameObjectLineT, OBJ_TREND, m_Info.corLineH); 089. ObjectSetInteger(def_InfoTerminal.ID, def_NameObjectLineT, OBJPROP_WIDTH, 2); 090. CreateObjectInfo(0, 0, def_NameObjectStudy); 091. } 092. m_Info.Study = eStudyCreate; 093. } 094. //+------------------------------------------------------------------+ 095. void ExecuteStudy(const double memPrice) 096. { 097. double v1 = GetInfoMouse().Position.Price - memPrice; 098. int w, h; 099. 100. if (!CheckClick(eClickLeft)) 101. { 102. m_Info.Study = eStudyNull; 103. ChartSetInteger(def_InfoTerminal.ID, CHART_MOUSE_SCROLL, true); 104. if (m_Mem.IsFull) ObjectsDeleteAll(def_InfoTerminal.ID, def_MousePrefixName + "T"); 105. }else if (m_Mem.IsFull) 106. { 107. string sz1 = StringFormat(" %." + (string)def_InfoTerminal.nDigits + "f [ %d ] %02.02f%% ", 108. MathAbs(v1), Bars(def_InfoTerminal.szSymbol, PERIOD_CURRENT, m_Mem.dt, GetInfoMouse().Position.dt) - 1, MathAbs((v1 / memPrice) * 100.0))); 109. GetDimensionText(sz1, w, h); 110. ObjectSetString(def_InfoTerminal.ID, def_NameObjectStudy, OBJPROP_TEXT, sz1); 111. ObjectSetInteger(def_InfoTerminal.ID, def_NameObjectStudy, OBJPROP_BGCOLOR, (v1 < 0 ? m_Info.corTrendN : m_Info.corTrendP)); 112. ObjectSetInteger(def_InfoTerminal.ID, def_NameObjectStudy, OBJPROP_XSIZE, w); 113. ObjectSetInteger(def_InfoTerminal.ID, def_NameObjectStudy, OBJPROP_YSIZE, h); 114. ObjectSetInteger(def_InfoTerminal.ID, def_NameObjectStudy, OBJPROP_XDISTANCE, GetInfoMouse().Position.X - w); 115. ObjectSetInteger(def_InfoTerminal.ID, def_NameObjectStudy, OBJPROP_YDISTANCE, GetInfoMouse().Position.Y - (v1 < 0 ? 1 : h)); 116. ObjectMove(def_InfoTerminal.ID, def_NameObjectLineT, 1, GetInfoMouse().Position.dt, GetInfoMouse().Position.Price); 117. ObjectSetInteger(def_InfoTerminal.ID, def_NameObjectLineT, OBJPROP_COLOR, (memPrice > GetInfoMouse().Position.Price ? m_Info.corTrendN : m_Info.corTrendP)); 118. } 119. m_Info.Data.ButtonStatus = eKeyNull; 120. } 121. //+------------------------------------------------------------------+ 122. public : 123. //+------------------------------------------------------------------+ 124. C_Mouse(const string szShortName) 125. { 126. Terminal = NULL; 127. m_Mem.IsTranslator = true; 128. m_Mem.szShortName = szShortName; 129. } 130. //+------------------------------------------------------------------+ 131. C_Mouse(C_Terminal *arg, color corH = clrNONE, color corP = clrNONE, color corN = clrNONE) 132. { 133. m_Mem.IsTranslator = false; 134. Terminal = arg; 135. if (CheckPointer(Terminal) == POINTER_INVALID) SetUserError(C_Terminal::ERR_PointerInvalid); 136. if (_LastError != ERR_SUCCESS) return; 137. m_Mem.CrossHair = (bool)ChartGetInteger(def_InfoTerminal.ID, CHART_CROSSHAIR_TOOL); 138. ChartSetInteger(def_InfoTerminal.ID, CHART_EVENT_MOUSE_MOVE, true); 139. ChartSetInteger(def_InfoTerminal.ID, CHART_CROSSHAIR_TOOL, false); 140. ZeroMemory(m_Info); 141. m_Info.corLineH = corH; 142. m_Info.corTrendP = corP; 143. m_Info.corTrendN = corN; 144. m_Info.Study = eStudyNull; 145. if (m_Mem.IsFull = (corP != clrNONE) && (corH != clrNONE) && (corN != clrNONE)) 146. def_AcessTerminal.CreateObjectGraphics(def_NameObjectLineH, OBJ_HLINE, m_Info.corLineH); 147. } 148. //+------------------------------------------------------------------+ 149. ~C_Mouse() 150. { 151. if (CheckPointer(Terminal) == POINTER_INVALID) return; 152. ChartSetInteger(def_InfoTerminal.ID, CHART_EVENT_OBJECT_DELETE, 0, false); 153. ChartSetInteger(def_InfoTerminal.ID, CHART_EVENT_MOUSE_MOVE, false); 154. ChartSetInteger(def_InfoTerminal.ID, CHART_CROSSHAIR_TOOL, m_Mem.CrossHair); 155. ObjectsDeleteAll(def_InfoTerminal.ID, def_MousePrefixName); 156. } 157. //+------------------------------------------------------------------+ 158. inline bool CheckClick(const eBtnMouse value) 159. { 160. return (GetInfoMouse().ButtonStatus & value) == value; 161. } 162. //+------------------------------------------------------------------+ 163. inline const st_Mouse GetInfoMouse(void) 164. { 165. if (m_Mem.IsTranslator) 166. { 167. double Buff[]; 168. uCast_Double loc; 169. int handle = ChartIndicatorGet(ChartID(), 0, m_Mem.szShortName); 170. 171. ZeroMemory(m_Info.Data); 172. if (CopyBuffer(handle, 0, 0, 4, Buff) == 4) 173. { 174. m_Info.Data.Position.Price = Buff[0]; 175. loc.dValue = Buff[1]; 176. m_Info.Data.Position.dt = loc._datetime; 177. loc.dValue = Buff[2]; 178. m_Info.Data.Position.X = loc._int[0]; 179. m_Info.Data.Position.Y = loc._int[1]; 180. loc.dValue = Buff[3]; 181. m_Info.Data.ButtonStatus = loc._char[0]; 182. IndicatorRelease(handle); 183. } 184. } 185. 186. return m_Info.Data; 187. } 188. //+------------------------------------------------------------------+ 189. virtual void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam) 190. { 191. int w = 0; 192. static double memPrice = 0; 193. 194. if (!m_Mem.IsTranslator) switch (id) 195. { 196. case (CHARTEVENT_CUSTOM + ev_HideMouse): 197. if (m_Mem.IsFull) ObjectSetInteger(def_InfoTerminal.ID, def_NameObjectLineH, OBJPROP_COLOR, clrNONE); 198. break; 199. case (CHARTEVENT_CUSTOM + ev_ShowMouse): 200. if (m_Mem.IsFull) ObjectSetInteger(def_InfoTerminal.ID, def_NameObjectLineH, OBJPROP_COLOR, m_Info.corLineH); 201. break; 202. case CHARTEVENT_MOUSE_MOVE: 203. ChartXYToTimePrice(def_InfoTerminal.ID, m_Info.Data.Position.X = (int)lparam, m_Info.Data.Position.Y = (int)dparam, w, m_Info.Data.Position.dt, m_Info.Data.Position.Price); 204. if (m_Mem.IsFull) ObjectMove(def_InfoTerminal.ID, def_NameObjectLineH, 0, 0, m_Info.Data.Position.Price = def_AcessTerminal.AdjustPrice(m_Info.Data.Position.Price)); 205. m_Info.Data.Position.dt = def_AcessTerminal.AdjustTime(m_Info.Data.Position.dt); 206. ChartTimePriceToXY(def_InfoTerminal.ID, w, m_Info.Data.Position.dt, m_Info.Data.Position.Price, m_Info.Data.Position.X, m_Info.Data.Position.Y); 207. if ((m_Info.Study != eStudyNull) && (m_Mem.IsFull)) ObjectMove(def_InfoTerminal.ID, def_NameObjectLineV, 0, m_Info.Data.Position.dt, 0); 208. m_Info.Data.ButtonStatus = (uint) sparam; 209. if (CheckClick(eClickMiddle)) 210. if ((!m_Mem.IsFull) || ((color)ObjectGetInteger(def_InfoTerminal.ID, def_NameObjectLineH, OBJPROP_COLOR) != clrNONE)) CreateStudy(); 211. if (CheckClick(eClickLeft) && (m_Info.Study == eStudyCreate)) 212. { 213. ChartSetInteger(def_InfoTerminal.ID, CHART_MOUSE_SCROLL, false); 214. if (m_Mem.IsFull) ObjectMove(def_InfoTerminal.ID, def_NameObjectLineT, 0, m_Mem.dt = GetInfoMouse().Position.dt, memPrice = GetInfoMouse().Position.Price); 215. m_Info.Study = eStudyExecute; 216. } 217. if (m_Info.Study == eStudyExecute) ExecuteStudy(memPrice); 218. m_Info.Data.ExecStudy = m_Info.Study == eStudyExecute; 219. break; 220. case CHARTEVENT_OBJECT_DELETE: 221. if ((m_Mem.IsFull) && (sparam == def_NameObjectLineH)) def_AcessTerminal.CreateObjectGraphics(def_NameObjectLineH, OBJ_HLINE, m_Info.corLineH); 222. break; 223. } 224. } 225. //+------------------------------------------------------------------+ 226. }; 227. //+------------------------------------------------------------------+ 228. #undef def_AcessTerminal 229. #undef def_InfoTerminal 230. //+------------------------------------------------------------------+ 231. #undef def_MousePrefixName 232. #undef def_NameObjectLineV 233. #undef def_NameObjectLineH 234. #undef def_NameObjectLineT 235. #undef def_NameObjectStudy 236. //+------------------------------------------------------------------+ 237.
我之所以在这里提供完整的代码,是因为应用程序中不会有任何文件(至少现在不会)。因此,任何想利用这些改进的人都可以通过使用提供的代码来实现。当系统进入更高级阶段时,我们将再次获得回报,但目前还不能使用。
如果您查看一下代码,就会发现有一些突出显示的地方。虽然它们是有一定意义的,但我不打算详述。原因是,它们中的大多数都是不言自明的。
从 C_Study 类第 20 行移除的代码现在位于 C_Mouse 类的第 19 行。现在,查看 C_Mouse.mqh 文件的第 18 行。我在此声明公有部分。但我们为什么需要它呢?因为没有它,任何信息都没有达到我们所需的访问级别。我们确实需要公开这些数据。通常我们只使用这部分内容一次,但由于我的习惯是先处理受保护数据,其次处理私有数据,最后处理公共数据,所以你会在第 122 行看到这部分内容。这是我习惯的编程风格造成的。
在第 20 行,我们还看到了 C_Mouse 类中已经存在但未设为公有的另一个枚举,以及一个从第 21 行开始一直到第 32 行的结构。在这个特例中,该结构没有用于任何全局公有变量。在类中声明公有全局变量不是一种好的做法。任何全局类变量都必须是私有或受保护的,而绝不能是公有的。
从第 34 行开始,数据不再是公有的。这里是受保护部分,确保数据、函数和过程得到保护。我们在以前的文章中已经看到过这一点,但这不是重点。问题出在第 39 行的检查中。以前没有这种检查。如果被检查变量的值为 "true",则不会创建CreateObjectInfo方法中所创建的对象。我们为什么要这样做?要理解这一点,我们需要仔细看看代码。
接下来,我们发现有两行声明了两个变量:第 68 行和第 70 行。最初,它们不在发布的代码中。但在目前这种情况下,这些变量是必要的。其中一个原因见第 39 行:用于允许或禁止执行方法或函数。第 165 和 194 行也是如此。对于第 165 行而言,有一个更强烈的动机,我们稍后将对此进行探讨。不过,在第 194 行中,原因与第 39 行相同:避免执行函数或方法,因为在翻译模式下,函数或方法无法正常工作或执行。
正如我们所料,这些变量在类的构造函数中被初始化,这是事实。但与此同时,我们不再是有一个类构造函数,现在我们的 C_Mouse 类有两个构造函数😱😵😲。如果你是 OOP 编程的新手,这可能会吓到你。不过,这对于 OOP 来说是完全正常的。其中一个构造函数的代码写在第 124 行和第 129 行之间。另一个构造函数位于第 131 和 147 行之间。虽然我们有两个构造函数,但只有一个析构函数。
由于其中一个构造函数将用于一个目的,而第二个构造函数将用于另一个目的,因此我们必须将它们分开。为此我们使用了私有全局变量。没有它们,我们就很难进行这样的分离。
现在让我们来看看为什么会有两个构造函数。首先,为了满足我们的需求而设立第二个类是没有意义的。其次,我们可以用这种方法控制执行流程,以便重复使用现有代码。所有这些都可以在无需创建继承或操作数据、函数和过程的情况下完成。
如果你仔细观察,就会发现我们迄今为止所做的只是隔离了两部分代码:一个是公有代码,另一个是受保护代码。因此,如果我们使用 C_Mouse 类来达到某种目的,我们就会有一个用法模型。但是,当涉及到实现其他类型的目的时,我们就会有不同的模型。但无论我们在函数中使用什么类,使用类进行编程的方式都是一样的。
请看第 131 行和第 147 行之间的构造函数。你可能注意到了,它与"开发回放系统(第 31 部分):EA 交易项目 - C_Mouse 类 (五)" 中介绍的几乎没有什么不同。我们是故意这样做的,正是因为当代码使用类中的对象时,这个构造函数将负责实现类的目的。该构造函数与原始构造函数的不同之处在于第 133 行,它初始化了一个新变量,表明我们将使用该类的原有形式。
这种情况的发生意味着大部分代码保持不变,本文中提供的任何解释仍然具有相关性。接下来,我们可以重点讨论使用该类的第二种方法。为此,首先查看第 124 行到第 129 行之间的构造函数代码。尽管很简单,但需要注意的是,我们正在初始化所有实际需要的变量。
这样就更容易将该类用作翻译器。也许你不明白如何或为什么要这样做,但我们将使用 C_Mouse 类作为鼠标指针翻译器。这看起来可能相当复杂和困难,不是吗?但这一点也不难。这一切都是有原因的。
要理解这一点,你需要稍微思考一下:当我们创建程序时,无论是指标、EA 交易、脚本还是其他任何程序,我们都倾向于添加一些经常重复的内容。其中之一就是鼠标处理过程。如果在每个 EA 中,我们都必须添加函数来生成研究或进行鼠标分析,那么我们的代码将永远无法真正可靠。但是,当我们创建的东西要长期使用时,就可以独立于其他代码对其进行改进。这样,我们就会有一种新的程序。代码自然会变得更加可靠和高效。
在本类中,我们将始终假定鼠标指针会出现在图表上。记住:我们不应该假设任何事情,但在这里,我们将假设指标将出现在图表上。使用 C_Mouse 类作为翻译器的人必须了解这一事实。这个类将假定指标位于图表上.
如果一切都清楚了,您就可以继续下一个问题。事实上,我们并不需要 C_Mouse 类为我们翻译指标,因为这可以直接在创建的程序中完成。不过,让该类帮我们完成翻译工作要容易得多,也方便得多。原因是,如果我们不想使用翻译器,只需更改类的构造函数,并在程序中添加事件处理调用即可。
到那时,你就会明白,完成任务会变得多么容易。但我还是希望你们使用翻译系统。要了解翻译系统的工作原理,只需查看第 163 行的 GetInfoMouse 函数。起初,它是一个常值函数,但现在已不再是了。虽然数据不能在函数外部更改,但我们需要在函数内部更改数据。
让我们回到上一篇文章开发回放系统(第 40 部分):启动第二阶段 (一)中的讨论,你会发现,要解释指标缓冲区数据非常困难,或者说要保持解释指标缓冲区数据的编码标准非常困难。因此,我将向你展示如何修改 C_Mouse 类来创建这种标准化。想象一下每次需要使用鼠标指针时,我们都必须写出与在函数 CheckClick 和 GetInfoMouse 中看到的相同的内容,就知道这有多难了。这可真够麻烦的。
让我们看看这里发生了什么。让我们从 CheckClick 函数开始。在这个函数中,我们只需将数值加载到鼠标数据中并进行检查,可以是指针,也可以是翻译器。无论是分析指标还是使用指标本身,我们都会这样做。在第 160 行,我们检查所分析的按钮是否被激活,即是否被按下。
这是需要理解的问题。无论我们将 C_Mouse 类用作翻译器还是指针,我们都会从 CheckClick 和 GetInfoMouse 函数中收到某种响应,总是如此。无论我们在哪里使用这些信息,这个答案总是代表鼠标正在做的事情。
要理解 CheckClick 所给出的答案,我们必须遵循并理解 GetInfoMouse 函数的工作原理。从第 163 行开始可以看到该函数。
在函数的第 165 行,我们要检查是将类作为翻译器使用,还是作为指针使用。如果检查通过,类将进入翻译器模式,然后我们需要访问指针缓冲区。这个问题既复杂又简单。我们先来看看简单的部分。
在第 171 行,我们重置了返回的结构数据。它实际上是一个私有的全局类变量,在第 58 行进行了声明。然后,我们要求 MetaTrader 5 读取指标缓冲区。如果从第 172 行读取的数据正确,我们就开始将数据转换为返回的结构。
数据转换遵循与数据写入相同的逻辑。相关代码可参见前一篇文章中的 SetBuffer 函数。为了方便起见,我在下面提供了相同的函数。
指标代码片段:
102. inline void SetBuffer(void) 103. { 104. uCast_Double Info; 105. 106. m_posBuff = (m_posBuff < 0 ? 0 : m_posBuff); 107. m_Buff[m_posBuff + 0] = (*Study).GetInfoMouse().Position.Price; 108. Info._datetime = (*Study).GetInfoMouse().Position.dt; 109. m_Buff[m_posBuff + 1] = Info.dValue; 110. Info._int[0] = (*Study).GetInfoMouse().Position.X; 111. Info._int[1] = (*Study).GetInfoMouse().Position.Y; 112. m_Buff[m_posBuff + 2] = Info.dValue; 113. Info._char[0] = ((*Study).GetInfoMouse().ExecStudy == C_Mouse::eStudyNull ? (char)(*Study).GetInfoMouse().ButtonStatus : 0); 114. m_Buff[m_posBuff + 3] = Info.dValue; 115. }
请注意,指标片段第 107 行中的信息已在类的第 174 行中解码。大多数情况下,翻译非常简单,但您必须遵守编码时制定的规则。如果将数据保存在指标片段的第 112 行,就能更好地理解这一点。请注意,在这种情况下,我们使用的是压缩为一个 double 型的两个值。说到翻译,我们必须反其道而行之。我们在类的第 177 行捕获值,在第 178 和 179 行将值放到正确的位置,以便以后使用。
同样的情况也发生在指标片段的第 113 行,我们在该行存储了鼠标点击值。然后,我们在类的第 181 行进行翻译。但现在让我们再看看指标片段的第 113 行。请注意,如果我们处于研究模式,三元运算符的值将保持为零。你必须明白这一点。如果我们通过指标进行研究,并使用类进行翻译,那么当我们通过 CheckClick 函数检查点击时,它将返回 false。当然,如果我们使用一个指标和一个类作为翻译器,这种情况总会发生。
这是最简单、最容易理解的部分,但如上所述,还有另一部分:复杂而困难。
当我们使用一个类作为翻译器时,它就会出现。但是,我们无法访问指标缓冲区。这种情况通常发生在从图表中移除指标的时候。发生这种情况时,第 169 行将生成一个空处理程序,我们将没有缓冲区可读取。我们仍需运行第 171 行,以便恢复数据。
这可能会导致在尝试使用指标数据时出现各种冲突和失败。虽然系统总是报告为零,我们实际上不会有任何点击或移动的正面证据,但我们仍然会遇到这样的问题。在这种特殊情况下不会,但在其他情况下也会给我们带来问题。一旦发生这种情况,我们将重新讨论这个问题。
将 C_Mouse 类用作翻译器
以下部分主要用于演示,因为我们稍后会对这一主题进行扩展。
让我们来看看 EA 交易,其工作方式与文章开发回放系统(第 31 部分):EA 交易项目 - C_Mouse 类 (五)中相同 ,但其代码将以不同的方式编写。
EA 交易代码:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. #property description "Generic EA for use on Demo account, replay system/simulator and Real account." 04. #property description "This system has means of sending orders using the mouse and keyboard combination." 05. #property description "For more information see the article about the system." 06. #property version "1.41" 07. #property icon "/Images/Market Replay/Icons/Replay - EA.ico" 08. #property link "https://www.mql5.com/en/articles/11607" 09. //+------------------------------------------------------------------+ 10. #include <Market Replay\Auxiliar\C_Mouse.mqh> 11. //+------------------------------------------------------------------+ 12. C_Mouse *mouse = NULL; 13. //+------------------------------------------------------------------+ 14. int OnInit() 15. { 16. mouse = new C_Mouse("Indicator Mouse Study"); 17. 18. return INIT_SUCCEEDED; 19. } 20. //+------------------------------------------------------------------+ 21. void OnTick() 22. { } 23. //+------------------------------------------------------------------+ 24. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 25. { 26. C_Mouse::st_Mouse Infos; 27. 28. switch (id) 29. { 30. case CHARTEVENT_MOUSE_MOVE: 31. Infos = (*mouse).GetInfoMouse(); 32. Comment((string)Infos.Position.Price + " :: [" + TimeToString(Infos.Position.dt), "]"); 33. break; 34. } 35. } 36. //+------------------------------------------------------------------+ 37. void OnDeinit(const int reason) 38. { 39. delete mouse; 40. } 41. //+------------------------------------------------------------------+
虽然看起来不像,但上面的代码与上述文章中的代码具有相同的行为,而且更简单、更实用、更可靠。这其中的原因现在还不容易理解,但随着我们在编码之旅中不断前进,你就会慢慢明白的。
你可能理解,事实上我们并没有做什么超级复杂的事情。我们在此假定,图表上将显示所有内容。在第 16 行,我们指出了指标的短名称,在第 39 行,我们删除了 C_Mouse 类。
现在请看第 31 行和第 32 行,这两行将通知鼠标有关图表数据,以便我们查看。
最棒的是,如果我们在第 16 行声明将该类用作指标,那么只需在 OnChatEvent 函数中添加对 DispatchMessage 函数的调用,就能获得与使用图形指标相同的效果。换句话说,程序设计不会改变。相反,这将只是适应我们的需要。
如果您想将该函数用作翻译器,那么 EA 将进行分析,从而知道鼠标在哪里以及在做什么,并始终准确地报告情况。
结论
重要的是,你要了解事情是如何运作的。否则,在接下来的文章中,你将完全迷失方向,而我们将不再像以前那样行事。我们将以更复杂的形式开展一切工作。虽然上两篇文章所展示的内容看似复杂,但都是为那些没有太多编程知识的人准备的。我们在前两篇文章中介绍的所有内容都是为添加 Chart Trader 应用程序做一点准备,在接下来的文章中,我们将开始在系统中添加和开发 Chart Trader。这个应用程序将使我们能够直接在市场上进行交易,这非常有用,也非常重要,尤其是因为我们不能依赖现有的市场订单系统。因此,我们必须创建自己的解决方案,即 Chart Trader。
尽管如此,我们仍是在 MQL5 的基础层面上开展工作。即使你认为这些材料很难,但它们仍然是非常基础的。但感觉太难了,这很自然。出现这种情况可能是因为您没有深入研究 MQL5。当你走在任何地面上时,你会认为自己是在高山上,直到有人告诉你,你一直都是在海平面上行走。相信我,很快你就会认为,你在最新文章中看到的一切都是儿童游戏,任何青少年都可以做。所以,做好准备吧,困难即将来临。当这样的时刻到来时,我们再来讨论鼠标指针的问题。
本文由MetaQuotes Ltd译自葡萄牙语
原文地址: https://www.mql5.com/pt/articles/11607
注意: 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.



