
开发回放系统(第 51 部分):事情变得复杂(三)
概述
在上一篇文章开发回放系统(第 50 部分):事情变得复杂(二)中,我们开始进一步修改控制指标,以确保它保持在图表限制范围内。不在任何图表内,而是由回放/模拟器服务打开的图表。那里实现的主要功能是使用自定义模板的能力,而不是系统所需的模板。
允许这种事情使得使用整个回放/模拟器系统变得更加愉快,并且适合那些真正想要使用该系统进行一些研究的人。您可以创建一个模板,在回放/模拟器系统中使用它,然后在真实帐户上使用相同的模板。因此,我们现在正在修改系统。
但控制指标与用户的交互不太好,这是因为系统中存在多个重复的地方。但主要问题是该系统并不像看上去那么安全和稳定。发生这种情况的原因是,开发阶段(即第一阶段)没有考虑到某些用户可能会尝试以非预期的方式使用系统这一情况。但随着当前方向和定位的改变,安全稳定水平将开始提高。
虽然我们目前仍在处理用户体验方面不稳定的系统,但很快就会得到解决。一旦完成,整个系统都将从中受益。
如果您阅读了上一篇文章(我建议您阅读),您就会知道我们已经开始直接通过服务使用控制指标。此后,该指标不再可自由放置在任何图表上。
冷静分析代码后,我发现我们可以做一些修改,使其变得更好。然而,回放/模拟器系统所使用的其他模块也需要进行一些修改。因此,在本文中我们将重点解释将实现的修改。这是因为其中一些模块将可供用户使用。因此,了解什么是可以操纵的,什么是不能操纵的,这一点很重要,因为你是这个系统的用户,也是监控其开发的程序员。这是避免任何模块使用不稳定所必需的。请记住,某些模块不仅可以在回放/模拟器系统中使用,还可以在真实和模拟帐户中使用。
您可能认为这里看到的修改并不大,但在进行进一步的修改之前,我们需要确保代码稳定。因此,这篇文章可能看起来有点模糊不清。但我再重复一遍:你必须了解系统是如何开发的,如果您不了解这一点,您将无法正确使用该系统。
不用多说了,让我们开始修改吧。
扩展模块的使用
我们需要做的第一个修改是控制指标的源代码。上一篇文章之前讨论的代码没有使用一些已经开发和创建的模块。这导致控制指标和系统其他模块上发生的事情出现一些不一致。
其中一个模块是鼠标指标。开发此模块是为了集合与鼠标相关的所有内容。这样,任何其他新代码就不需要任何特别的测试和分析。这一切都将由鼠标指标模块完成。
这个指标是在不久前关于回放/模拟器系统的系列文章中开发的。但是,它最初是设计用于真实账户或模拟账户的,不太适合以当前形式工作,但我们稍后会更详细地讨论这一点。
在下面的代码中,您可以看到如何将鼠标指标模块与控制指标集成,该模块已经进行了一些修复和更改以允许这种集成。
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. #property icon "/Images/Market Replay/Icons/Replay - Device.ico" 04. #property description "Control indicator for the Replay-Simulator service." 05. #property description "This one doesn't work without the service loaded." 06. #property version "1.51" 07. #property link "https://www.mql5.com/en/articles/11877" 08. #property indicator_chart_window 09. #property indicator_plots 0 10. //+------------------------------------------------------------------+ 11. #include <Market Replay\Service Graphics\C_Controls.mqh> 12. //+------------------------------------------------------------------+ 13. C_Controls *control = NULL; 14. //+------------------------------------------------------------------+ 15. input long user00 = 0; //ID 16. //+------------------------------------------------------------------+ 17. int OnInit() 18. { 19. u_Interprocess Info; 20. 21. ResetLastError(); 22. if (CheckPointer(control = new C_Controls(user00, "Market Replay Control", "Indicator Mouse Study")) == POINTER_INVALID) 23. SetUserError(C_Terminal::ERR_PointerInvalid); 24. if (_LastError != ERR_SUCCESS) 25. { 26. Print("Control indicator failed on initialization."); 27. return INIT_FAILED; 28. } 29. if (GlobalVariableCheck(def_GlobalVariableReplay)) Info.df_Value = GlobalVariableGet(def_GlobalVariableReplay); else Info.df_Value = 0; 30. EventChartCustom(user00, C_Controls::ev_WaitOff, 1, Info.df_Value, ""); 31. (*control).Init(Info.s_Infos.isPlay); 32. 33. return INIT_SUCCEEDED; 34. } 35. //+------------------------------------------------------------------+ 36. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 37. { 38. static bool bWait = false; 39. u_Interprocess Info; 40. 41. Info.df_Value = GlobalVariableGet(def_GlobalVariableReplay); 42. if (!bWait) 43. { 44. if (Info.s_Infos.isWait) 45. { 46. EventChartCustom(user00, C_Controls::ev_WaitOn, 1, 0, ""); 47. bWait = true; 48. } 49. }else if (!Info.s_Infos.isWait) 50. { 51. EventChartCustom(user00, C_Controls::ev_WaitOff, 1, Info.df_Value, ""); 52. bWait = false; 53. } 54. 55. return rates_total; 56. } 57. //+------------------------------------------------------------------+ 58. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 59. { 60. (*control).DispatchMessage(id, lparam, dparam, sparam); 61. } 62. //+------------------------------------------------------------------+ 63. void OnDeinit(const int reason) 64. { 65. switch (reason) 66. { 67. case REASON_TEMPLATE: 68. Print("Modified template. Replay/simulation system shutting down."); 69. case REASON_PARAMETERS: 70. case REASON_REMOVE: 71. case REASON_CHARTCLOSE: 72. if (ChartSymbol(user00) != def_SymbolReplay) break; 73. GlobalVariableDel(def_GlobalVariableReplay); 74. ChartClose(user00); 75. break; 76. } 77. delete control; 78. } 79. //+------------------------------------------------------------------+
控制指标源代码
您可能已经注意到,该代码与之前的代码有很大不同。但最明显的区别是在 OnInit 函数中,在第 22 行,我们对控制类的引用有一个与前一个完全不同的声明。
如果您查看并尝试弄清楚第 22 行的作用,您可能会感到有些迷茫。但在这一行,我们做了两件事:
- 首先,我们将控制指标转换成一个模块;任何其他需要知道该指标功能的模块都可以使用这个模块。
- 第二,我们告诉控制指标,我们将不再使用代码来分析鼠标。换句话说,控制指标现在会询问鼠标指标用户正在做什么或将要做什么。根据这些信息,控制指标将执行适当的程序。
这一改变或决定,让鼠标指标负责用户、鼠标和图形之间的交互,无疑是实现这一目标的最佳方式。重复的事情是没有意义的,因为我们解决了鼠标指标中的问题,但却在控制指标中产生了问题,当我们将它们放在一起时,一个会干扰另一个。这不是我们想要的。我们希望它们两者能够和谐地工作。这样,我们就不会浪费时间进行修改,因为只需编辑交互的方式就足够了。
现在,如果你注意的话,你会注意到控制指标不再像以前那样提及 C_Terminal 类。为什么呢?原因就是继承。我决定将控制类设为 C_Terminal 类的子类。现在解释这一行动的原因有点困难,但为了我们以后要做的事情,这是必要的。
在牢记对指标代码的首次解释之后,让我们看一下控制类代码。
001. //+------------------------------------------------------------------+ 002. #property copyright "Daniel Jose" 003. //+------------------------------------------------------------------+ 004. #include "..\Auxiliar\Interprocess.mqh" 005. //+------------------------------------------------------------------+ 006. #define def_PathBMP "Images\\Market Replay\\Control\\" 007. #define def_ButtonPlay def_PathBMP + "Play.bmp" 008. #define def_ButtonPause def_PathBMP + "Pause.bmp" 009. #define def_ButtonLeft def_PathBMP + "Left.bmp" 010. #define def_ButtonLeftBlock def_PathBMP + "Left_Block.bmp" 011. #define def_ButtonRight def_PathBMP + "Right.bmp" 012. #define def_ButtonRightBlock def_PathBMP + "Right_Block.bmp" 013. #define def_ButtonPin def_PathBMP + "Pin.bmp" 014. #define def_ButtonWait def_PathBMP + "Wait.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. #resource "\\" + def_ButtonWait 023. //+------------------------------------------------------------------+ 024. #define def_PrefixObjectName "Market Replay _ " 025. #define def_NameObjectsSlider def_PrefixObjectName + "Slider" 026. #define def_PosXObjects 120 027. //+------------------------------------------------------------------+ 028. #include "..\Auxiliar\C_Terminal.mqh" 029. #include "..\Auxiliar\C_Mouse.mqh" 030. //+------------------------------------------------------------------+ 031. class C_Controls : private C_Terminal 032. { 033. protected: 034. enum EventCustom {ev_WaitOn, ev_WaitOff}; 035. private : 036. //+------------------------------------------------------------------+ 037. string m_szBtnPlay; 038. bool m_bWait; 039. struct st_00 040. { 041. string szBtnLeft, 042. szBtnRight, 043. szBtnPin, 044. szBarSlider, 045. szBarSliderBlock; 046. int posPinSlider, 047. posY, 048. Minimal; 049. }m_Slider; 050. C_Mouse *m_MousePtr; 051. //+------------------------------------------------------------------+ 052. inline void CreateObjectBitMap(int x, int y, string szName, string Resource1, string Resource2 = NULL) 053. { 054. ObjectCreate(GetInfoTerminal().ID, szName, OBJ_BITMAP_LABEL, 0, 0, 0); 055. ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_XDISTANCE, def_PosXObjects + x); 056. ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_YDISTANCE, y); 057. ObjectSetString(GetInfoTerminal().ID, szName, OBJPROP_BMPFILE, 0, "::" + Resource1); 058. ObjectSetString(GetInfoTerminal().ID, szName, OBJPROP_BMPFILE, 1, "::" + (Resource2 == NULL ? Resource1 : Resource2)); 059. ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_ZORDER, 1); 060. } 061. //+------------------------------------------------------------------+ 062. inline void CreteBarSlider(int x, int size) 063. { 064. ObjectCreate(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJ_RECTANGLE_LABEL, 0, 0, 0); 065. ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_XDISTANCE, def_PosXObjects + x); 066. ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_YDISTANCE, m_Slider.posY - 4); 067. ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_XSIZE, size); 068. ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_YSIZE, 9); 069. ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_BGCOLOR, clrLightSkyBlue); 070. ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_BORDER_COLOR, clrBlack); 071. ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_WIDTH, 3); 072. ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_BORDER_TYPE, BORDER_FLAT); 073. ObjectCreate(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJ_RECTANGLE_LABEL, 0, 0, 0); 074. ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_XDISTANCE, def_PosXObjects + x); 075. ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_YDISTANCE, m_Slider.posY - 9); 076. ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_YSIZE, 19); 077. ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_BGCOLOR, clrRosyBrown); 078. ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_BORDER_TYPE, BORDER_RAISED); 079. } 080. //+------------------------------------------------------------------+ 081. void CreateBtnPlayPause(bool state) 082. { 083. m_szBtnPlay = def_PrefixObjectName + "Play"; 084. CreateObjectBitMap(0, 25, m_szBtnPlay, (m_bWait ? def_ButtonWait : def_ButtonPause), (m_bWait ? def_ButtonWait : def_ButtonPlay)); 085. ObjectSetInteger(GetInfoTerminal().ID, m_szBtnPlay, OBJPROP_STATE, state); 086. } 087. //+------------------------------------------------------------------+ 088. void CreteCtrlSlider(void) 089. { 090. u_Interprocess Info; 091. 092. m_Slider.szBarSlider = def_NameObjectsSlider + " Bar"; 093. m_Slider.szBarSliderBlock = def_NameObjectsSlider + " Bar Block"; 094. m_Slider.szBtnLeft = def_NameObjectsSlider + " BtnL"; 095. m_Slider.szBtnRight = def_NameObjectsSlider + " BtnR"; 096. m_Slider.szBtnPin = def_NameObjectsSlider + " BtnP"; 097. m_Slider.posY = 40; 098. CreteBarSlider(77, 436); 099. CreateObjectBitMap(47, 25, m_Slider.szBtnLeft, def_ButtonLeft, def_ButtonLeftBlock); 100. CreateObjectBitMap(511, 25, m_Slider.szBtnRight, def_ButtonRight, def_ButtonRightBlock); 101. CreateObjectBitMap(0, m_Slider.posY, m_Slider.szBtnPin, def_ButtonPin); 102. ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBtnPin, OBJPROP_ANCHOR, ANCHOR_CENTER); 103. if (GlobalVariableCheck(def_GlobalVariableReplay)) Info.df_Value = GlobalVariableGet(def_GlobalVariableReplay); else Info.df_Value = 0; 104. m_Slider.Minimal = Info.s_Infos.iPosShift; 105. PositionPinSlider(Info.s_Infos.iPosShift); 106. } 107. //+------------------------------------------------------------------+ 108. inline void RemoveCtrlSlider(void) 109. { 110. ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, false); 111. ObjectsDeleteAll(GetInfoTerminal().ID, def_NameObjectsSlider); 112. ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, true); 113. } 114. //+------------------------------------------------------------------+ 115. inline void PositionPinSlider(int p, const int minimal = 0) 116. { 117. m_Slider.posPinSlider = (p < minimal ? minimal : (p > def_MaxPosSlider ? def_MaxPosSlider : p)); 118. m_Slider.posPinSlider = (p < m_Slider.Minimal ? m_Slider.Minimal : (p > def_MaxPosSlider ? def_MaxPosSlider : p)); 119. ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBtnPin, OBJPROP_XDISTANCE, m_Slider.posPinSlider + def_PosXObjects + 95); 120. ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBtnLeft, OBJPROP_STATE, m_Slider.posPinSlider != minimal); 121. ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBtnLeft, OBJPROP_STATE, m_Slider.posPinSlider != m_Slider.Minimal); 122. ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBtnRight, OBJPROP_STATE, m_Slider.posPinSlider < def_MaxPosSlider); 123. ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_XSIZE, minimal + 2); 124. ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_XSIZE, m_Slider.Minimal + 2); 125. ChartRedraw(GetInfoTerminal().ID); 126. } 127. //+------------------------------------------------------------------+ 128. public : 129. //+------------------------------------------------------------------+ 130. C_Controls(const long Arg0, const string szShortName, C_Mouse *MousePtr) 131. :C_Terminal(Arg0), 132. m_bWait(false), 133. m_MousePtr(MousePtr) 134. { 135. if (!IndicatorCheckPass(szShortName)) SetUserError(C_Terminal::ERR_Unknown); 136. m_szBtnPlay = NULL; 137. m_Slider.szBarSlider = NULL; 138. m_Slider.szBtnPin = NULL; 139. m_Slider.szBtnLeft = NULL; 140. m_Slider.szBtnRight = NULL; 141. } 142. //+------------------------------------------------------------------+ 143. ~C_Controls() 144. { 145. ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, false); 146. ObjectsDeleteAll(GetInfoTerminal().ID, def_PrefixObjectName); 147. } 148. //+------------------------------------------------------------------+ 149. void Init(const bool state) 150. { 151. CreateBtnPlayPause(state); 152. GlobalVariableTemp(def_GlobalVariableReplay); 153. if (!state) CreteCtrlSlider(); 154. ChartRedraw(GetInfoTerminal().ID); 155. } 156. //+------------------------------------------------------------------+ 157. void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam) 158. { 159. u_Interprocess Info; 160. 161. switch (id) 162. { 163. case (CHARTEVENT_CUSTOM + C_Controls::ev_WaitOn): 164. if (lparam == 0) break; 165. m_bWait = true; 166. CreateBtnPlayPause(true); 167. break; 168. case (CHARTEVENT_CUSTOM + C_Controls::ev_WaitOff): 169. if (lparam == 0) break; 170. m_bWait = false; 171. Info.df_Value = dparam; 172. CreateBtnPlayPause(Info.s_Infos.isPlay); 173. break; 174. case CHARTEVENT_OBJECT_DELETE: 175. if (StringSubstr(sparam, 0, StringLen(def_PrefixObjectName)) == def_PrefixObjectName) 176. { 177. if (StringSubstr(sparam, 0, StringLen(def_NameObjectsSlider)) == def_NameObjectsSlider) 178. { 179. RemoveCtrlSlider(); 180. CreteCtrlSlider(); 181. }else 182. { 183. Info.df_Value = GlobalVariableGet(def_GlobalVariableReplay); 184. CreateBtnPlayPause(Info.s_Infos.isPlay); 185. } 186. ChartRedraw(GetInfoTerminal().ID); 187. } 188. break; 189. } 190. } 191. //+------------------------------------------------------------------+ 192. }; 193. //+------------------------------------------------------------------+ 194. #undef def_PosXObjects 195. #undef def_ButtonPlay 196. #undef def_ButtonPause 197. #undef def_ButtonLeft 198. #undef def_ButtonRight 199. #undef def_ButtonPin 200. #undef def_NameObjectsSlider 201. #undef def_PrefixObjectName 202. #undef def_PathBMP 203. //+------------------------------------------------------------------+
C_Control 类的源代码
您可能不会注意到代码中的任何重大变化。事实上,它们非常微妙。首先,控制类是 C_Terminal 类的私有继承者。这可在第 31 行看到。这样我们就不再需要使用指针来访问 C_Terminal 类。换句话说,由于某些编译细节,对 C_Terminal 类的批量访问可以直接执行,并且在某些情况下速度会更快一些,但这不再重要。
在第 130 行,声明类构造函数的地方,我们发现了一些非常有趣的事情。请注意,在此早期阶段,只有服务才能访问此控制指标。看第 135 行,实际上,这并不是真正必要的,但由于代码重用总是应该的,我们确保指标遵循我们想要的假设。换句话说,每个图表应该只有一个指标。同样,这不是必需的,因为在早期阶段只有服务可以访问该指标并将其添加到图表中。还有其他方法可以做到这一点,但我不会详细说明,因为我不想鼓励任何人在不了解其工作原理的情况下使用该资源。
在这个构造函数中另一件值得一提的是,在第 133 行,我们在一个私有全局类变量中存储了访问鼠标指标的指针。但这以后将会变得清晰。
如果你注意的话,你会看到在这个代码中已经做了修复,这是一个非常微妙的变化,但它带来了很大的不同。它不是为了代码,而是为了 MetaTrader 5。重点是 ChartRedraw 调用会接收一个数值,我们通常不会向此调用传递任何值。那么我们为什么现在要这样做呢?原因是 Chart ID 值不同。
我在上一篇文章中已经解释了这个差异。不过这可能还不够清楚,让我们巩固一下所学到的知识。Chart ID 的主要问题不是谁会打开图表,而是谁会在图表上放置对象。让我们花点时间来了解为什么有时我们需要将值传递到 ChartRedraw 调用中,而有时则不需要。
当服务打开图表(尽管可以是任何对象)时,该图表会从 MetaTrader 5 获取一个 ID。如果您检查此标识符,您将在解析 ChartOpen 返回的值时看到一些值。好的,现在,举个例子,要在此图表上放置一个指标,程序必须使用 ChartOpen 函数返回的相同标识符。到目前为止一切都清楚了,但问题就在这时出现了。
当您作为用户在图表上放置相同的指标时,您将不会获得程序使用 ChartOpen 获得的相同图表 ID。现在一切似乎都很混乱,甚至可能看起来我疯了,或者不明白我在说什么。当服务打开图表时,我们将通过 ChartOpen 获取 ID 值,我们应该使用这个值来创建指标句柄,以将指标放置在所需的图表上。如果指标(与控制指标一样)使用函数通过 ObjectCreate 调用将对象放置在图表上,我们将需要指定一个标识符,以便 MetaTrader 5 知道哪个图表是正确的。
如果图表是通过 ChartOpen 打开的,则提供的 ID 不能是通过 ChartID 获得的 ID。如果我们使用 ChartID 中指定的图表 ID,那么在使用 ChartOpen 打开的图表上调用 ObjectCreate 时,对象将不会显示在图表上。但是如果用户或模板使用 ObjectCreate 函数将相同的代码放置在图表上,则必须使用 ChartID 提供的值作为 ID。
我知道这看起来很令人困惑,但这正是它所需要的。因此,几篇文章之前,我们专门更新了 C_Terminal 类来处理这个问题。
但由于我们的代码不知道究竟是谁在图表上执行了它,我们使用对 C_Terminal 类的调用来返回正确的 ID。这样,ChartRedraw 函数就会按照预期正确地更新图表。
尽管存在这些复杂性,但在第 157 行(消息处理程序开始的位置)中,您可以看到此代码负责处理 MetaTrader 5 报告的事件。我们删除了解析鼠标移动和点击事件的事件。这是因为我们将以稍微不同的方式处理这种事件,尽管是在同一个函数中。
但是,在开始处理之前,我们需要进行一些额外的更改。但现在它不在这个指标代码中,也不在服务代码中。我们必须返回鼠标指标代码并对其进行一些更改。
更新鼠标指标代码
我们即将进行的更新不会与鼠标指标中已经使用的功能冲突。出于上述原因,我们需要进行此更新。一旦我们运行回放/模拟器服务,它将在图表上启动鼠标指标,以便我们可以与回放/模拟器系统进行交互。但是您不必担心某些事情,因为代码本身会做出必要的更改,以便鼠标指标的工作方式与您(用户)手动或使用模板将其放置在图表上一样。
当我们使用所拥有的工具正确初始化回放/模拟器系统时,大问题就出现了。因此,让我们看一下已更新的第一件事,即鼠标指标源代码。这是其完整代码。
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.51" 07. #property icon "/Images/Market Replay/Icons/Indicators.ico" 08. #property link "https://www.mql5.com/en/articles/11877" 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 - 4; 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. SetBuffer(); 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. delete Study; 81. } 82. } 83. //+------------------------------------------------------------------+ 84. inline void SetBuffer(void) 85. { 86. uCast_Double Info; 87. 88. m_posBuff = (m_posBuff < 0 ? 0 : m_posBuff); 89. m_Buff[m_posBuff + 0] = (*Study).GetInfoMouse().Position.Price; 90. Info._datetime = (*Study).GetInfoMouse().Position.dt; 91. m_Buff[m_posBuff + 1] = Info.dValue; 92. Info._int[0] = (*Study).GetInfoMouse().Position.X; 93. Info._int[1] = (*Study).GetInfoMouse().Position.Y; 94. m_Buff[m_posBuff + 2] = Info.dValue; 95. Info._char[0] = ((*Study).GetInfoMouse().ExecStudy == C_Mouse::eStudyNull ? (char)(*Study).GetInfoMouse().ButtonStatus : 0); 96. m_Buff[m_posBuff + 3] = Info.dValue; 97. } 98. //+------------------------------------------------------------------+
鼠标指标源代码
注意:您可能认为此代码与之前的版本没有什么区别,但其实是有区别的。你必须对它们极其小心。
对于用户来说,主要的区别在第 17 行。这可能是整个代码中最复杂的一行。理想情况下,用户应该永远不能更改此行或在指标界面中看到它。这是因为任何新用户都会倾向于更改此行,因为它是一个输入参数。但是,请记住您绝不能更改此值。该值必须由调用指标的程序或指标本身设置的。但是,作为用户,您绝不应该更改此值。其他值可以由用户配置和更改,不会出现任何问题,但 ID 值永远不能更改。
除此之外,第 30 行的内容很有趣。在这里我们初始化学习类指针。以前构造函数接收 3 个值,主要是鼠标指标中使用的颜色,但现在构造函数将接收两个额外的值。第一个值是图表 ID,第二个值是指标名称。该名称将用于后续对指标缓冲区的访问。因此,每当鼠标指标位于图表上并且我们想要读取其缓冲区时,我们都会使用此处指定的名称。
代码中还有一些差异,但由于它们并不难理解,因此我们不再讨论。然后我们可以继续进行 C_Study 类代码。在这段代码中,我们不会使用指针来访问 C_Terminal 类,而是使用继承。
001. //+------------------------------------------------------------------+ 002. #property copyright "Daniel Jose" 003. //+------------------------------------------------------------------+ 004. #include "..\C_Mouse.mqh" 005. //+------------------------------------------------------------------+ 006. #define def_ExpansionPrefix "MouseExpansion_" 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. }m_Info; 024. //+------------------------------------------------------------------+ 025. const datetime GetBarTime(void) 026. { 027. datetime dt; 028. u_Interprocess info; 029. int i0 = PeriodSeconds(); 030. 031. if (m_Info.Status == eInReplay) 032. { 033. if (!GlobalVariableGet(def_GlobalVariableServerTime, info.df_Value)) return ULONG_MAX; 034. if ((dt = info.ServerTime) == 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. ObjectSetInteger(GetInfoTerminal().ID, def_ExpansionBtn1, OBJPROP_YDISTANCE, GetInfoMouse().Position.Y - 18); 047. ObjectSetInteger(GetInfoTerminal().ID, def_ExpansionBtn2, OBJPROP_YDISTANCE, GetInfoMouse().Position.Y - 1); 048. ObjectSetInteger(GetInfoTerminal().ID, def_ExpansionBtn3, OBJPROP_YDISTANCE, GetInfoMouse().Position.Y - 1); 049. ObjectSetString(GetInfoTerminal().ID, def_ExpansionBtn1, OBJPROP_TEXT, m_Info.szInfo); 050. v1 = NormalizeDouble(100.0 - ((m_Info.Rate.close / GetInfoMouse().Position.Price) * 100.0), 2); 051. ObjectSetInteger(GetInfoTerminal().ID, def_ExpansionBtn2, OBJPROP_BGCOLOR, (v1 < 0 ? m_Info.corN : m_Info.corP)); 052. ObjectSetString(GetInfoTerminal().ID, def_ExpansionBtn2, OBJPROP_TEXT, StringFormat("%.2f%%", MathAbs(v1))); 053. v1 = NormalizeDouble(100.0 - ((m_Info.Rate.close / iClose(GetInfoTerminal().szSymbol, PERIOD_D1, 0)) * 100.0), 2); 054. ObjectSetInteger(GetInfoTerminal().ID, def_ExpansionBtn3, OBJPROP_BGCOLOR, (v1 < 0 ? m_Info.corN : m_Info.corP)); 055. ObjectSetString(GetInfoTerminal().ID, def_ExpansionBtn3, OBJPROP_TEXT, StringFormat("%.2f%%", MathAbs(v1))); 056. } 057. //+------------------------------------------------------------------+ 058. public : 059. //+------------------------------------------------------------------+ 060. C_Study(long IdParam, string szShortName, color corH, color corP, color corN) 061. :C_Mouse(IdParam, szShortName, corH, corP, corN) 062. { 063. if (_LastError != ERR_SUCCESS) return; 064. ZeroMemory(m_Info); 065. m_Info.Status = eCloseMarket; 066. m_Info.Rate.close = iClose(GetInfoTerminal().szSymbol, PERIOD_D1, ((GetInfoTerminal().szSymbol == def_SymbolReplay) || (macroGetDate(TimeCurrent()) != macroGetDate(iTime(GetInfoTerminal().szSymbol, PERIOD_D1, 0))) ? 0 : 1)); 067. m_Info.corP = corP; 068. m_Info.corN = corN; 069. CreateObjectInfo(2, 110, def_ExpansionBtn1, clrPaleTurquoise); 070. CreateObjectInfo(2, 53, def_ExpansionBtn2); 071. CreateObjectInfo(58, 53, def_ExpansionBtn3); 072. } 073. //+------------------------------------------------------------------+ 074. ~C_Study() 075. { 076. ObjectsDeleteAll(GetInfoTerminal().ID, def_ExpansionPrefix); 077. } 078. //+------------------------------------------------------------------+ 079. void Update(const eStatusMarket arg) 080. { 081. datetime dt; 082. 083. switch (m_Info.Status = (m_Info.Status != arg ? arg : m_Info.Status)) 084. { 085. case eCloseMarket : m_Info.szInfo = "Closed Market"; 086. break; 087. case eInReplay : 088. case eInTrading : 089. if ((dt = GetBarTime()) < ULONG_MAX) 090. { 091. m_Info.szInfo = TimeToString(dt, TIME_SECONDS); 092. break; 093. } 094. case eAuction : m_Info.szInfo = "Auction"; 095. break; 096. default : m_Info.szInfo = "ERROR"; 097. } 098. Draw(); 099. } 100. //+------------------------------------------------------------------+ 101. virtual void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam) 102. { 103. C_Mouse::DispatchMessage(id, lparam, dparam, sparam); 104. if (id == CHARTEVENT_MOUSE_MOVE) Draw(); 105. } 106. //+------------------------------------------------------------------+ 107. }; 108. //+------------------------------------------------------------------+ 109. #undef def_ExpansionBtn3 110. #undef def_ExpansionBtn2 111. #undef def_ExpansionBtn1 112. #undef def_ExpansionPrefix 113. //+------------------------------------------------------------------+
C_Study 类的源代码
此代码与旧代码几乎没有区别。唯一的区别是我们直接在 C_Terminal 类中查找图形标识符。以前我们用指针,现在用继承,但是这个继承从何而来?它来自 C_Mouse 类。让我们看一下 C_Mouse 类的代码以更好地理解这种继承。
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. class C_Mouse : public C_Terminal 014. { 015. public : 016. enum eStatusMarket {eCloseMarket, eAuction, eInTrading, eInReplay}; 017. enum eBtnMouse {eKeyNull = 0x00, eClickLeft = 0x01, eClickRight = 0x02, eSHIFT_Press = 0x04, eCTRL_Press = 0x08, eClickMiddle = 0x10}; 018. struct st_Mouse 019. { 020. struct st00 021. { 022. int X, 023. Y; 024. double Price; 025. datetime dt; 026. }Position; 027. uint ButtonStatus; 028. bool ExecStudy; 029. }; 030. //+------------------------------------------------------------------+ 031. protected: 032. enum eEventsMouse {ev_HideMouse, ev_ShowMouse}; 033. //+------------------------------------------------------------------+ 034. void CreateObjectInfo(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. //+------------------------------------------------------------------+ 069. void GetDimensionText(const string szArg, int &w, int &h) 070. { 071. TextSetFont("Lucida Console", -100, FW_NORMAL); 072. TextGetSize(szArg, w, h); 073. h += 5; 074. w += 5; 075. } 076. //+------------------------------------------------------------------+ 077. void CreateStudy(void) 078. { 079. if (m_Mem.IsFull) 080. { 081. CreateObjectGraphics(def_NameObjectLineV, OBJ_VLINE, m_Info.corLineH); 082. CreateObjectGraphics(def_NameObjectLineT, OBJ_TREND, m_Info.corLineH); 083. ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectLineT, OBJPROP_WIDTH, 2); 084. CreateObjectInfo(0, 0, def_NameObjectStudy); 085. } 086. m_Info.Study = eStudyCreate; 087. } 088. //+------------------------------------------------------------------+ 089. void ExecuteStudy(const double memPrice) 090. { 091. double v1 = GetInfoMouse().Position.Price - memPrice; 092. int w, h; 093. 094. if (!CheckClick(eClickLeft)) 095. { 096. m_Info.Study = eStudyNull; 097. ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, true); 098. if (m_Mem.IsFull) ObjectsDeleteAll(GetInfoTerminal().ID, def_MousePrefixName + "T"); 099. }else if (m_Mem.IsFull) 100. { 101. string sz1 = StringFormat(" %." + (string)GetInfoTerminal().nDigits + "f [ %d ] %02.02f%% ", 102. MathAbs(v1), Bars(GetInfoTerminal().szSymbol, PERIOD_CURRENT, m_Mem.dt, GetInfoMouse().Position.dt) - 1, MathAbs((v1 /memPrice) * 100.0))); 103. GetDimensionText(sz1, w, h); 104. ObjectSetString(GetInfoTerminal().ID, def_NameObjectStudy, OBJPROP_TEXT, sz1); 105. ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectStudy, OBJPROP_BGCOLOR, (v1 < 0 ? m_Info.corTrendN : m_Info.corTrendP)); 106. ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectStudy, OBJPROP_XSIZE, w); 107. ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectStudy, OBJPROP_YSIZE, h); 108. ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectStudy, OBJPROP_XDISTANCE, GetInfoMouse().Position.X - w); 109. ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectStudy, OBJPROP_YDISTANCE, GetInfoMouse().Position.Y - (v1 < 0 ? 1 : h)); 110. ObjectMove(GetInfoTerminal().ID, def_NameObjectLineT, 1, GetInfoMouse().Position.dt, GetInfoMouse().Position.Price); 111. ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectLineT, OBJPROP_COLOR, (memPrice > GetInfoMouse().Position.Price ? m_Info.corTrendN : m_Info.corTrendP)); 112. } 113. m_Info.Data.ButtonStatus = eKeyNull; 114. } 115. //+------------------------------------------------------------------+ 116. public : 117. //+------------------------------------------------------------------+ 118. C_Mouse(const long id, const string szShortName) 119. :C_Terminal(id) 120. { 121. m_Mem.szShortName = szShortName; 122. } 123. //+------------------------------------------------------------------+ 124. C_Mouse(const long id, const string szShortName, color corH, color corP, color corN) 125. :C_Terminal(id) 126. { 127. if (!IndicatorCheckPass(szShortName)) SetUserError(C_Terminal::ERR_Unknown); 128. m_Mem.szShortName = NULL; 129. if (_LastError != ERR_SUCCESS) return; 130. m_Mem.CrossHair = (bool)ChartGetInteger(GetInfoTerminal().ID, CHART_CROSSHAIR_TOOL); 131. ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_MOUSE_MOVE, true); 132. ChartSetInteger(GetInfoTerminal().ID, CHART_CROSSHAIR_TOOL, false); 133. ZeroMemory(m_Info); 134. m_Info.corLineH = corH; 135. m_Info.corTrendP = corP; 136. m_Info.corTrendN = corN; 137. m_Info.Study = eStudyNull; 138. if (m_Mem.IsFull = (corP != clrNONE) && (corH != clrNONE) && (corN != clrNONE)) 139. CreateObjectGraphics(def_NameObjectLineH, OBJ_HLINE, m_Info.corLineH); 140. } 141. //+------------------------------------------------------------------+ 142. ~C_Mouse() 143. { 144. ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, 0, false); 145. ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_MOUSE_MOVE, false); 146. ChartSetInteger(GetInfoTerminal().ID, CHART_CROSSHAIR_TOOL, m_Mem.CrossHair); 147. ObjectsDeleteAll(GetInfoTerminal().ID, def_MousePrefixName); 148. } 149. //+------------------------------------------------------------------+ 150. inline bool CheckClick(const eBtnMouse value) 151. { 152. return (GetInfoMouse().ButtonStatus & value) == value; 153. } 154. //+------------------------------------------------------------------+ 155. inline const st_Mouse GetInfoMouse(void) 156. { 157. if (m_Mem.szShortName != NULL) 158. { 159. double Buff[]; 160. uCast_Double loc; 161. int handle = ChartIndicatorGet(GetInfoTerminal().ID, 0, m_Mem.szShortName); 162. 163. ZeroMemory(m_Info.Data); 164. if (CopyBuffer(handle, 0, 0, 4, Buff) == 4) 165. { 166. m_Info.Data.Position.Price = Buff[0]; 167. loc.dValue = Buff[1]; 168. m_Info.Data.Position.dt = loc._datetime; 169. loc.dValue = Buff[2]; 170. m_Info.Data.Position.X = loc._int[0]; 171. m_Info.Data.Position.Y = loc._int[1]; 172. loc.dValue = Buff[3]; 173. m_Info.Data.ButtonStatus = loc._char[0]; 174. IndicatorRelease(handle); 175. } 176. } 177. 178. return m_Info.Data; 179. } 180. //+------------------------------------------------------------------+ 181. virtual void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam) 182. { 183. int w = 0; 184. static double memPrice = 0; 185. 186. if (m_Mem.szShortName == NULL) switch (id) 187. { 188. case (CHARTEVENT_CUSTOM + ev_HideMouse): 189. if (m_Mem.IsFull) ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectLineH, OBJPROP_COLOR, clrNONE); 190. break; 191. case (CHARTEVENT_CUSTOM + ev_ShowMouse): 192. if (m_Mem.IsFull) ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectLineH, OBJPROP_COLOR, m_Info.corLineH); 193. break; 194. case CHARTEVENT_MOUSE_MOVE: 195. ChartXYToTimePrice(GetInfoTerminal().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); 196. if (m_Mem.IsFull) ObjectMove(GetInfoTerminal().ID, def_NameObjectLineH, 0, 0, m_Info.Data.Position.Price = AdjustPrice(m_Info.Data.Position.Price)); 197. m_Info.Data.Position.dt = AdjustTime(m_Info.Data.Position.dt); 198. ChartTimePriceToXY(GetInfoTerminal().ID, w, m_Info.Data.Position.dt, m_Info.Data.Position.Price, m_Info.Data.Position.X, m_Info.Data.Position.Y); 199. if ((m_Info.Study != eStudyNull) && (m_Mem.IsFull)) ObjectMove(GetInfoTerminal().ID, def_NameObjectLineV, 0, m_Info.Data.Position.dt, 0); 200. m_Info.Data.ButtonStatus = (uint) sparam; 201. if (CheckClick(eClickMiddle)) 202. if ((!m_Mem.IsFull) || ((color)ObjectGetInteger(GetInfoTerminal().ID, def_NameObjectLineH, OBJPROP_COLOR) != clrNONE)) CreateStudy(); 203. if (CheckClick(eClickLeft) && (m_Info.Study == eStudyCreate)) 204. { 205. ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, false); 206. if (m_Mem.IsFull) ObjectMove(GetInfoTerminal().ID, def_NameObjectLineT, 0, m_Mem.dt = GetInfoMouse().Position.dt, memPrice = GetInfoMouse().Position.Price); 207. m_Info.Study = eStudyExecute; 208. } 209. if (m_Info.Study == eStudyExecute) ExecuteStudy(memPrice); 210. m_Info.Data.ExecStudy = m_Info.Study == eStudyExecute; 211. break; 212. case CHARTEVENT_OBJECT_DELETE: 213. if ((m_Mem.IsFull) && (sparam == def_NameObjectLineH)) CreateObjectGraphics(def_NameObjectLineH, OBJ_HLINE, m_Info.corLineH); 214. break; 215. } 216. } 217. //+------------------------------------------------------------------+ 218. }; 219. //+------------------------------------------------------------------+ 220. #undef def_MousePrefixName 221. #undef def_NameObjectLineV 222. #undef def_NameObjectLineH 223. #undef def_NameObjectLineT 224. #undef def_NameObjectStudy 225. //+------------------------------------------------------------------+
C_Mouse 类的源代码
与以前一样,此代码与旧代码之间的主要区别在于不再使用指针来访问 C_Terminal 类。该过程的职责可以在第 13 行看到,其中我们从 C_Terminal 类继承到 C_Mouse 类。因此,C_Terminal 类的所有过程和公有函数现在都扩展了 C_Mouse 类。所有代码大部分已在与鼠标指标相关的文章中解释清楚。
关于这个话题的最后一篇文章是开发回放系统(第 31 部分):EA 项目 - C_Mouse 类 (五),您可以使用本文跳转到有关此鼠标指标的第一篇文章,因为文章开头有一个链接,就像我所有的文章一样,这样您就可以回去跟踪系统开发的演变。
如果您对鼠标指标的工作原理有任何疑问,或者想要根据个人需要进行调整,您可以阅读文章来了解有关指标如何开发的解释。尝试了解它是如何工作的,然后回到这个地方并添加所示的修改。您还可以在鼠标指标中放置您需要的其他东西。如果您所有操作都正确,您将能够在此回放/模拟器系统中使用您自己的鼠标指标。
对于那些真正想学习编程的人来说,这里有一个小建议:尝试为鼠标指标添加新功能,并在回放/模拟器系统中使用它。但别忘了指出你知识的来源,这会给我带来极大的快乐。它不仅激励了我,而且我也喜欢展示我是如何解决许多人认为无法克服的问题的。
但是让我们回到对代码的解释。在 C_Mouse 类的构造函数中,您可以看到在第 125 行我们初始化了 C_Terminal 类。初始化期间要使用的值在指标代码中,在名为 user00 的输入参数中提供。如果用户保持不变此输入,C_Terminal 类将确定图表 ID,并在必要时切换使用此 ID 将对象放置在所需图表上。
此外,第 127 行锁定指标,使得它不能多次放置在图表上。这将阻止用户在图表上放置另一个鼠标指标。该指标将出现在 MetaTrader 5 指标列表中。
其余代码的工作方式与以前相同。该鼠标指标将负责告诉 MetaTrader 5 接收鼠标事件并将其发送到指标所在的图表。请注意,为了在 MetaTrader 5 中接收鼠标事件,鼠标指标必须位于图表上;尝试从另一个图表访问该指标是没有意义的。即使我们有能力读取鼠标指标在另一个图表上的作用,MetaTrader 5 也不会为不包含此类指标的图表生成鼠标事件。
第 131 行负责这个。因此,只要指标在图表上,任何 EA 交易或其他指标都会接收鼠标事件。记住这一事实,因为以下代码将要求鼠标指标出现在图表上,以便 MetaTrader 5 将鼠标事件传递给以特定方式处理它们的其他代码。
在进入下一阶段修改控制指标之前,我认为有必要对实际情况做出更精确的解释。这样你才能正确的理解本文的全部内容。
理解和吸收知识
你可能会认为我在这篇文章中谈论的一切都是无稽之谈,因为你们中的许多人肯定对 MQL5 有很多经验,或者认识一个编码系统专家。但为了让一切都清楚,我将把鼠标指标的可执行文件附加到本文中。这样做是为了简单起见,但是您可以使用本系列中的材料,并且您将获得相同的结果,因为我将完整的代码放在文章中。您可以尝试一下,看看使用以下代码会发生什么,它比回放/模拟器系统简单得多,但工作原理相同。
01. //+------------------------------------------------------------------+ 02. #property service 03. #property copyright "Daniel Jose" 04. #property version "1.00" 05. //+------------------------------------------------------------------+ 06. input string user00 = "EURUSD"; // Symbol 07. //+------------------------------------------------------------------+ 08. void OnStart() 09. { 10. long id; 11. int handle; 12. 13. SymbolSelect(user00, true); 14. id = ChartOpen(user00, PERIOD_M5); 15. handle = iCustom(ChartSymbol(id), ChartPeriod(id), "\\Indicators\\Replay\\Mouse Study.ex5", id); 16. ChartIndicatorAdd(id, 0, handle); 17. IndicatorRelease(handle); 18. 19. Print("ID: ", id); 20. 21. while ((!_StopFlag) && (ChartSymbol(id) == user00)) Sleep(300); 22. 23. ChartClose(id); 24. SymbolSelect(user00, false); 25. } 26. //+------------------------------------------------------------------+
测试服务源代码
在第 06 行,我们为用户提供了指定资产的选项。它可用于打开图表,但交易品种必须位于市场观察窗口中。为此,我们使用的是第 13 行。在第 14 行,我们要求 MetaTrader 5 为我们打开一个图表。在第 15 行中,我们创建一个句柄,将指标添加到图表中;在这种情况下,我们将强制 MetaTrader 5 加载鼠标指标。在第 16 行,我们将指标添加到图表中。在第 17 行,我们释放句柄,因为我们不再需要它了。在第 19 行,我们向终端打印一条消息,指示服务打开的图表的 ID。现在,在第 21 行,我们等待用户关闭图表或终止服务。
如果用户关闭图表,第 23 行将不执行任何操作。然而,如果用户终止服务,第23行将关闭之前打开的图表。第 24 行从市场观察窗口中删除了该交易品种。另一点:要删除的交易品种,它不能有任何与之关联的元素。在这种情况下,我们仅使用图表进行测试,因此不会有任何相关内容。
您可能认为这段代码没什么意义。为什么要费心创建这样的东西,并试图解释它是如何工作的?这正是每个人都应该做的:努力探索超出许多人感到舒适的界限。只有这样,你才能真正理解一切是如何运作的。
我希望你能理解这个思路和本质,因为它对以下文章非常重要。您可以观看视频 01 并查看测试。您也可以尝试使用自己的标准亲自测试。我想强调的是:不要仅仅因为某人在你看来更优秀或更有能力就接受他们的“真理”。测试、质疑权威,只有这样,真正的知识才会变得清晰。
视频 01
结论
这篇文章是迄今为止最难的文章之一,这是由于所提供信息的复杂性造成的。虽然编程本身对我来说很简单,但解释正在发生的事情非常困难。原因是它不仅适合知识水平较高的人阅读,而且适合初次尝试了解这个编程世界的爱好者阅读。
在下一篇文章中,我将展示如何在鼠标指标、控制指标、回放/模拟器系统和您(亲爱的系统用户)之间创建交互。回头见!
本文由MetaQuotes Ltd译自葡萄牙语
原文地址: https://www.mql5.com/pt/articles/11877
注意: MetaQuotes Ltd.将保留所有关于这些材料的权利。全部或部分复制或者转载这些材料将被禁止。
本文由网站的一位用户撰写,反映了他们的个人观点。MetaQuotes Ltd 不对所提供信息的准确性负责,也不对因使用所述解决方案、策略或建议而产生的任何后果负责。


