
开发回放系统(第 56 部分):调整模块
概述
在上一篇文章"开发回放系统(第 55 部分):控制模块"中,我们实现了一些修改,使我们能够在不使用终端全局变量的情况下创建控制指标。至少在存储信息和用户指定的设置方面是这样。
虽然一切运行正常且相当稳定,但当系统被放置在具有一定数量柱形的图表上时,当使用与自定义交易品种相关的数据时,由于超出范围限制,系统会不断崩溃。
原因并不是自定义交易品种实际上坏了或造成了问题。这是因为数据库经常不足以保证整个系统的正常运行。主要的问题因素是缓冲区。
你现在可能会问:"问题怎么可能出在缓冲区上?当我们使用真实交易品种时,系统能正常工作,但当我们使用自定义交易品种时,系统就会崩溃,而崩溃的原因就是指标缓冲区!"
是的,原因就是缓冲区。但不是你想象的那种方式。这是因为当我们创建自定义交易品种时,缓冲区的大小可能不足以存储我们要放入其中的数据。
你可能会认为,只要分配更多内存,问题就会迎刃而解。但是,说到 MQL5,事情就没那么简单了。为指标缓冲区中的数据分配内存并不像你想象的那样有效。内存分配取决于图表上柱形的数量。因此,使用任何函数分配内存都是没有意义的,因为它实际上不会以你期望的方式被使用。
理解问题
真正的问题不在于控制指标,而在于鼠标指标。在修复这一异常的同时,我们将创建一个也会影响控制指标的解决方案。本文稍后将介绍这些修改。但首先,让我们了解一下发生故障的性质以及故障是如何发生的。
如果您使用前几篇文章中介绍的鼠标指标,并将其放置在自定义交易品种图表上,例如,该图表有 60 个一分钟柱形图,那么在等于或少于 10 分钟的时间框架内都不会有问题。但是,如果您尝试使用超过 10 分钟的时间框架,您将收到来自 MetaTrader 5 的消息:"鼠标指标:范围错误"。
为什么会发生这种情况?原因是上一篇文章中介绍的鼠标指标需要 6 个位置来存储指标缓冲区中的数据。因此,数学将会回答实际发生了什么的问题。如果每分钟图表有 60 个柱形,您可以将时间框架改为 10 分钟,这将在图表上产生 6 个柱形。这六个柱形将提供所需的六个缓冲位置,以容纳数据。但是,如果选择更高的时间框架,图表上的柱形数量将少于 6 个。
此时,鼠标指标将返回范围错误,因为它将尝试向 MetaTrader 5 尚未分配的内存位置写入数据。
这就是错误所在,有两种方法可以解决。第一种方法是在自定义交易品种上放置足够数量的柱形,以便在任何时间框架内至少有六个柱形。这不是最合适的解决方案,因为要访问每月时间框架,我们需要在自定义交易品种图表中加载至少 6 个月的 1 分钟柱形。而这只是为了防止出现范围错误。
我个人认为,而且我相信很多人也会同意,这远不是最好的解决方案,尤其是在回放/模拟器系统方面。如果系统纯粹以回放为导向,只要分析只在一个时间框架或更低的时间框架内进行,这个解决方案或许可行。但是,由于我们可以使用该系统来模拟市场走势,因此这种解决方案是完全不可接受的,我们需要更优雅的解决方案。
这就是我们现在要做的。我们将修改鼠标指标,以便将信息紧凑地放入缓冲区内的一个位置。因此,我们只需要在图表上显示一个柱形,指标就能实现其功能。
开始实现解决方案
我决定将信息紧凑地放置在一个位置,因为对于回放/模拟系统来说,在图表上添加和维护至少一个柱形比做其他任何事情都要容易得多。
不过,主要原因是我们可能希望在不使用大量柱形的情况下创建市场的模拟。我们将能仅使用我们想要的数量,而回放/模拟器服务本身将为系统提供必要的稳定性。这样,我们就可以在真实账户和模拟账户上使用相同的手段。
总结一下:我们将使鼠标指标缓冲区更小。它将只使用一个位置,但当我们请求指标读取缓冲区时,我们将获得相同数量的信息。有一点值得注意:如果直接从鼠标指标缓冲区读取数据,则只能获得该缓冲区中一个位置的数据。这些数据必须经过翻译才能真正发挥作用。通过 C_Mouse 类,我们可以使用某个函数来确保正确完成翻译。
说完这些,我们进入实现阶段。我们首先需要修改的是头文件,其代码如下。
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #define def_VERSION_DEBUG 05. //+------------------------------------------------------------------+ 06. #ifdef def_VERSION_DEBUG 07. #define macro_DEBUG_MODE(A) \ 08. Print(__FILE__, " ", __LINE__, " ", __FUNCTION__ + " " + #A + " = " + (string)(A)); 09. #else 10. #define macro_DEBUG_MODE(A) 11. #endif 12. //+------------------------------------------------------------------+ 13. #define def_SymbolReplay "RePlay" 14. #define def_MaxPosSlider 400 15. //+------------------------------------------------------------------+ 16. union uCast_Double 17. { 18. double dValue; 19. long _long; // 1 Information 20. datetime _datetime; // 1 Information 21. uint _32b[sizeof(double) / sizeof(uint)]; // 2 Informations 22. ushort _16b[sizeof(double) / sizeof(ushort)]; // 4 Informations 23. uchar _8b [sizeof(double) / sizeof(uchar)]; // 8 Informations 24. }; 25. //+------------------------------------------------------------------+ 26. enum EnumEvents { 27. evHideMouse, //Hide mouse price line 28. evShowMouse, //Show mouse price line 29. evHideBarTime, //Hide bar time 30. evShowBarTime, //Show bar time 31. evHideDailyVar, //Hide daily variation 32. evShowDailyVar, //Show daily variation 33. evHidePriceVar, //Hide instantaneous variation 34. evShowPriceVar, //Show instantaneous variation 35. evSetServerTime, //Replay/simulation system timer 36. evCtrlReplayInit //Initialize replay control 37. }; 38. //+------------------------------------------------------------------+
Defines.mqh 文件源代码
至少从总体上看,这里几乎没有明显的差异。不过,如果仔细观察,你会发现第 21 行和第 23 行有修改。这些修改是专门为更有效地使用数位而实施的。为了更容易识别我们将使用的信息类型,我使用了一种简单的标注:_32b 表示 32 位,_16b 表示 16 位,_8b 表示 8 位。这样,在任何类型的访问中,我们都能准确知道将使用多少数位。值得注意的是,一个双精度型数值代表 64 位,因此每个数据包的长度限制都在这 64 位内。
因此,_32b 可以包含 2 个值,_16b 可以包含 4 个值,_8b 可以包含 8 个值。不过,请注意,我们使用的是联合,这意味着我们可以将这些集合组合起来。要正确执行此操作,您必须了解 MQL5 中的每个数组都是基于 C/C++ 中使用的编号系统。换句话说,数组总是从零开始,之后每个位置都递增一个单位。
此时,如果对 C/C++ 的工作原理缺乏基本了解,许多人可能会开始感到困惑。这是因为这里的 "单位" 一词可能无法充分表达在递增索引值以将数据正确放置到数据包中时实际需要的内容。对这一概念的误解会让你完全迷失方向,或者至少无法理解信息究竟是如何被压缩的。
在深入了解对鼠标指标所做的修改之前,让我们先快速了解一下控制指标。原因很简单:在控制指标中,我们只需调整函数、过程和变量,使其与头文件 Defines.mqh 中引入的新类型保持一致。我们先来看看该类的代码,如下所示。
001. //+------------------------------------------------------------------+ 002. #property copyright "Daniel Jose" 003. //+------------------------------------------------------------------+ 004. #include "..\Auxiliar\C_DrawImage.mqh" 005. #include "..\Defines.mqh" 006. //+------------------------------------------------------------------+ 007. #define def_PathBMP "Images\\Market Replay\\Control\\" 008. #define def_ButtonPlay def_PathBMP + "Play.bmp" 009. #define def_ButtonPause def_PathBMP + "Pause.bmp" 010. #define def_ButtonLeft def_PathBMP + "Left.bmp" 011. #define def_ButtonLeftBlock def_PathBMP + "Left_Block.bmp" 012. #define def_ButtonRight def_PathBMP + "Right.bmp" 013. #define def_ButtonRightBlock def_PathBMP + "Right_Block.bmp" 014. #define def_ButtonPin def_PathBMP + "Pin.bmp" 015. #resource "\\" + def_ButtonPlay 016. #resource "\\" + def_ButtonPause 017. #resource "\\" + def_ButtonLeft 018. #resource "\\" + def_ButtonLeftBlock 019. #resource "\\" + def_ButtonRight 020. #resource "\\" + def_ButtonRightBlock 021. #resource "\\" + def_ButtonPin 022. //+------------------------------------------------------------------+ 023. #define def_PrefixCtrlName "MarketReplayCTRL_" 024. #define def_PosXObjects 120 025. //+------------------------------------------------------------------+ 026. #define def_SizeButtons 32 027. #define def_ColorFilter 0xFF00FF 028. //+------------------------------------------------------------------+ 029. #include "..\Auxiliar\C_Terminal.mqh" 030. #include "..\Auxiliar\C_Mouse.mqh" 031. //+------------------------------------------------------------------+ 032. class C_Controls : private C_Terminal 033. { 034. protected: 035. private : 036. //+------------------------------------------------------------------+ 037. enum eObjectControl {ePlay, eLeft, eRight, ePin, eNull}; 038. //+------------------------------------------------------------------+ 039. struct st_00 040. { 041. string szBarSlider, 042. szBarSliderBlock; 043. short Minimal; 044. }m_Slider; 045. struct st_01 046. { 047. C_DrawImage *Btn; 048. bool state; 049. short x, y, w, h; 050. }m_Section[eObjectControl::eNull]; 051. C_Mouse *m_MousePtr; 052. //+------------------------------------------------------------------+ 053. inline void CreteBarSlider(short x, short size) 054. { 055. ObjectCreate(GetInfoTerminal().ID, m_Slider.szBarSlider = def_PrefixCtrlName + "B1", OBJ_RECTANGLE_LABEL, 0, 0, 0); 056. ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_XDISTANCE, def_PosXObjects + x); 057. ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_YDISTANCE, m_Section[ePin].y + 11); 058. ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_XSIZE, size); 059. ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_YSIZE, 9); 060. ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_BGCOLOR, clrLightSkyBlue); 061. ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_BORDER_COLOR, clrBlack); 062. ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_WIDTH, 3); 063. ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_BORDER_TYPE, BORDER_FLAT); 064. ObjectCreate(GetInfoTerminal().ID, m_Slider.szBarSliderBlock = def_PrefixCtrlName + "B2", OBJ_RECTANGLE_LABEL, 0, 0, 0); 065. ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_XDISTANCE, def_PosXObjects + x); 066. ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_YDISTANCE, m_Section[ePin].y + 6); 067. ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_YSIZE, 19); 068. ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_BGCOLOR, clrRosyBrown); 069. ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_BORDER_TYPE, BORDER_RAISED); 070. } 071. //+------------------------------------------------------------------+ 072. void SetPlay(bool state) 073. { 074. if (m_Section[ePlay].Btn == NULL) 075. m_Section[ePlay].Btn = new C_DrawImage(GetInfoTerminal().ID, 0, def_PrefixCtrlName + EnumToString(ePlay), def_ColorFilter, "::" + def_ButtonPlay, "::" + def_ButtonPause); 076. m_Section[ePlay].Btn.Paint(m_Section[ePlay].x, m_Section[ePlay].y, m_Section[ePlay].w, m_Section[ePlay].h, 20, ((m_Section[ePlay].state = state) ? 0 : 1)); 077. } 078. //+------------------------------------------------------------------+ 079. void CreateCtrlSlider(void) 080. { 081. CreteBarSlider(77, 436); 082. m_Section[eLeft].Btn = new C_DrawImage(GetInfoTerminal().ID, 0, def_PrefixCtrlName + EnumToString(eLeft), def_ColorFilter, "::" + def_ButtonLeft, "::" + def_ButtonLeftBlock); 083. m_Section[eRight].Btn = new C_DrawImage(GetInfoTerminal().ID, 0, def_PrefixCtrlName + EnumToString(eRight), def_ColorFilter, "::" + def_ButtonRight, "::" + def_ButtonRightBlock); 084. m_Section[ePin].Btn = new C_DrawImage(GetInfoTerminal().ID, 0, def_PrefixCtrlName + EnumToString(ePin), def_ColorFilter, "::" + def_ButtonPin); 085. PositionPinSlider(m_Slider.Minimal); 086. } 087. //+------------------------------------------------------------------+ 088. inline void RemoveCtrlSlider(void) 089. { 090. ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, false); 091. for (eObjectControl c0 = ePlay + 1; c0 < eNull; c0++) 092. { 093. delete m_Section[c0].Btn; 094. m_Section[c0].Btn = NULL; 095. } 096. ObjectsDeleteAll(GetInfoTerminal().ID, def_PrefixCtrlName + "B"); 097. ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, true); 098. } 099. //+------------------------------------------------------------------+ 100. inline void PositionPinSlider(short p) 101. { 102. int iL, iR; 103. 104. m_Section[ePin].x = (p < m_Slider.Minimal ? m_Slider.Minimal : (p > def_MaxPosSlider ? def_MaxPosSlider : p)); 105. iL = (m_Section[ePin].x != m_Slider.Minimal ? 0 : 1); 106. iR = (m_Section[ePin].x < def_MaxPosSlider ? 0 : 1); 107. m_Section[ePin].x += def_PosXObjects; 108. m_Section[ePin].x += 95 - (def_SizeButtons / 2); 109. for (eObjectControl c0 = ePlay + 1; c0 < eNull; c0++) 110. m_Section[c0].Btn.Paint(m_Section[c0].x, m_Section[c0].y, m_Section[c0].w, m_Section[c0].h, 20, (c0 == eLeft ? iL : (c0 == eRight ? iR : 0))); 111. ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_XSIZE, m_Slider.Minimal + 2); 112. } 113. //+------------------------------------------------------------------+ 114. inline eObjectControl CheckPositionMouseClick(short &x, short &y) 115. { 116. C_Mouse::st_Mouse InfoMouse; 117. 118. InfoMouse = (*m_MousePtr).GetInfoMouse(); 119. x = (short) InfoMouse.Position.X_Graphics; 120. y = (short) InfoMouse.Position.Y_Graphics; 121. for (eObjectControl c0 = ePlay; c0 < eNull; c0++) 122. { 123. if ((m_Section[c0].Btn != NULL) && (m_Section[c0].x <= x) && (m_Section[c0].y <= y) && ((m_Section[c0].x + m_Section[c0].w) >= x) && ((m_Section[c0].y + m_Section[c0].h) >= y)) 124. return c0; 125. } 126. 127. return eNull; 128. } 129. //+------------------------------------------------------------------+ 130. public : 131. //+------------------------------------------------------------------+ 132. C_Controls(const long Arg0, const string szShortName, C_Mouse *MousePtr) 133. :C_Terminal(Arg0), 134. m_MousePtr(MousePtr) 135. { 136. if ((!IndicatorCheckPass(szShortName)) || (CheckPointer(m_MousePtr) == POINTER_INVALID)) SetUserError(C_Terminal::ERR_Unknown); 137. if (_LastError != ERR_SUCCESS) return; 138. ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, false); 139. ObjectsDeleteAll(GetInfoTerminal().ID, def_PrefixCtrlName); 140. ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, true); 141. for (eObjectControl c0 = ePlay; c0 < eNull; c0++) 142. { 143. m_Section[c0].h = m_Section[c0].w = def_SizeButtons; 144. m_Section[c0].y = 25; 145. m_Section[c0].Btn = NULL; 146. } 147. m_Section[ePlay].x = def_PosXObjects; 148. m_Section[eLeft].x = m_Section[ePlay].x + 47; 149. m_Section[eRight].x = m_Section[ePlay].x + 511; 150. m_Slider.Minimal = SHORT_MIN; 151. } 152. //+------------------------------------------------------------------+ 153. ~C_Controls() 154. { 155. for (eObjectControl c0 = ePlay; c0 < eNull; c0++) delete m_Section[c0].Btn; 156. ObjectsDeleteAll(GetInfoTerminal().ID, def_PrefixCtrlName); 157. delete m_MousePtr; 158. } 159. //+------------------------------------------------------------------+ 160. void SetBuffer(const int rates_total, double &Buff[]) 161. { 162. uCast_Double info; 163. 164. info._16b[0] = (ushort) m_Slider.Minimal; 165. info._16b[1] = (ushort) (m_Section[ePlay].state ? SHORT_MAX : SHORT_MIN); 166. if (rates_total > 0) 167. Buff[rates_total - 1] = info.dValue; 168. } 169. //+------------------------------------------------------------------+ 170. void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam) 171. { 172. short x, y; 173. static short iPinPosX = -1, six = -1, sps; 174. uCast_Double info; 175. 176. switch (id) 177. { 178. case (CHARTEVENT_CUSTOM + evCtrlReplayInit): 179. info.dValue = dparam; 180. iPinPosX = m_Slider.Minimal = (short) info._16b[0]; 181. if (info._16b[1] == 0) SetUserError(C_Terminal::ERR_Unknown); else 182. { 183. SetPlay((short)(info._16b[1]) == SHORT_MAX); 184. if ((short)(info._16b[1]) == SHORT_MIN) CreateCtrlSlider(); 185. } 186. break; 187. case CHARTEVENT_OBJECT_DELETE: 188. if (StringSubstr(sparam, 0, StringLen(def_PrefixCtrlName)) == def_PrefixCtrlName) 189. { 190. if (sparam == (def_PrefixCtrlName + EnumToString(ePlay))) 191. { 192. delete m_Section[ePlay].Btn; 193. m_Section[ePlay].Btn = NULL; 194. SetPlay(m_Section[ePlay].state); 195. }else 196. { 197. RemoveCtrlSlider(); 198. CreateCtrlSlider(); 199. } 200. } 201. break; 202. case CHARTEVENT_MOUSE_MOVE: 203. if ((*m_MousePtr).CheckClick(C_Mouse::eClickLeft)) switch (CheckPositionMouseClick(x, y)) 204. { 205. case ePlay: 206. SetPlay(!m_Section[ePlay].state); 207. if (m_Section[ePlay].state) 208. { 209. RemoveCtrlSlider(); 210. m_Slider.Minimal = iPinPosX; 211. }else CreateCtrlSlider(); 212. break; 213. case eLeft: 214. PositionPinSlider(iPinPosX = (iPinPosX > m_Slider.Minimal ? iPinPosX - 1 : m_Slider.Minimal)); 215. break; 216. case eRight: 217. PositionPinSlider(iPinPosX = (iPinPosX < def_MaxPosSlider ? iPinPosX + 1 : def_MaxPosSlider)); 218. break; 219. case ePin: 220. if (six == -1) 221. { 222. six = x; 223. sps = iPinPosX; 224. ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, false); 225. } 226. iPinPosX = sps + x - six; 227. PositionPinSlider(iPinPosX = (iPinPosX < m_Slider.Minimal ? m_Slider.Minimal : (iPinPosX > def_MaxPosSlider ? def_MaxPosSlider : iPinPosX))); 228. break; 229. }else if (six > 0) 230. { 231. six = -1; 232. ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, true); 233. } 234. break; 235. } 236. ChartRedraw(GetInfoTerminal().ID); 237. } 238. //+------------------------------------------------------------------+ 239. }; 240. //+------------------------------------------------------------------+ 241. #undef def_PosXObjects 242. #undef def_ButtonPlay 243. #undef def_ButtonPause 244. #undef def_ButtonLeft 245. #undef def_ButtonRight 246. #undef def_ButtonPin 247. #undef def_PrefixCtrlName 248. #undef def_PathBMP 249. //+------------------------------------------------------------------+
C_Control.mqh 的源代码
在检查代码时,您会注意到上面显示的代码与上一篇讨论此头文件的文章中提供的相同代码之间没有明显的差异,至少乍一看是这样。不过,由于对定义文件进行了修改,代码的某些部分稍作了修改。其中一个修改是明确使用了类型转换,如第 164 和 165 行所示,我们指示编译器明确使用特定的数据类型。
请注意第 165 行的修改,以前我们使用整数型常量,现在我们使用短整数型常量。虽然这些是有符号常量,即可以表示负值,但数组中的值是无符号的。这时,您可能会认为这可能会导致对数值的错误解释。如果是这样,那么你可能没有正确理解数值是如何以二进制形式表示的。我建议学习二进制值表示法,以了解如何在一个没有明确处理负值的系统中表示负值,同时还能无质量损失地传输信息。
在相同的代码中,你会在第 180 行看到另一个显式的类型转换。在这里,存储在无符号变量中的值被分配给有符号变量。这种类型转换可确保负值得到对应的表示。在整个自定义事件处理代码和控制指标的初始化过程中,这种类型的转换被大量使用。请花点时间仔细研究第 178 行和第 186 行之间的代码行,因为它展示了这种转换方法的密集使用。
值得一提的是,为了最大限度地有效利用 double 型中的数位,控制指标中进行的压缩并没有达到应有的深度。这是因为我们的情况不需要如此高级的优化。最后,关于控制指标代码,唯一的改动涉及版本号和一个链接。因此,我在此不再重复整个代码。您只需用本文提供的头文件替换上一篇文章中的头文件即可。指标代码的其他部分保持不变,可以正常使用。
现在,说到鼠标指标,情况就有点复杂了。因此,我们需要创建指标所需的全部三个文件。让我们在下一部分讨论这些文件。
实现鼠标指标解决方案
正如您在上一节中看到的,我们对控制指标代码进行了小幅调整,这些修改仅限于头文件。然而,鼠标指标的情况则截然不同,要复杂得多。
让我们来看看这些修改。首先,让我们来看看下面介绍的新 C_Mouse 类。
001. //+------------------------------------------------------------------+ 002. #property copyright "Daniel Jose" 003. //+------------------------------------------------------------------+ 004. #include "C_Terminal.mqh" 005. //+------------------------------------------------------------------+ 006. #define def_MousePrefixName "MouseBase_" 007. #define def_NameObjectLineH def_MousePrefixName + "H" 008. #define def_NameObjectLineV def_MousePrefixName + "TV" 009. #define def_NameObjectLineT def_MousePrefixName + "TT" 010. #define def_NameObjectStudy def_MousePrefixName + "TB" 011. //+------------------------------------------------------------------+ 012. class C_Mouse : public C_Terminal 013. { 014. public : 015. enum eStatusMarket {eCloseMarket, eAuction, eInTrading, eInReplay}; 016. enum eBtnMouse {eKeyNull = 0x00, eClickLeft = 0x01, eClickRight = 0x02, eSHIFT_Press = 0x04, eCTRL_Press = 0x08, eClickMiddle = 0x10}; 017. struct st_Mouse 018. { 019. struct st00 020. { 021. short X_Adjusted, 022. Y_Adjusted, 023. X_Graphics, 024. Y_Graphics; 025. double Price; 026. datetime dt; 027. }Position; 028. uchar ButtonStatus; 029. bool ExecStudy; 030. }; 031. //+------------------------------------------------------------------+ 032. protected: 033. //+------------------------------------------------------------------+ 034. void CreateObjToStudy(int x, int w, string szName, color backColor = clrNONE) const 035. { 036. if (m_Mem.szShortName != NULL) return; 037. CreateObjectGraphics(szName, OBJ_BUTTON, clrNONE); 038. ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_STATE, true); 039. ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_BORDER_COLOR, clrBlack); 040. ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_COLOR, clrBlack); 041. ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_BGCOLOR, backColor); 042. ObjectSetString(GetInfoTerminal().ID, szName, OBJPROP_FONT, "Lucida Console"); 043. ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_FONTSIZE, 10); 044. ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_CORNER, CORNER_LEFT_UPPER); 045. ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_XDISTANCE, x); 046. ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_YDISTANCE, TerminalInfoInteger(TERMINAL_SCREEN_HEIGHT) + 1); 047. ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_XSIZE, w); 048. ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_YSIZE, 18); 049. } 050. //+------------------------------------------------------------------+ 051. private : 052. enum eStudy {eStudyNull, eStudyCreate, eStudyExecute}; 053. struct st01 054. { 055. st_Mouse Data; 056. color corLineH, 057. corTrendP, 058. corTrendN; 059. eStudy Study; 060. }m_Info; 061. struct st_Mem 062. { 063. bool CrossHair, 064. IsFull; 065. datetime dt; 066. string szShortName; 067. }m_Mem; 068. bool m_OK; 069. //+------------------------------------------------------------------+ 070. void GetDimensionText(const string szArg, int &w, int &h) 071. { 072. TextSetFont("Lucida Console", -100, FW_NORMAL); 073. TextGetSize(szArg, w, h); 074. h += 5; 075. w += 5; 076. } 077. //+------------------------------------------------------------------+ 078. void CreateStudy(void) 079. { 080. if (m_Mem.IsFull) 081. { 082. CreateObjectGraphics(def_NameObjectLineV, OBJ_VLINE, m_Info.corLineH); 083. CreateObjectGraphics(def_NameObjectLineT, OBJ_TREND, m_Info.corLineH); 084. ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectLineT, OBJPROP_WIDTH, 2); 085. CreateObjToStudy(0, 0, def_NameObjectStudy); 086. } 087. m_Info.Study = eStudyCreate; 088. } 089. //+------------------------------------------------------------------+ 090. void ExecuteStudy(const double memPrice) 091. { 092. double v1 = GetInfoMouse().Position.Price - memPrice; 093. int w, h; 094. 095. if (!CheckClick(eClickLeft)) 096. { 097. m_Info.Study = eStudyNull; 098. ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, true); 099. if (m_Mem.IsFull) ObjectsDeleteAll(GetInfoTerminal().ID, def_MousePrefixName + "T"); 100. }else if (m_Mem.IsFull) 101. { 102. string sz1 = StringFormat(" %." + (string)GetInfoTerminal().nDigits + "f [ %d ] %02.02f%% ", 103. MathAbs(v1), Bars(GetInfoTerminal().szSymbol, PERIOD_CURRENT, m_Mem.dt, GetInfoMouse().Position.dt) - 1, MathAbs((v1 / memPrice) * 100.0)); 104. GetDimensionText(sz1, w, h); 105. ObjectSetString(GetInfoTerminal().ID, def_NameObjectStudy, OBJPROP_TEXT, sz1); 106. ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectStudy, OBJPROP_BGCOLOR, (v1 < 0 ? m_Info.corTrendN : m_Info.corTrendP)); 107. ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectStudy, OBJPROP_XSIZE, w); 108. ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectStudy, OBJPROP_YSIZE, h); 109. ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectStudy, OBJPROP_XDISTANCE, GetInfoMouse().Position.X_Adjusted - w); 110. ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectStudy, OBJPROP_YDISTANCE, GetInfoMouse().Position.Y_Adjusted - (v1 < 0 ? 1 : h)); 111. ObjectMove(GetInfoTerminal().ID, def_NameObjectLineT, 1, GetInfoMouse().Position.dt, GetInfoMouse().Position.Price); 112. ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectLineT, OBJPROP_COLOR, (memPrice > GetInfoMouse().Position.Price ? m_Info.corTrendN : m_Info.corTrendP)); 113. } 114. m_Info.Data.ButtonStatus = eKeyNull; 115. } 116. //+------------------------------------------------------------------+ 117. inline void DecodeAlls(int xi, int yi) 118. { 119. int w = 0; 120. 121. ChartXYToTimePrice(GetInfoTerminal().ID, m_Info.Data.Position.X_Graphics = (short) xi, m_Info.Data.Position.Y_Graphics = (short)yi, w, m_Info.Data.Position.dt, m_Info.Data.Position.Price); 122. m_Info.Data.Position.dt = AdjustTime(m_Info.Data.Position.dt); 123. m_Info.Data.Position.Price = AdjustPrice(m_Info.Data.Position.Price); 124. ChartTimePriceToXY(GetInfoTerminal().ID, w, m_Info.Data.Position.dt, m_Info.Data.Position.Price, xi, yi); 125. m_Info.Data.Position.X_Adjusted = (short) xi; 126. m_Info.Data.Position.Y_Adjusted = (short) yi; 127. } 128. //+------------------------------------------------------------------+ 129. public : 130. //+------------------------------------------------------------------+ 131. C_Mouse(const long id, const string szShortName) 132. :C_Terminal(id), 133. m_OK(false) 134. { 135. m_Mem.szShortName = szShortName; 136. } 137. //+------------------------------------------------------------------+ 138. C_Mouse(const long id, const string szShortName, color corH, color corP, color corN) 139. :C_Terminal(id) 140. { 141. if (!(m_OK = IndicatorCheckPass(szShortName))) SetUserError(C_Terminal::ERR_Unknown); 142. if (_LastError != ERR_SUCCESS) return; 143. m_Mem.szShortName = NULL; 144. m_Mem.CrossHair = (bool)ChartGetInteger(GetInfoTerminal().ID, CHART_CROSSHAIR_TOOL); 145. ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_MOUSE_MOVE, true); 146. ChartSetInteger(GetInfoTerminal().ID, CHART_CROSSHAIR_TOOL, false); 147. ZeroMemory(m_Info); 148. m_Info.corLineH = corH; 149. m_Info.corTrendP = corP; 150. m_Info.corTrendN = corN; 151. m_Info.Study = eStudyNull; 152. if (m_Mem.IsFull = (corP != clrNONE) && (corH != clrNONE) && (corN != clrNONE)) 153. CreateObjectGraphics(def_NameObjectLineH, OBJ_HLINE, m_Info.corLineH); 154. } 155. //+------------------------------------------------------------------+ 156. ~C_Mouse() 157. { 158. if (!m_OK) return; 159. ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, false); 160. ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_MOUSE_MOVE, false); 161. ChartSetInteger(GetInfoTerminal().ID, CHART_CROSSHAIR_TOOL, m_Mem.CrossHair); 162. ObjectsDeleteAll(GetInfoTerminal().ID, def_MousePrefixName); 163. } 164. //+------------------------------------------------------------------+ 165. inline bool CheckClick(const eBtnMouse value) 166. { 167. return (GetInfoMouse().ButtonStatus & value) == value; 168. } 169. //+------------------------------------------------------------------+ 170. inline const st_Mouse GetInfoMouse(void) 171. { 172. if (m_Mem.szShortName != NULL) 173. { 174. double Buff[]; 175. uCast_Double loc; 176. int handle = ChartIndicatorGet(GetInfoTerminal().ID, 0, m_Mem.szShortName); 177. 178. ZeroMemory(m_Info.Data); 179. if (CopyBuffer(handle, 0, 0, 1, Buff) == 1) 180. { 181. loc.dValue = Buff[0]; 182. m_Info.Data.ButtonStatus = loc._8b[0]; 183. DecodeAlls((int)loc._16b[1], (int)loc._16b[2]); 184. } 185. IndicatorRelease(handle); 186. } 187. 188. return m_Info.Data; 189. } 190. //+------------------------------------------------------------------+ 191. inline void SetBuffer(const int rates_total, double &Buff[]) 192. { 193. uCast_Double info; 194. 195. info._8b[0] = (uchar)(m_Info.Study == C_Mouse::eStudyNull ? m_Info.Data.ButtonStatus : 0); 196. info._16b[1] = (ushort) m_Info.Data.Position.X_Graphics; 197. info._16b[2] = (ushort) m_Info.Data.Position.Y_Graphics; 198. Buff[rates_total - 1] = info.dValue; 199. } 200. //+------------------------------------------------------------------+ 201. void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam) 202. { 203. int w = 0; 204. static double memPrice = 0; 205. 206. if (m_Mem.szShortName == NULL) 207. { 208. C_Terminal::DispatchMessage(id, lparam, dparam, sparam); 209. switch (id) 210. { 211. case (CHARTEVENT_CUSTOM + evHideMouse): 212. if (m_Mem.IsFull) ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectLineH, OBJPROP_COLOR, clrNONE); 213. break; 214. case (CHARTEVENT_CUSTOM + evShowMouse): 215. if (m_Mem.IsFull) ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectLineH, OBJPROP_COLOR, m_Info.corLineH); 216. break; 217. case CHARTEVENT_MOUSE_MOVE: 218. DecodeAlls((int)lparam, (int)dparam); 219. if (m_Mem.IsFull) ObjectMove(GetInfoTerminal().ID, def_NameObjectLineH, 0, 0, m_Info.Data.Position.Price = AdjustPrice(m_Info.Data.Position.Price)); 220. if ((m_Info.Study != eStudyNull) && (m_Mem.IsFull)) ObjectMove(GetInfoTerminal().ID, def_NameObjectLineV, 0, m_Info.Data.Position.dt, 0); 221. m_Info.Data.ButtonStatus = (uchar) sparam; //Mudança no tipo ... 222. if (CheckClick(eClickMiddle)) 223. if ((!m_Mem.IsFull) || ((color)ObjectGetInteger(GetInfoTerminal().ID, def_NameObjectLineH, OBJPROP_COLOR) != clrNONE)) CreateStudy(); 224. if (CheckClick(eClickLeft) && (m_Info.Study == eStudyCreate)) 225. { 226. ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, false); 227. if (m_Mem.IsFull) ObjectMove(GetInfoTerminal().ID, def_NameObjectLineT, 0, m_Mem.dt = GetInfoMouse().Position.dt, memPrice = GetInfoMouse().Position.Price); 228. m_Info.Study = eStudyExecute; 229. } 230. if (m_Info.Study == eStudyExecute) ExecuteStudy(memPrice); 231. m_Info.Data.ExecStudy = m_Info.Study == eStudyExecute; 232. break; 233. case CHARTEVENT_OBJECT_DELETE: 234. if ((m_Mem.IsFull) && (sparam == def_NameObjectLineH)) CreateObjectGraphics(def_NameObjectLineH, OBJ_HLINE, m_Info.corLineH); 235. break; 236. } 237. } 238. } 239. //+------------------------------------------------------------------+ 240. }; 241. //+------------------------------------------------------------------+ 242. #undef def_NameObjectLineV 243. #undef def_NameObjectLineH 244. #undef def_NameObjectLineT 245. #undef def_NameObjectStudy 246. //+------------------------------------------------------------------+
C_Mouse.mqh 文件的源代码
您可能不会立即注意到所做的修改,这主要是因为大多数修改都与变量类型有关。尽管如此,这些问题还是值得一提的,正确理解这些问题将有助于您识别该鼠标指标的局限性。是的,它有局限性,但一旦你了解了这些局限性,你就能恰当地使用它,确保这些限制不会影响你的使用。
首先,你会注意到在第 21 行至第 24 行,我们现在使用的是 SHORT 类型,而之前使用的是 INT 类型。同样,第 28 行修改了变量类型。
在你恐慌地认为这是由这些类型变化引起的混乱之前,让我提醒你 SHORT 和 INT 类型的一些特征和特性。
在 MQL5 中,INT 类型是一种 32 位数据类型,这意味着其值范围为 -2,147,483,648 至 2,147,483,647(带符号时)。如果是无符号,即只有正数,数值范围为 0 至 4,294,967,295 之间。相反,SHORT 类型是一种 16 位数据类型,有符号时范围为 -32,768 至 32,767,无符号时范围为 0 至 65,535。
现在,请考虑一下:您的显示器像素分辨率是多少?我为什么要问这个?因为在显示器上使用 INT 表示笛卡尔维度(X 和 Y)的效率非常低。别误会我的意思,但你实际上浪费了很多空间。例如,8K 显示器是一种高分辨率显示器,具有 7680 个水平像素和 4320 个垂直像素。这种分辨率在水平和垂直方向上都小于 2 的 13次方,与带符号 SHORT 类型的 2 的 16次方相比还很宽松。因此,仍有 3 个未使用的位可用于其他目的。
这种简单的优化就足够了。使用 INT 只允许存储两个值,而使用 SHORT 就可以在同一个 64 位的双精度型数中存储四个值。由于我们只需要两个 SHORT 值来表示鼠标位置(X 和 Y),因此我们还有两个 SHORT 空闲用于其他用途。此外,即使是 8K 显示器,每个 SHORT 中仍有 3 个未使用的位用于存储鼠标坐标。
如您所见,限制与位置数据无关。实际限制在于其他方面。如果您一直关注本系列文章,就会注意到我们的目标是让鼠标指标提供信息,以便进行更简单、更快速的分析。限制就在这里:价格和时间值不能直接存储在鼠标指标的缓冲区中。这些值各需要 64 位,而我们无法为它们分配两个位置。为了解决这个问题,我们需要一种不同的方法,从而要修改 C_Mouse 类。
第一个明显的修改出现在第 117 行。请注意:为了避免对鼠标指标中的所有类型进行大量修改,我决定将位置作为 INT 保留在类中。不过,在类之外,这些数值会根据需要进行调整。
第 117 行的程序将操作系统提供的屏幕坐标转换为 MQL5 调整的坐标,使其与图表上显示的坐标一致。请特别注意这一点。该过程是类的私有过程,这意味着类外的代码无法访问它。不过,它能确保数据转换,保留功能。如果您一直使用该指标通过类翻译数据,就不会遇到任何问题。
要理解这一点,请看第 170 行。该函数用于翻译指标数据。请注意,虽然内部代码已经修改,但其接口保持不变。以前返回六个值,现在只返回一个。请密切关注我们的处理方式。在第 179 行,我们从指标缓冲区获取一个值。如果返回了一个值,第 181 行将其分配给翻译系统。在第 182 行,我们捕捉并转换数值,以确定按钮状态。注意使用的索引和位的长度。然后,在第 183 行,数据被发送到一个程序,该程序将屏幕坐标 X 和 Y 转换为 MetaTrader 5 或其他使用鼠标指标作为辅助工具的应用程序所期望的类型。尽管发生了变化,但这些修改仍局限于函数,类之外的一切都不受影响。
现在我们来看看一个更复杂但更重要的方面:第 191 行引入了一个将数据写入指标缓冲区的过程。以前,该程序是指标代码的一部分。不过,出于实际考虑,我把它移到了类中,主要是为了限制对类数据的访问。
让我们逐步研究一下。在第 193 行,我们声明了 compression 变量。第 195 行开始数据压缩过程。请密切注意,了解在访问数组时如何使用索引。
_8b 数组包含 8 个位置,而 _16b 数组包含 4 个位置。两者都从索引 0 开始。不过,请注意 _16b 数组的索引 0 与 _8b 数组的索引 0 和索引 1 相对应。例如,在第 195 行中,当使用 _8b 中的零索引时,我们占用了 _16b 中的零索引。由于所需信息只需要 _8b 的索引 0,因此 _8b 的索引 1 仍然空缺。不过,_16b 的索引 0 不能重复使用,因为它需要两个_8b 的索引来构建。
因此,_8b 的索引一保持空闲,而鼠标位置值则从_8b 的索引二开始。由于 _8b 的索引二与 _16b 索引一相对应,因此第 196 行引用了该索引,在逻辑和功能上将数据分开。第 197 行遵循同样的索引原则。尽管外表如此,但并非所有位都被占用。_16b 中索引 3 的位, _8b 中索引 1 的位,以及 X 和 Y 位置两个 SHORT 值中的 6 个位仍未使用。如果需要,这些未使用的位可用于其他数据。
请看下面的图片,以便更清楚地了解情况。
图片逐个字节展示了缓冲区的内容,每个字节代表 8 位。蓝色区域表示被占用的位,白色区域为未来数据的空闲位。X 代表图形的 X 坐标,Y 代表 Y 坐标。希望通过观察这张图片,你能更好地理解我究竟在做什么。
至于鼠标指标代码的其他部分,由于相对简单,无需详细解释。不过,由于代码经过了修改,现将更新后的版本提供如下。它将确保一切按预期运行。
001. //+------------------------------------------------------------------+ 002. #property copyright "Daniel Jose" 003. //+------------------------------------------------------------------+ 004. #include "..\C_Mouse.mqh" 005. //+------------------------------------------------------------------+ 006. #define def_ExpansionPrefix def_MousePrefixName + "Expansion_" 007. #define def_ExpansionBtn1 def_ExpansionPrefix + "B1" 008. #define def_ExpansionBtn2 def_ExpansionPrefix + "B2" 009. #define def_ExpansionBtn3 def_ExpansionPrefix + "B3" 010. //+------------------------------------------------------------------+ 011. class C_Study : public C_Mouse 012. { 013. private : 014. //+------------------------------------------------------------------+ 015. struct st00 016. { 017. eStatusMarket Status; 018. MqlRates Rate; 019. string szInfo; 020. color corP, 021. corN; 022. int HeightText; 023. bool bvT, bvD, bvP; 024. datetime TimeDevice; 025. }m_Info; 026. //+------------------------------------------------------------------+ 027. const datetime GetBarTime(void) 028. { 029. datetime dt; 030. int i0 = PeriodSeconds(); 031. 032. if (m_Info.Status == eInReplay) 033. { 034. if ((dt = m_Info.TimeDevice) == ULONG_MAX) return ULONG_MAX; 035. }else dt = TimeCurrent(); 036. if (m_Info.Rate.time <= dt) 037. m_Info.Rate.time = (datetime)(((ulong) dt / i0) * i0) + i0; 038. 039. return m_Info.Rate.time - dt; 040. } 041. //+------------------------------------------------------------------+ 042. void Draw(void) 043. { 044. double v1; 045. 046. if (m_Info.bvT) 047. { 048. ObjectSetInteger(GetInfoTerminal().ID, def_ExpansionBtn1, OBJPROP_YDISTANCE, GetInfoMouse().Position.Y_Adjusted - 18); 049. ObjectSetString(GetInfoTerminal().ID, def_ExpansionBtn1, OBJPROP_TEXT, m_Info.szInfo); 050. } 051. if (m_Info.bvD) 052. { 053. v1 = NormalizeDouble((((GetInfoMouse().Position.Price - m_Info.Rate.close) / m_Info.Rate.close) * 100.0), 2); 054. ObjectSetInteger(GetInfoTerminal().ID, def_ExpansionBtn2, OBJPROP_YDISTANCE, GetInfoMouse().Position.Y_Adjusted - 1); 055. ObjectSetInteger(GetInfoTerminal().ID, def_ExpansionBtn2, OBJPROP_BGCOLOR, (v1 < 0 ? m_Info.corN : m_Info.corP)); 056. ObjectSetString(GetInfoTerminal().ID, def_ExpansionBtn2, OBJPROP_TEXT, StringFormat("%.2f%%", MathAbs(v1))); 057. } 058. if (m_Info.bvP) 059. { 060. v1 = NormalizeDouble((((iClose(GetInfoTerminal().szSymbol, PERIOD_D1, 0) - m_Info.Rate.close) / m_Info.Rate.close) * 100.0), 2); 061. ObjectSetInteger(GetInfoTerminal().ID, def_ExpansionBtn3, OBJPROP_YDISTANCE, GetInfoMouse().Position.Y_Adjusted - 1); 062. ObjectSetInteger(GetInfoTerminal().ID, def_ExpansionBtn3, OBJPROP_BGCOLOR, (v1 < 0 ? m_Info.corN : m_Info.corP)); 063. ObjectSetString(GetInfoTerminal().ID, def_ExpansionBtn3, OBJPROP_TEXT, StringFormat("%.2f%%", MathAbs(v1))); 064. } 065. } 066. //+------------------------------------------------------------------+ 067. inline void CreateObjInfo(EnumEvents arg) 068. { 069. switch (arg) 070. { 071. case evShowBarTime: 072. C_Mouse::CreateObjToStudy(2, 110, def_ExpansionBtn1, clrPaleTurquoise); 073. m_Info.bvT = true; 074. break; 075. case evShowDailyVar: 076. C_Mouse::CreateObjToStudy(2, 53, def_ExpansionBtn2); 077. m_Info.bvD = true; 078. break; 079. case evShowPriceVar: 080. C_Mouse::CreateObjToStudy(58, 53, def_ExpansionBtn3); 081. m_Info.bvP = true; 082. break; 083. } 084. } 085. //+------------------------------------------------------------------+ 086. inline void RemoveObjInfo(EnumEvents arg) 087. { 088. string sz; 089. 090. switch (arg) 091. { 092. case evHideBarTime: 093. sz = def_ExpansionBtn1; 094. m_Info.bvT = false; 095. break; 096. case evHideDailyVar: 097. sz = def_ExpansionBtn2; 098. m_Info.bvD = false; 099. break; 100. case evHidePriceVar: 101. sz = def_ExpansionBtn3; 102. m_Info.bvP = false; 103. break; 104. } 105. ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, false); 106. ObjectDelete(GetInfoTerminal().ID, sz); 107. ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, true); 108. } 109. //+------------------------------------------------------------------+ 110. public : 111. //+------------------------------------------------------------------+ 112. C_Study(long IdParam, string szShortName, color corH, color corP, color corN) 113. :C_Mouse(IdParam, szShortName, corH, corP, corN) 114. { 115. if (_LastError != ERR_SUCCESS) return; 116. ZeroMemory(m_Info); 117. m_Info.Status = eCloseMarket; 118. m_Info.Rate.close = iClose(GetInfoTerminal().szSymbol, PERIOD_D1, ((GetInfoTerminal().szSymbol == def_SymbolReplay) || (macroGetDate(TimeCurrent()) != macroGetDate(iTime(GetInfoTerminal().szSymbol, PERIOD_D1, 0))) ? 0 : 1)); 119. m_Info.corP = corP; 120. m_Info.corN = corN; 121. CreateObjInfo(evShowBarTime); 122. CreateObjInfo(evShowDailyVar); 123. CreateObjInfo(evShowPriceVar); 124. } 125. //+------------------------------------------------------------------+ 126. void Update(const eStatusMarket arg) 127. { 128. datetime dt; 129. 130. switch (m_Info.Status = (m_Info.Status != arg ? arg : m_Info.Status)) 131. { 132. case eCloseMarket : 133. m_Info.szInfo = "Closed Market"; 134. break; 135. case eInReplay : 136. case eInTrading : 137. if ((dt = GetBarTime()) < ULONG_MAX) 138. { 139. m_Info.szInfo = TimeToString(dt, TIME_SECONDS); 140. break; 141. } 142. case eAuction : 143. m_Info.szInfo = "Auction"; 144. break; 145. default : 146. m_Info.szInfo = "ERROR"; 147. } 148. Draw(); 149. } 150. //+------------------------------------------------------------------+ 151. virtual void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam) 152. { 153. C_Mouse::DispatchMessage(id, lparam, dparam, sparam); 154. switch (id) 155. { 156. case CHARTEVENT_CUSTOM + evHideBarTime: 157. RemoveObjInfo(evHideBarTime); 158. break; 159. case CHARTEVENT_CUSTOM + evShowBarTime: 160. CreateObjInfo(evShowBarTime); 161. break; 162. case CHARTEVENT_CUSTOM + evHideDailyVar: 163. RemoveObjInfo(evHideDailyVar); 164. break; 165. case CHARTEVENT_CUSTOM + evShowDailyVar: 166. CreateObjInfo(evShowDailyVar); 167. break; 168. case CHARTEVENT_CUSTOM + evHidePriceVar: 169. RemoveObjInfo(evHidePriceVar); 170. break; 171. case CHARTEVENT_CUSTOM + evShowPriceVar: 172. CreateObjInfo(evShowPriceVar); 173. break; 174. case (CHARTEVENT_CUSTOM + evSetServerTime): 175. m_Info.TimeDevice = (datetime)dparam; 176. break; 177. case CHARTEVENT_MOUSE_MOVE: 178. Draw(); 179. break; 180. } 181. ChartRedraw(GetInfoTerminal().ID); 182. } 183. //+------------------------------------------------------------------+ 184. }; 185. //+------------------------------------------------------------------+ 186. #undef def_ExpansionBtn3 187. #undef def_ExpansionBtn2 188. #undef def_ExpansionBtn1 189. #undef def_ExpansionPrefix 190. #undef def_MousePrefixName 191. //+------------------------------------------------------------------+
C_Study.mqh 文件源代码
C_Study 类几乎保持不变,因此,我无需再作解释。现在让我们看看下图所示的指标代码。
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. #property description "This is an indicator for graphical studies using the mouse." 04. #property description "This is an integral part of the Replay / Simulator system." 05. #property description "However it can be used in the real market." 06. #property version "1.56" 07. #property icon "/Images/Market Replay/Icons/Indicators.ico" 08. #property link "https://www.mql5.com/pt/articles/12000" 09. #property indicator_chart_window 10. #property indicator_plots 0 11. #property indicator_buffers 1 12. //+------------------------------------------------------------------+ 13. #include <Market Replay\Auxiliar\Study\C_Study.mqh> 14. //+------------------------------------------------------------------+ 15. C_Study *Study = NULL; 16. //+------------------------------------------------------------------+ 17. input long user00 = 0; //ID 18. input C_Study::eStatusMarket user01 = C_Study::eAuction; //Market Status 19. input color user02 = clrBlack; //Price Line 20. input color user03 = clrPaleGreen; //Positive Study 21. input color user04 = clrLightCoral; //Negative Study 22. //+------------------------------------------------------------------+ 23. C_Study::eStatusMarket m_Status; 24. int m_posBuff = 0; 25. double m_Buff[]; 26. //+------------------------------------------------------------------+ 27. int OnInit() 28. { 29. ResetLastError(); 30. Study = new C_Study(user00, "Indicator Mouse Study", user02, user03, user04); 31. if (_LastError != ERR_SUCCESS) return INIT_FAILED; 32. if ((*Study).GetInfoTerminal().szSymbol != def_SymbolReplay) 33. { 34. MarketBookAdd((*Study).GetInfoTerminal().szSymbol); 35. OnBookEvent((*Study).GetInfoTerminal().szSymbol); 36. m_Status = C_Study::eCloseMarket; 37. }else 38. m_Status = user01; 39. SetIndexBuffer(0, m_Buff, INDICATOR_DATA); 40. ArrayInitialize(m_Buff, EMPTY_VALUE); 41. 42. return INIT_SUCCEEDED; 43. } 44. //+------------------------------------------------------------------+ 45. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 46. { 47. m_posBuff = rates_total; 48. (*Study).Update(m_Status); 49. 50. return rates_total; 51. } 52. //+------------------------------------------------------------------+ 53. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 54. { 55. (*Study).DispatchMessage(id, lparam, dparam, sparam); 56. (*Study).SetBuffer(m_posBuff, m_Buff); 57. 58. ChartRedraw((*Study).GetInfoTerminal().ID); 59. } 60. //+------------------------------------------------------------------+ 61. void OnBookEvent(const string &symbol) 62. { 63. MqlBookInfo book[]; 64. C_Study::eStatusMarket loc = m_Status; 65. 66. if (symbol != (*Study).GetInfoTerminal().szSymbol) return; 67. MarketBookGet((*Study).GetInfoTerminal().szSymbol, book); 68. m_Status = (ArraySize(book) == 0 ? C_Study::eCloseMarket : C_Study::eInTrading); 69. for (int c0 = 0; (c0 < ArraySize(book)) && (m_Status != C_Study::eAuction); c0++) 70. if ((book[c0].type == BOOK_TYPE_BUY_MARKET) || (book[c0].type == BOOK_TYPE_SELL_MARKET)) m_Status = C_Study::eAuction; 71. if (loc != m_Status) (*Study).Update(m_Status); 72. } 73. //+------------------------------------------------------------------+ 74. void OnDeinit(const int reason) 75. { 76. if (reason != REASON_INITFAILED) 77. { 78. if ((*Study).GetInfoTerminal().szSymbol != def_SymbolReplay) 79. MarketBookRelease((*Study).GetInfoTerminal().szSymbol); 80. } 81. delete Study; 82. } 83. //+------------------------------------------------------------------+
鼠标指标源代码
请注意,这里的代码与之前的代码有些不同,但它们并没有重要到让你无法理解代码的程度。不过,我认为有两点值得简单谈谈。它们分别位于第 47 行和第 56 行。
在第 47 行,您可以看到,与之前不同的是,我们现在只保存 MetaTrader 5 提供的值,而不对其进行任何更改,因为这将在其他地方完成。
然后,在第 56 行中,我们将信息传递给 C_Mouse 类,在该类中,我们将纠正情况并将其写入指标缓冲区。请注意,正是因为我们把所有的复杂性都转移到了类中,所以这里的事情变得简单多了。
结论
经过对代码的修改和调整,我们现在已经能够在我们正在开发的回放/模拟系统中实际使用鼠标指标。现在,我们可以在相关应用程序之间使用消息交换系统,同时使用缓冲区读取功能在它们之间传递信息。唯一的限制是,我们必须始终使用缓冲区中的一个位置。这样做是必要的,以免事件系统超负荷。但这将是今后讨论的下一个话题。
在下面的视频中,您可以看到该系统的运行演示。附件提供了编译后的代码,您可以运行测试并了解实际情况。在我忘记提及之前,您可以用上一篇文章中的鼠标指标替换鼠标指标,并运行本文中的服务来测试范围错误。您将看到上述错误出现。
最后一点:虽然本文没有提到将要使用的服务代码,但我会把它的解释留给下一个,因为我们将使用相同的代码作为我们实际开发的跳板。所以请耐心等待下一篇文章,因为每一天都变得越来越有趣。
演示视频
本文由MetaQuotes Ltd译自葡萄牙语
原文地址: https://www.mql5.com/pt/articles/12000

