市场模拟(第二部分):跨期订单(二)
概述
在上一篇文章市场模拟(第一部分):跨期订单(一)中,我演示并解释了一个相当常见的问题的替代解决方案,特别是对于那些以某种方式交易期货合约的人来说。虽然我没有提出最终的解决方案 —— 因为整篇文章只关注 Chart Trade 指标 —— 但其中涵盖的内容至关重要。它使我们有可能通过 C_Terminal 类为交易这些期货合约提供一个合适的名称。
然而,问题仍然存在。这并不是因为 Chart Trade 或 EA 交易,而是因为挂单系统。虽然我们在这些文章中还没有开始讨论它,但我们必须以某种方式为此做好准备。该系统使用的信息部分来自 Chart Trade,与服务器的所有通信均通过 EA 交易进行。换句话说,我们有一个三角形,其中每个顶点代表要开发的应用程序之一。与服务器通信的边源自 EA 交易的顶点。
因此,考虑让 EA 交易控制期货合约的选择是完全合理的。这将使我们能够在特定时间决定是交易迷你合约还是完整合约。但是,此选项无法在所有应用程序中复制。决定交易哪种类型的合约必须在一个地点进行,以避免选择和信息中的歧义。否则,如果可以在多个地方选择合约类型,系统会让用户非常困惑。
通过在 Chart Trade 中进行选择,如上一篇文章所示,我们可以控制该过程。然而,由于 EA 交易是唯一直接与服务器通信以发送订单的组件,因此考虑将选择放在那里也是合乎逻辑的。这引入了另一种类型的问题,演示一种解决方法将是本文的重点。但在你假设我们在这里提出一个明确的解决方案之前,我想强调的是,下面是一个拟议的解决方案。最终的解决方案将在稍后讨论,因为我还没有决定最终的方法。
理解问题
由于所提出的解决方案确实将使用 EA 交易来选择合约类型,因此我们需要修改 Chart Trade 的某些方面。这是因为当合约类型选择置于 EA 交易的控制之下时就会出现问题。问题在于 Chart Trade 显示的信息,如下图所示。

请注意,我正在突出显示资产或合约名称。其中一个关键问题是,该名称用于计算 Chart Trade 界面中显示的财务价值。问题变得更加严重,因为 Chart Trade 将已经转换为报价的值发送给 EA 交易,而 EA 交易仅负责发送市场订单。
现在考虑一下挑战:如果 EA 交易将合约类型更改为迷你合约或完整合约,Chart Trade 必须复制此信息,以便用户或操作员不会犯错误。复制这些信息本身并不困难。我们可以通过将 Chart Trade 链接到 EA 交易来实现这一点。与当前设置不同,用户必须将 Chart Trade 添加到图表中,而 EA 交易会处理此问题。用户将 EA 交易放置在图表上,然后它会添加 Chart Trade 指标,自动调整合约信息。
这将是最简单、最明显的解决方案。然而,这需要在创建的每个 EA 交易中放置 Chart Trade 指标 —— 这是一种完全不可行的方法。这并不是因为它无法做到,而是因为对 Chart Trade 的任何改进都需要重新编译所有 EA 交易。这就是为什么 Chart Trade 被设计为与 EA 交易分开的原因。
因此,我们将把各个组件分开。那么,在这种情况下我们该如何解决这个问题呢?已经有人提出了解决方案:在 EA 交易和 Chart Trade 指标之间使用消息交换。很简单,不是吗?但事实上,事情并没有那么简单。这就是为什么我决定在本文中解释实现。
第一个问题涉及应用程序添加到图表中的顺序。为何如此?我们可以强制用户在 Chart Trade 之前添加 EA 交易。然后,当加载 Chart Trade 时,它会向 EA 交易查询合约类型。听起来不错,但此时还有另一个问题。虽然我们可以指导用户正确的顺序,但我们不能强迫 MetaTrader 5 遵守。也许您没有考虑以下问题,即第二个问题:当 MetaTrader 5 更改图表时间周期时,它会破坏并重新加载图表。这适用于指标和 EA 交易。现在的问题是:谁先加载 —— Chart Trade 还是 EA 交易?如果 EA 交易首先加载,问题一就能解决问题二。但是如果 Chart Trade 先加载会怎样?
正如您所见,情况比看上去的要复杂得多。此外,还有第三个问题:如果用户更改 EA 交易中的合约参数或任何其他参数,Chart Trade 将无法正确更新。所有先前的规划仅解决前两个问题。出现此问题的原因是 MetaTrader 5 会将 EA 交易删除并重新添加到图表中。此外,Chart Trade 必须以某种方式意识到 EA 交易的存在。否则,就会出现更多问题。
幸运的是,意识问题可以暂时搁置。该系统旨在向用户提供有关其状态的反馈。因此,在另一个时刻,这种意识问题将成为一个真正的问题。在当前的 Chart Trade 和 EA 交易交互环境中,这不是一个关键问题。因此,为了避免大幅度的代码修改,我们将做出一定的让步。组件的分离将在新的部分中讨论。
做出一些让步
我们在这里实施的改变将是永久性的,这意味着代码将继续发展而不是倒退。这些让步将允许使用以前不可用的某些功能。因此,我们应当谨慎并专注地利用这些新的可能性。
第一个变化可以在文件 C_Terminal.mqh 中看到。下面,您可以找到要使用的完整更新代码。
001. //+------------------------------------------------------------------+ 002. #property copyright "Daniel Jose" 003. //+------------------------------------------------------------------+ 004. #include "Macros.mqh" 005. #include "..\Defines.mqh" 006. //+------------------------------------------------------------------+ 007. class C_Terminal 008. { 009. //+------------------------------------------------------------------+ 010. protected: 011. enum eErrUser {ERR_Unknown, ERR_FileAcess, ERR_PointerInvalid, ERR_NoMoreInstance}; 012. //+------------------------------------------------------------------+ 013. struct st_Terminal 014. { 015. ENUM_SYMBOL_CHART_MODE ChartMode; 016. ENUM_ACCOUNT_MARGIN_MODE TypeAccount; 017. long ID; 018. string szSymbol; 019. int Width, 020. Height, 021. nDigits, 022. SubWin, 023. HeightBar; 024. double PointPerTick, 025. ValuePerPoint, 026. VolumeMinimal, 027. AdjustToTrade; 028. }; 029. //+------------------------------------------------------------------+ 030. void CurrentSymbol(bool bUsingFull) 031. { 032. MqlDateTime mdt1; 033. string sz0, sz1; 034. datetime dt = macroGetDate(TimeCurrent(mdt1)); 035. enum eTypeSymbol {WIN, IND, WDO, DOL, OTHER} eTS = OTHER; 036. 037. sz0 = StringSubstr(m_Infos.szSymbol = _Symbol, 0, 3); 038. for (eTypeSymbol c0 = 0; (c0 < OTHER) && (eTS == OTHER); c0++) eTS = (EnumToString(c0) == sz0 ? c0 : eTS); 039. switch (eTS) 040. { 041. case DOL : 042. case WDO : sz1 = "FGHJKMNQUVXZ"; break; 043. case IND : 044. case WIN : sz1 = "GJMQVZ"; break; 045. default : return; 046. } 047. sz0 = EnumToString((eTypeSymbol)(((eTS & 1) == 1) ? (bUsingFull ? eTS : eTS - 1) : (bUsingFull ? eTS + 1: eTS))); 048. for (int i0 = 0, i1 = mdt1.year - 2000, imax = StringLen(sz1);; i0 = ((++i0) < imax ? i0 : 0), i1 += (i0 == 0 ? 1 : 0)) 049. if (dt < macroGetDate(SymbolInfoInteger(m_Infos.szSymbol = StringFormat("%s%s%d", sz0, StringSubstr(sz1, i0, 1), i1), SYMBOL_EXPIRATION_TIME))) break; 050. } 051. //+------------------------------------------------------------------+ 052. private : 053. st_Terminal m_Infos; 054. struct mem 055. { 056. long Show_Descr, 057. Show_Date; 058. bool AccountLock; 059. }m_Mem; 060. //+------------------------------------------------------------------+ 061. inline void ChartChange(void) 062. { 063. int x, y, t; 064. m_Infos.Width = (int)ChartGetInteger(m_Infos.ID, CHART_WIDTH_IN_PIXELS); 065. m_Infos.Height = (int)ChartGetInteger(m_Infos.ID, CHART_HEIGHT_IN_PIXELS); 066. ChartTimePriceToXY(m_Infos.ID, 0, 0, 0, x, t); 067. ChartTimePriceToXY(m_Infos.ID, 0, 0, m_Infos.PointPerTick * 100, x, y); 068. m_Infos.HeightBar = (int)((t - y) / 100); 069. } 070. //+------------------------------------------------------------------+ 071. public : 072. //+------------------------------------------------------------------+ 073. C_Terminal(const long id = 0, const uchar sub = 0) 074. { 075. m_Infos.ID = (id == 0 ? ChartID() : id); 076. m_Mem.AccountLock = false; 077. m_Infos.SubWin = (int) sub; 078. CurrentSymbol(false); 079. m_Mem.Show_Descr = ChartGetInteger(m_Infos.ID, CHART_SHOW_OBJECT_DESCR); 080. m_Mem.Show_Date = ChartGetInteger(m_Infos.ID, CHART_SHOW_DATE_SCALE); 081. ChartSetInteger(m_Infos.ID, CHART_SHOW_OBJECT_DESCR, false); 082. ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_DELETE, true); 083. ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_CREATE, true); 084. ChartSetInteger(m_Infos.ID, CHART_SHOW_DATE_SCALE, false); 085. m_Infos.nDigits = (int) SymbolInfoInteger(m_Infos.szSymbol, SYMBOL_DIGITS); 086. m_Infos.Width = (int)ChartGetInteger(m_Infos.ID, CHART_WIDTH_IN_PIXELS); 087. m_Infos.Height = (int)ChartGetInteger(m_Infos.ID, CHART_HEIGHT_IN_PIXELS); 088. m_Infos.PointPerTick = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_TRADE_TICK_SIZE); 089. m_Infos.ValuePerPoint = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_TRADE_TICK_VALUE); 090. m_Infos.VolumeMinimal = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_VOLUME_STEP); 091. m_Infos.AdjustToTrade = m_Infos.ValuePerPoint / m_Infos.PointPerTick; 092. m_Infos.ChartMode = (ENUM_SYMBOL_CHART_MODE) SymbolInfoInteger(m_Infos.szSymbol, SYMBOL_CHART_MODE); 093. if(m_Infos.szSymbol != def_SymbolReplay) SetTypeAccount((ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE)); 094. ChartChange(); 095. } 096. //+------------------------------------------------------------------+ 097. ~C_Terminal() 098. { 099. ChartSetInteger(m_Infos.ID, CHART_SHOW_DATE_SCALE, m_Mem.Show_Date); 100. ChartSetInteger(m_Infos.ID, CHART_SHOW_OBJECT_DESCR, m_Mem.Show_Descr); 101. ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_DELETE, false); 102. ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_CREATE, false); 103. } 104. //+------------------------------------------------------------------+ 105. inline void SetTypeAccount(const ENUM_ACCOUNT_MARGIN_MODE arg) 106. { 107. if (m_Mem.AccountLock) return; else m_Mem.AccountLock = true; 108. m_Infos.TypeAccount = (arg == ACCOUNT_MARGIN_MODE_RETAIL_HEDGING ? arg : ACCOUNT_MARGIN_MODE_RETAIL_NETTING); 109. } 110. //+------------------------------------------------------------------+ 111. inline const st_Terminal GetInfoTerminal(void) const 112. { 113. return m_Infos; 114. } 115. //+------------------------------------------------------------------+ 116. const double AdjustPrice(const double arg) const 117. { 118. return NormalizeDouble(round(arg / m_Infos.PointPerTick) * m_Infos.PointPerTick, m_Infos.nDigits); 119. } 120. //+------------------------------------------------------------------+ 121. inline datetime AdjustTime(const datetime arg) 122. { 123. int nSeconds= PeriodSeconds(); 124. datetime dt = iTime(m_Infos.szSymbol, PERIOD_CURRENT, 0); 125. 126. return (dt < arg ? ((datetime)(arg / nSeconds) * nSeconds) : iTime(m_Infos.szSymbol, PERIOD_CURRENT, Bars(m_Infos.szSymbol, PERIOD_CURRENT, arg, dt))); 127. } 128. //+------------------------------------------------------------------+ 129. inline double FinanceToPoints(const double Finance, const uint Leverage) 130. { 131. double volume = m_Infos.VolumeMinimal + (m_Infos.VolumeMinimal * (Leverage - 1)); 132. 133. return AdjustPrice(MathAbs(((Finance / volume) / m_Infos.AdjustToTrade))); 134. }; 135. //+------------------------------------------------------------------+ 136. void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam) 137. { 138. static string st_str = ""; 139. 140. switch (id) 141. { 142. case CHARTEVENT_CHART_CHANGE: 143. m_Infos.Width = (int)ChartGetInteger(m_Infos.ID, CHART_WIDTH_IN_PIXELS); 144. m_Infos.Height = (int)ChartGetInteger(m_Infos.ID, CHART_HEIGHT_IN_PIXELS); 145. ChartChange(); 146. break; 147. case CHARTEVENT_OBJECT_CLICK: 148. if (st_str != sparam) ObjectSetInteger(m_Infos.ID, st_str, OBJPROP_SELECTED, false); 149. if (ObjectGetInteger(m_Infos.ID, sparam, OBJPROP_SELECTABLE) == true) 150. ObjectSetInteger(m_Infos.ID, st_str = sparam, OBJPROP_SELECTED, true); 151. break; 152. case CHARTEVENT_OBJECT_CREATE: 153. if (st_str != sparam) ObjectSetInteger(m_Infos.ID, st_str, OBJPROP_SELECTED, false); 154. st_str = sparam; 155. break; 156. } 157. } 158. //+------------------------------------------------------------------+ 159. inline void CreateObjectGraphics(const string szName, const ENUM_OBJECT obj, const color cor = clrNONE, const int zOrder = -1) const 160. { 161. ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_CREATE, 0, false); 162. ObjectCreate(m_Infos.ID, szName, obj, m_Infos.SubWin, 0, 0); 163. ObjectSetString(m_Infos.ID, szName, OBJPROP_TOOLTIP, "\n"); 164. ObjectSetInteger(m_Infos.ID, szName, OBJPROP_BACK, false); 165. ObjectSetInteger(m_Infos.ID, szName, OBJPROP_COLOR, cor); 166. ObjectSetInteger(m_Infos.ID, szName, OBJPROP_SELECTABLE, false); 167. ObjectSetInteger(m_Infos.ID, szName, OBJPROP_SELECTED, false); 168. ObjectSetInteger(m_Infos.ID, szName, OBJPROP_ZORDER, zOrder); 169. ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_CREATE, 0, true); 170. } 171. //+------------------------------------------------------------------+ 172. bool IndicatorCheckPass(const string szShortName) 173. { 174. string szTmp = szShortName + "_TMP"; 175. 176. IndicatorSetString(INDICATOR_SHORTNAME, szTmp); 177. m_Infos.SubWin = ((m_Infos.SubWin = ChartWindowFind(m_Infos.ID, szTmp)) < 0 ? 0 : m_Infos.SubWin); 178. if (ChartIndicatorGet(m_Infos.ID, m_Infos.SubWin, szShortName) != INVALID_HANDLE) 179. { 180. ChartIndicatorDelete(m_Infos.ID, 0, szTmp); 181. Print("Only one instance is allowed..."); 182. SetUserError(C_Terminal::ERR_NoMoreInstance); 183. 184. return false; 185. } 186. IndicatorSetString(INDICATOR_SHORTNAME, szShortName); 187. 188. return true; 189. } 190. //+------------------------------------------------------------------+ 191. };
C_Terminal.mqh 源代码
请注意,变化很小。事实上,与上一篇文章中的代码相比,我们只开放了在从 C_Terminal 类继承的其他类中调用 CurrentSymbol 过程的可能性。以前,这个过程对于类来说是私有的,但现在我已将其更改为受保护的。许多人可能会直接改为公有,但我不想做出如此彻底的改变。我喜欢授予必要的最低权限,直到程序显示出真正需要更多访问权限。作为一种受保护的过程,它不能随意访问。
除了这一小变化外,还进行了另一项修改。现在,在构造函数中,请注意,在第 73 行,我删除了上一篇文章中添加的参数。然而,在第 78 行,我强制首次使用迷你合约。这种调整为进一步的修改开创了先例。这样做的原因是,如果我们维持旧结构,我们将被迫关闭 Chart Trade 来更新指标中显示的合约。现在,这不再是必要的了。我们可以允许通过消息来切换合约。然而,由于这一变化,上一篇文章中的其他修改仍然与 Chart Trade 代码相关,但我们现在不会关注它们。必须先解决其他问题。
说到消息,我们需要实施新的消息来涵盖这些问题的解决方案。但在查看新消息之前,让我们先暂停一下并思考一下。请看下图:

在这里,我们看到了消息交换实际发生的点。请注意一件事:在 EA 交易的情况下,有两个点会向 Chart Trade 发送消息。对于 Chart Trade 来说,只有一个点。
为了理解这种交换将如何发生,重要的是要认识到,当向图表中添加某些内容时,在 OnInit 之后,OnChartEvent 会被执行。但是,您知道当对象放置在图表上时,哪个事件会触发 OnChartEvent 吗?如果您进行调查,您会发现,当任何对象(指标或 EA 交易)被添加到图表时,MetaTrader 5 都会触发 CHARTEVENT_CHART_CHANGE 事件。理解这一点至关重要,因为我们将使用此事件使系统正常运行。
在继续之前,请考虑一下:如果执行订单的 EA 交易甚至可能不在图表上,为什么我们需要按钮来发送订单?这完全不合理。为了证明消息交换确实发生了,并且 Chart Trade 中的信息可以而且应该被用户或操作员使用,我们将修改 Chart Trade 中一个小但重要的细节。有了这个,我们现在就有了实施问题解决方案所需的概念基础。
实施解决方案
第一步是向我们的系统添加三个新事件。这些可以在 Defines.mqh 文件中看到,详细介绍如下:
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. #define def_MaskTimeService 0xFED00000 16. #define def_IndicatorTimeFrame (_Period < 60 ? _Period : (_Period < PERIOD_D1 ? _Period - 16325 : (_Period == PERIOD_D1 ? 84 : (_Period == PERIOD_W1 ? 91 : 96)))) 17. #define def_IndexTimeFrame 4 18. //+------------------------------------------------------------------+ 19. union uCast_Double 20. { 21. double dValue; 22. long _long; // 1 Information 23. datetime _datetime; // 1 Information 24. uint _32b[sizeof(double) / sizeof(uint)]; // 2 Informations 25. ushort _16b[sizeof(double) / sizeof(ushort)]; // 4 Informations 26. uchar _8b [sizeof(double) / sizeof(uchar)]; // 8 Informations 27. }; 28. //+------------------------------------------------------------------+ 29. enum EnumEvents { 30. evHideMouse, //Hide mouse price line 31. evShowMouse, //Show mouse price line 32. evHideBarTime, //Hide bar time 33. evShowBarTime, //Show bar time 34. evHideDailyVar, //Hide daily variation 35. evShowDailyVar, //Show daily variation 36. evHidePriceVar, //Hide instantaneous variation 37. evShowPriceVar, //Show instantaneous variation 38. evCtrlReplayInit, //Initialize replay control 39. evChartTradeBuy, //Market buy event 40. evChartTradeSell, //Market sales event 41. evChartTradeCloseAll, //Event to close positions 42. evChartTrade_At_EA, //Event to communication 43. evEA_At_ChartTrade //Event to communication 44. }; 45. //+------------------------------------------------------------------+
Defines.mqh 文件源代码
添加的是第 42 行和第 43 行。这些话几乎是不言自明的,因为关键在于通信的方向。第 42 行涉及从 Chart Trade 到 EA 交易的通信。重要提示:不要将此与交易事件混淆。此事件旨在用于不同类型的通信,本质上是一个专用的通信渠道。
第 43 行指定了事件的名称,该事件作为对 Chart Trade 向 EA 交易发起的请求的响应。目前,就这些了 —— 只有这两行。但它们的影响将是巨大的。接下来,让我们检查一下 Chart Trade 指标的完整代码。如下图所示。请注意,它与上一篇文章中的版本略有不同。具体来说,用户或交易者之前可以调整的参数不再存在。
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. #property description "Chart Trade Base Indicator." 04. #property description "See the articles for more details." 05. #property version "1.81" 06. #property icon "/Images/Market Replay/Icons/Indicators.ico" 07. #property link "https://www.mql5.com/en/articles/12537" 08. #property indicator_chart_window 09. #property indicator_plots 0 10. //+------------------------------------------------------------------+ 11. #include <Market Replay\Chart Trader\C_ChartFloatingRAD.mqh> 12. //+------------------------------------------------------------------+ 13. #define def_ShortName "Indicator Chart Trade" 14. //+------------------------------------------------------------------+ 15. C_ChartFloatingRAD *chart = NULL; 16. //+------------------------------------------------------------------+ 17. input ushort user01 = 1; //Leverage 18. input double user02 = 100.1; //Finance Take 19. input double user03 = 75.4; //Finance Stop 20. //+------------------------------------------------------------------+ 21. int OnInit() 22. { 23. chart = new C_ChartFloatingRAD(def_ShortName, new C_Mouse(0, "Indicator Mouse Study"), user01, user02, user03); 24. 25. if (_LastError >= ERR_USER_ERROR_FIRST) return INIT_FAILED; 26. 27. return INIT_SUCCEEDED; 28. } 29. //+------------------------------------------------------------------+ 30. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 31. { 32. return rates_total; 33. } 34. //+------------------------------------------------------------------+ 35. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 36. { 37. if (_LastError < ERR_USER_ERROR_FIRST) 38. (*chart).DispatchMessage(id, lparam, dparam, sparam); 39. } 40. //+------------------------------------------------------------------+ 41. void OnDeinit(const int reason) 42. { 43. switch (reason) 44. { 45. case REASON_INITFAILED: 46. ChartIndicatorDelete(ChartID(), 0, def_ShortName); 47. break; 48. case REASON_CHARTCHANGE: 49. (*chart).SaveState(); 50. break; 51. } 52. 53. delete chart; 54. } 55. //+------------------------------------------------------------------+
Chart Trade 指标源代码
但是新事件在哪里?人们期望在 OnChartEvent 过程中看到它们。事实上,它们就在那里,但为了简化起见,一切都在一个地方考虑,即在 DispatchMessage 过程中。此过程位于 C_ChartFloatingRAD 类中,完整内容可在下面看到。
001. //+------------------------------------------------------------------+ 002. #property copyright "Daniel Jose" 003. //+------------------------------------------------------------------+ 004. #include "../Auxiliar/C_Mouse.mqh" 005. #include "C_AdjustTemplate.mqh" 006. //+------------------------------------------------------------------+ 007. #define macro_NameGlobalVariable(A) StringFormat("ChartTrade_%u%s", GetInfoTerminal().ID, A) 008. #define macro_CloseIndicator(A) { \ 009. OnDeinit(REASON_INITFAILED); \ 010. SetUserError(A); \ 011. return; \ 012. } 013. //+------------------------------------------------------------------+ 014. class C_ChartFloatingRAD : private C_Terminal 015. { 016. private : 017. enum eObjectsIDE {MSG_LEVERAGE_VALUE, MSG_TAKE_VALUE, MSG_STOP_VALUE, MSG_MAX_MIN, MSG_TITLE_IDE, MSG_DAY_TRADE, MSG_BUY_MARKET, MSG_SELL_MARKET, MSG_CLOSE_POSITION, MSG_NULL}; 018. struct st00 019. { 020. short x, y, minx, miny, 021. Leverage; 022. string szObj_Chart, 023. szObj_Editable, 024. szFileNameTemplate; 025. long WinHandle; 026. double FinanceTake, 027. FinanceStop; 028. bool IsMaximized, 029. IsDayTrade, 030. IsSaveState; 031. struct st01 032. { 033. short x, y, w, h; 034. color bgcolor; 035. int FontSize; 036. string FontName; 037. }Regions[MSG_NULL]; 038. }m_Info; 039. struct st01 040. { 041. short y[2]; 042. bool bOk; 043. }m_Init; 044. C_Mouse *m_Mouse; 045. //+------------------------------------------------------------------+ 046. void CreateWindowRAD(int w, int h) 047. { 048. m_Info.szObj_Chart = "Chart Trade IDE"; 049. m_Info.szObj_Editable = m_Info.szObj_Chart + " > Edit"; 050. ObjectCreate(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJ_CHART, 0, 0, 0); 051. ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_XDISTANCE, m_Info.x); 052. ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YDISTANCE, m_Info.y); 053. ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_XSIZE, w); 054. ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YSIZE, h); 055. ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_DATE_SCALE, false); 056. ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_PRICE_SCALE, false); 057. m_Info.WinHandle = ObjectGetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_CHART_ID); 058. }; 059. //+------------------------------------------------------------------+ 060. void AdjustEditabled(C_AdjustTemplate &Template, bool bArg) 061. { 062. for (eObjectsIDE c0 = MSG_LEVERAGE_VALUE; c0 <= MSG_STOP_VALUE; c0++) 063. if (bArg) 064. { 065. Template.Add(EnumToString(c0), "bgcolor", NULL); 066. Template.Add(EnumToString(c0), "fontsz", NULL); 067. Template.Add(EnumToString(c0), "fontnm", NULL); 068. } 069. else 070. { 071. m_Info.Regions[c0].bgcolor = (color) StringToInteger(Template.Get(EnumToString(c0), "bgcolor")); 072. m_Info.Regions[c0].FontSize = (int) StringToInteger(Template.Get(EnumToString(c0), "fontsz")); 073. m_Info.Regions[c0].FontName = Template.Get(EnumToString(c0), "fontnm"); 074. } 075. } 076. //+------------------------------------------------------------------+ 077. inline void AdjustTemplate(const bool bFirst = false) 078. { 079. #define macro_PointsToFinance(A) A * (GetInfoTerminal().VolumeMinimal + (GetInfoTerminal().VolumeMinimal * (m_Info.Leverage - 1))) * GetInfoTerminal().AdjustToTrade 080. 081. C_AdjustTemplate *Template; 082. 083. if (bFirst) 084. { 085. Template = new C_AdjustTemplate(m_Info.szFileNameTemplate = IntegerToString(GetInfoTerminal().ID) + ".tpl", true); 086. for (eObjectsIDE c0 = MSG_LEVERAGE_VALUE; c0 <= MSG_CLOSE_POSITION; c0++) 087. { 088. (*Template).Add(EnumToString(c0), "size_x", NULL); 089. (*Template).Add(EnumToString(c0), "size_y", NULL); 090. (*Template).Add(EnumToString(c0), "pos_x", NULL); 091. (*Template).Add(EnumToString(c0), "pos_y", NULL); 092. } 093. AdjustEditabled(Template, true); 094. }else Template = new C_AdjustTemplate(m_Info.szFileNameTemplate); 095. if (_LastError >= ERR_USER_ERROR_FIRST) 096. { 097. delete Template; 098. 099. return; 100. } 101. m_Info.Leverage = (m_Info.Leverage <= 0 ? 1 : m_Info.Leverage); 102. m_Info.FinanceTake = macro_PointsToFinance(FinanceToPoints(MathAbs(m_Info.FinanceTake), m_Info.Leverage)); 103. m_Info.FinanceStop = macro_PointsToFinance(FinanceToPoints(MathAbs(m_Info.FinanceStop), m_Info.Leverage)); 104. (*Template).Add("MSG_NAME_SYMBOL", "descr", GetInfoTerminal().szSymbol); 105. (*Template).Add("MSG_LEVERAGE_VALUE", "descr", IntegerToString(m_Info.Leverage)); 106. (*Template).Add("MSG_TAKE_VALUE", "descr", DoubleToString(m_Info.FinanceTake, 2)); 107. (*Template).Add("MSG_STOP_VALUE", "descr", DoubleToString(m_Info.FinanceStop, 2)); 108. (*Template).Add("MSG_DAY_TRADE", "state", (m_Info.IsDayTrade ? "1" : "0")); 109. (*Template).Add("MSG_MAX_MIN", "state", (m_Info.IsMaximized ? "1" : "0")); 110. if (!(*Template).Execute()) 111. { 112. delete Template; 113. 114. macro_CloseIndicator(C_Terminal::ERR_FileAcess); 115. }; 116. if (bFirst) 117. { 118. for (eObjectsIDE c0 = MSG_LEVERAGE_VALUE; c0 <= MSG_CLOSE_POSITION; c0++) 119. { 120. m_Info.Regions[c0].x = (short) StringToInteger((*Template).Get(EnumToString(c0), "pos_x")); 121. m_Info.Regions[c0].y = (short) StringToInteger((*Template).Get(EnumToString(c0), "pos_y")); 122. m_Info.Regions[c0].w = (short) StringToInteger((*Template).Get(EnumToString(c0), "size_x")); 123. m_Info.Regions[c0].h = (short) StringToInteger((*Template).Get(EnumToString(c0), "size_y")); 124. } 125. m_Info.Regions[MSG_TITLE_IDE].w = m_Info.Regions[MSG_MAX_MIN].x; 126. AdjustEditabled(Template, false); 127. }; 128. ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YSIZE, (m_Info.IsMaximized ? m_Init.y[m_Init.bOk] : m_Info.Regions[MSG_TITLE_IDE].h + 6)); 129. ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_XDISTANCE, (m_Info.IsMaximized ? m_Info.x : m_Info.minx)); 130. ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YDISTANCE, (m_Info.IsMaximized ? m_Info.y : m_Info.miny)); 131. 132. delete Template; 133. 134. ChartApplyTemplate(m_Info.WinHandle, "/Files/" + m_Info.szFileNameTemplate); 135. ChartRedraw(m_Info.WinHandle); 136. 137. #undef macro_PointsToFinance 138. } 139. //+------------------------------------------------------------------+ 140. eObjectsIDE CheckMousePosition(const short x, const short y) 141. { 142. int xi, yi, xf, yf; 143. 144. for (eObjectsIDE c0 = MSG_LEVERAGE_VALUE; c0 <= MSG_CLOSE_POSITION; c0++) 145. { 146. xi = (m_Info.IsMaximized ? m_Info.x : m_Info.minx) + m_Info.Regions[c0].x; 147. yi = (m_Info.IsMaximized ? m_Info.y : m_Info.miny) + m_Info.Regions[c0].y; 148. xf = xi + m_Info.Regions[c0].w; 149. yf = yi + m_Info.Regions[c0].h; 150. if ((x > xi) && (y > yi) && (x < xf) && (y < yf)) return c0; 151. } 152. return MSG_NULL; 153. } 154. //+------------------------------------------------------------------+ 155. inline void DeleteObjectEdit(void) 156. { 157. ChartRedraw(); 158. ObjectsDeleteAll(GetInfoTerminal().ID, m_Info.szObj_Editable); 159. } 160. //+------------------------------------------------------------------+ 161. template <typename T > 162. void CreateObjectEditable(eObjectsIDE arg, T value) 163. { 164. long id = GetInfoTerminal().ID; 165. 166. DeleteObjectEdit(); 167. CreateObjectGraphics(m_Info.szObj_Editable, OBJ_EDIT, clrBlack, 0); 168. ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_XDISTANCE, m_Info.Regions[arg].x + m_Info.x + 3); 169. ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_YDISTANCE, m_Info.Regions[arg].y + m_Info.y + 3); 170. ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_XSIZE, m_Info.Regions[arg].w); 171. ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_YSIZE, m_Info.Regions[arg].h); 172. ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_BGCOLOR, m_Info.Regions[arg].bgcolor); 173. ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_ALIGN, ALIGN_CENTER); 174. ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_FONTSIZE, m_Info.Regions[arg].FontSize - 1); 175. ObjectSetString(id, m_Info.szObj_Editable, OBJPROP_FONT, m_Info.Regions[arg].FontName); 176. ObjectSetString(id, m_Info.szObj_Editable, OBJPROP_TEXT, (typename(T) == "double" ? DoubleToString(value, 2) : (string) value)); 177. ChartRedraw(); 178. } 179. //+------------------------------------------------------------------+ 180. bool RestoreState(void) 181. { 182. uCast_Double info; 183. bool bRet; 184. C_AdjustTemplate *Template; 185. 186. if (bRet = GlobalVariableGet(macro_NameGlobalVariable("POST"), info.dValue)) 187. { 188. m_Info.x = (short) info._16b[0]; 189. m_Info.y = (short) info._16b[1]; 190. m_Info.minx = (short) info._16b[2]; 191. m_Info.miny = (short) info._16b[3]; 192. Template = new C_AdjustTemplate(m_Info.szFileNameTemplate = IntegerToString(GetInfoTerminal().ID) + ".tpl"); 193. if (_LastError >= ERR_USER_ERROR_FIRST) bRet = false; else 194. { 195. (*Template).Add("MSG_LEVERAGE_VALUE", "descr", NULL); 196. (*Template).Add("MSG_TAKE_VALUE", "descr", NULL); 197. (*Template).Add("MSG_STOP_VALUE", "descr", NULL); 198. (*Template).Add("MSG_DAY_TRADE", "state", NULL); 199. (*Template).Add("MSG_MAX_MIN", "state", NULL); 200. if (!(*Template).Execute()) bRet = false; else 201. { 202. m_Info.IsDayTrade = (bool) StringToInteger((*Template).Get("MSG_DAY_TRADE", "state")) == 1; 203. m_Info.IsMaximized = (bool) StringToInteger((*Template).Get("MSG_MAX_MIN", "state")) == 1; 204. m_Info.Leverage = (short)StringToInteger((*Template).Get("MSG_LEVERAGE_VALUE", "descr")); 205. m_Info.FinanceTake = (double) StringToDouble((*Template).Get("MSG_TAKE_VALUE", "descr")); 206. m_Info.FinanceStop = (double) StringToDouble((*Template).Get("MSG_STOP_VALUE", "descr")); 207. } 208. }; 209. delete Template; 210. }; 211. 212. GlobalVariablesDeleteAll(macro_NameGlobalVariable("")); 213. 214. return bRet; 215. } 216. //+------------------------------------------------------------------+ 217. public : 218. //+------------------------------------------------------------------+ 219. C_ChartFloatingRAD(string szShortName, C_Mouse *MousePtr, const short Leverage, const double FinanceTake, const double FinanceStop) 220. :C_Terminal(0) 221. { 222. m_Mouse = MousePtr; 223. m_Info.IsSaveState = false; 224. if (!IndicatorCheckPass(szShortName)) return; 225. if (!RestoreState()) 226. { 227. m_Info.Leverage = Leverage; 228. m_Info.IsDayTrade = true; 229. m_Info.FinanceTake = FinanceTake; 230. m_Info.FinanceStop = FinanceStop; 231. m_Info.IsMaximized = true; 232. m_Info.minx = m_Info.x = 115; 233. m_Info.miny = m_Info.y = 64; 234. } 235. m_Init.y[false] = 150; 236. m_Init.y[true] = 210; 237. CreateWindowRAD(170, m_Init.y[m_Init.bOk = false]); 238. AdjustTemplate(true); 239. } 240. //+------------------------------------------------------------------+ 241. ~C_ChartFloatingRAD() 242. { 243. ChartRedraw(); 244. ObjectsDeleteAll(GetInfoTerminal().ID, m_Info.szObj_Chart); 245. if (!m_Info.IsSaveState) 246. FileDelete(m_Info.szFileNameTemplate); 247. 248. delete m_Mouse; 249. } 250. //+------------------------------------------------------------------+ 251. void SaveState(void) 252. { 253. #define macro_GlobalVariable(A, B) if (GlobalVariableTemp(A)) GlobalVariableSet(A, B); 254. 255. uCast_Double info; 256. 257. info._16b[0] = m_Info.x; 258. info._16b[1] = m_Info.y; 259. info._16b[2] = m_Info.minx; 260. info._16b[3] = m_Info.miny; 261. macro_GlobalVariable(macro_NameGlobalVariable("POST"), info.dValue); 262. m_Info.IsSaveState = true; 263. 264. #undef macro_GlobalVariable 265. } 266. //+------------------------------------------------------------------+ 267. void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam) 268. { 269. #define macro_AdjustMinX(A, B) { \ 270. B = (A + m_Info.Regions[MSG_TITLE_IDE].w) > x; \ 271. mx = x - m_Info.Regions[MSG_TITLE_IDE].w; \ 272. A = (B ? (mx > 0 ? mx : 0) : A); \ 273. } 274. #define macro_AdjustMinY(A, B) { \ 275. B = (A + m_Info.Regions[MSG_TITLE_IDE].h) > y; \ 276. my = y - m_Info.Regions[MSG_TITLE_IDE].h; \ 277. A = (B ? (my > 0 ? my : 0) : A); \ 278. } 279. 280. static short sx = -1, sy = -1, sz = -1; 281. static eObjectsIDE obj = MSG_NULL; 282. short x, y, mx, my; 283. double dvalue; 284. bool b1, b2, b3, b4; 285. ushort ev = evChartTradeCloseAll; 286. 287. switch (id) 288. { 289. case CHARTEVENT_CUSTOM + evEA_At_ChartTrade: 290. if (m_Init.bOk = ((lparam >= 0) && (lparam < 2))) 291. CurrentSymbol((bool)lparam); 292. AdjustTemplate(true); 293. break; 294. case CHARTEVENT_CHART_CHANGE: 295. if (!m_Init.bOk) 296. EventChartCustom(GetInfoTerminal().ID, evChartTrade_At_EA, 0, 0, ""); 297. x = (short)ChartGetInteger(GetInfoTerminal().ID, CHART_WIDTH_IN_PIXELS); 298. y = (short)ChartGetInteger(GetInfoTerminal().ID, CHART_HEIGHT_IN_PIXELS); 299. macro_AdjustMinX(m_Info.x, b1); 300. macro_AdjustMinY(m_Info.y, b2); 301. macro_AdjustMinX(m_Info.minx, b3); 302. macro_AdjustMinY(m_Info.miny, b4); 303. if (b1 || b2 || b3 || b4) AdjustTemplate(); 304. break; 305. case CHARTEVENT_MOUSE_MOVE: 306. if ((*m_Mouse).CheckClick(C_Mouse::eClickLeft)) 307. { 308. switch (CheckMousePosition(x = (short)lparam, y = (short)dparam)) 309. { 310. case MSG_MAX_MIN: 311. if (sz < 0) m_Info.IsMaximized = (m_Info.IsMaximized ? false : true); 312. break; 313. case MSG_DAY_TRADE: 314. if ((m_Info.IsMaximized) && (sz < 0)) m_Info.IsDayTrade = (m_Info.IsDayTrade ? false : true); 315. break; 316. case MSG_LEVERAGE_VALUE: 317. if ((m_Info.IsMaximized) && (sz < 0)) CreateObjectEditable(obj = MSG_LEVERAGE_VALUE, m_Info.Leverage); 318. break; 319. case MSG_TAKE_VALUE: 320. if ((m_Info.IsMaximized) && (sz < 0)) CreateObjectEditable(obj = MSG_TAKE_VALUE, m_Info.FinanceTake); 321. break; 322. case MSG_STOP_VALUE: 323. if ((m_Info.IsMaximized) && (sz < 0)) CreateObjectEditable(obj = MSG_STOP_VALUE, m_Info.FinanceStop); 324. break; 325. case MSG_TITLE_IDE: 326. if (sx < 0) 327. { 328. ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, false); 329. sx = x - (m_Info.IsMaximized ? m_Info.x : m_Info.minx); 330. sy = y - (m_Info.IsMaximized ? m_Info.y : m_Info.miny); 331. } 332. if ((mx = x - sx) > 0) ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_XDISTANCE, mx); 333. if ((my = y - sy) > 0) ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YDISTANCE, my); 334. if (m_Info.IsMaximized) 335. { 336. m_Info.x = (mx > 0 ? mx : m_Info.x); 337. m_Info.y = (my > 0 ? my : m_Info.y); 338. }else 339. { 340. m_Info.minx = (mx > 0 ? mx : m_Info.minx); 341. m_Info.miny = (my > 0 ? my : m_Info.miny); 342. } 343. break; 344. case MSG_BUY_MARKET: 345. ev = evChartTradeBuy; 346. case MSG_SELL_MARKET: 347. ev = (ev != evChartTradeBuy ? evChartTradeSell : ev); 348. case MSG_CLOSE_POSITION: 349. if ((m_Info.IsMaximized) && (sz < 0) && (m_Init.bOk)) //<< 350. { 351. string szTmp = StringFormat("%d?%s?%s?%c?%d?%.2f?%.2f", ev, _Symbol, GetInfoTerminal().szSymbol, (m_Info.IsDayTrade ? 'D' : 'S'), 352. m_Info.Leverage, FinanceToPoints(m_Info.FinanceTake, m_Info.Leverage), FinanceToPoints(m_Info.FinanceStop, m_Info.Leverage)); 353. PrintFormat("Send %s - Args ( %s )", EnumToString((EnumEvents) ev), szTmp); 354. EventChartCustom(GetInfoTerminal().ID, ev, 0, 0, szTmp); 355. } 356. break; 357. } 358. if (sz < 0) 359. { 360. sz = x; 361. AdjustTemplate(); 362. if (obj == MSG_NULL) DeleteObjectEdit(); 363. } 364. }else 365. { 366. sz = -1; 367. if (sx > 0) 368. { 369. ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, true); 370. sx = sy = -1; 371. } 372. } 373. break; 374. case CHARTEVENT_OBJECT_ENDEDIT: 375. switch (obj) 376. { 377. case MSG_LEVERAGE_VALUE: 378. case MSG_TAKE_VALUE: 379. case MSG_STOP_VALUE: 380. dvalue = StringToDouble(ObjectGetString(GetInfoTerminal().ID, m_Info.szObj_Editable, OBJPROP_TEXT)); 381. if (obj == MSG_TAKE_VALUE) 382. m_Info.FinanceTake = (dvalue <= 0 ? m_Info.FinanceTake : dvalue); 383. else if (obj == MSG_STOP_VALUE) 384. m_Info.FinanceStop = (dvalue <= 0 ? m_Info.FinanceStop : dvalue); 385. else 386. m_Info.Leverage = (dvalue <= 0 ? m_Info.Leverage : (short)MathFloor(dvalue)); 387. AdjustTemplate(); 388. obj = MSG_NULL; 389. ObjectDelete(GetInfoTerminal().ID, m_Info.szObj_Editable); 390. break; 391. } 392. break; 393. case CHARTEVENT_OBJECT_DELETE: 394. if (sparam == m_Info.szObj_Chart) macro_CloseIndicator(C_Terminal::ERR_Unknown); 395. break; 396. } 397. ChartRedraw(); 398. } 399. //+------------------------------------------------------------------+ 400. }; 401. //+------------------------------------------------------------------+ 402. #undef macro_NameGlobalVariable 403. #undef macro_CloseIndicator 404. //+------------------------------------------------------------------+
C_ChartFloatingRAD.mqh 文件的源代码
尽管头文件 C_ChartFloatingRAD.mqh 中的代码可能会让许多人感到不知所措,而且严格来说没有必要完整查看,但我决定将其全部包含在本文中。这是由于与上一篇文章相比所做的更改。然而,唯一显著的变化是类构造函数。由于行号发生了变化,我不想让读者在试图找到正确的行时感到困惑。因此,提供了完整的代码。
让我们关注一下实际发生了什么变化。不是与上一篇文章相比,而是为了支持本文末尾视频中的内容。第一个变化是第 39 行出现的一个小结构。请仔细听我的解释,否则您将无法理解视频中显示的内容。在这个结构中,有第 41 行。它包含两个表示窗口大小的值,实际上是一个 OBJ_CHART对象。别担心 —— 你很快就会明白这一点。第 42 行包含一个变量,该变量与这个双元素数组一起实现视频中看到的功能。
接下来,让我们查看类构造函数,从第 219 行开始。请注意,上一篇文章中的额外参数不再存在。第 220 行调用 C_Terminal 类的构造函数,返回到上一篇文章之前的状态。现在,请注意:Chart Trade 窗口是一个 OBJ_CHART 对象,可以最小化或最大化。此功能已经实现。然而,当 EA 交易不存在于 Chart Trade 所在的图表上时,我们希望隐藏按钮。
为了实现这一点,我们必须调整坐标系的 Y 维度。这些大小在第 235 行和第 236 行设置。密切注意:如果变量 m_Init.bOk 为 false,则表示某些东西未对齐。在这种情况下,交互按钮被隐藏,以防止用户或操作员错误地认为订单正在发送到服务器。对于要隐藏的按钮,Y 坐标设置为 150。当 m_Init.bOk 为 true 时,用户或操作员可以通过 EA 交易发送订单。因此,Y坐标设置为210。如第 237 行所示,此值 210 是之前使用的默认值。
请注意,创建 OBJ_CHART 对象以包含 Chart Trade 的第 237 行已发生改变。我们现在不再使用固定值 210 作为尺度,而是访问数组中定义的值。同时,m_Init.bOk 初始化为 false。本质上,OBJ_CHART 对象最初创建的 Y 坐标为 150。为什么不直接使用这个值呢?原因是为了测试并确认代码是否按预期工作,直接通过 150 并不能保证模型正常运行。这种方法可确保实现按预期运行。
不久之后调用的 AdjustTemplate 过程中出现了一个新细节,其代码从第 77 行开始。此过程中唯一的更改是第 128 行。原因如下:当 Chart Trade 最大化或最小化时,我们会改变 Y 坐标。为了确保 Chart Trade 与构造函数中建立的规则保持一致,我们必须相应地调整 Y 坐标。因此,即使您尝试最大化或最小化 Chart Trade 来访问按钮,它们也不会可见,直到满足显示条件。
这是比较容易的部分,变化很少而且简单。现在让我们检查一下消息传递功能,它也有一些小的修改。DispatchMessage 过程从第 267 行开始。在查看消息之前,请注意第 349 行,其中引入了一个小而重要的差异。为了简化鼠标检查函数,在此行添加了一个新的测试值。如果 m_Init.bOk 为 false,并且用户点击按钮出现的区域,则检查会阻止触发任何事件。这种简单程度使编程变得优雅。否则,许多开发人员会试图在其他地方实现此检查,使过程变得不必要地复杂。
现在,让我们回到消息系统。当指标添加到图表时,收到的第一个事件是 CHARTEVENT_CHART_CHANGE 。在第 295 行,我们检查变量是否为 false。如果是,则在第 296 行触发自定义事件。无论 EA 交易是否捕获了触发的事件或只是添加到图表中,都会发生另一个事件,该事件由 Chart Trade 在第 289 行捕获。
现在,有趣的部分开始了。当第 290 行测试的值大于或等于零时,将执行第 291 行。注意:如果值为零,则表示 false;如果非零,则表示 true。但是,大于 1 的值无效。因此,EA 交易应该只发送 0 或 1 的值,表明使用的是迷你合约还是完整合约。最后,第 292 行请求更新 Chart Trade。这听起来可能很复杂,尤其是因为可能会出现 0 或 1 以外的值。在这种情况下,按钮是隐藏的。这表明该 EA 交易已从图表中移除或发生了意外事件。
说到这儿,你可能会认为我太过谨慎了。Chart Trade 如何知道 EA 交易已被删除或发生了异常情况?为了理解这一点,我们需要检查 EA 交易本身。接下来将介绍这一点。
EA 的当前功能
用于演示目的的 EA 交易代码完整如下所示。
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. #property description "Virtual Test..." 04. #property description "Demo version between interaction" 05. #property description "of Chart Trade and Expert Advisor" 06. #property version "1.81" 07. #property link "https://www.mql5.com/en/articles/12537" 08. //+------------------------------------------------------------------+ 09. #include <Market Replay\Defines.mqh> 10. //+------------------------------------------------------------------+ 11. class C_Decode 12. { 13. private : 14. struct stInfoEvent 15. { 16. EnumEvents ev; 17. string szSymbol, 18. szContract; 19. bool IsDayTrade; 20. ushort Leverange; 21. double PointsTake, 22. PointsStop; 23. }info[1]; 24. public : 25. //+------------------------------------------------------------------+ 26. C_Decode() 27. { 28. info[0].szSymbol = _Symbol; 29. } 30. //+------------------------------------------------------------------+ 31. bool Decode(const int id, const string sparam) 32. { 33. string Res[]; 34. 35. if (StringSplit(sparam, '?', Res) != 7) return false; 36. stInfoEvent loc = {(EnumEvents) StringToInteger(Res[0]), Res[1], Res[2], (bool)(Res[3] == "D"), (ushort) StringToInteger(Res[4]), StringToDouble(Res[5]), StringToDouble(Res[6])}; 37. if ((id == loc.ev) && (loc.szSymbol == info[0].szSymbol)) info[0] = loc; 38. 39. ArrayPrint(info, 2); 40. 41. return true; 42. } 43. }*GL_Decode; 44. //+------------------------------------------------------------------+ 45. enum eTypeContract {MINI, FULL}; 46. //+------------------------------------------------------------------+ 47. input eTypeContract user00 = MINI; //Cross order in contract 48. //+------------------------------------------------------------------+ 49. bool bOk; 50. //+------------------------------------------------------------------+ 51. int OnInit() 52. { 53. bOk = false; 54. GL_Decode = new C_Decode; 55. 56. return INIT_SUCCEEDED; 57. } 58. //+------------------------------------------------------------------+ 59. void OnTick() {} 60. //+------------------------------------------------------------------+ 61. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 62. { 63. switch (id) 64. { 65. case CHARTEVENT_CUSTOM + evChartTradeBuy : 66. case CHARTEVENT_CUSTOM + evChartTradeSell : 67. case CHARTEVENT_CUSTOM + evChartTradeCloseAll: 68. GL_Decode.Decode(id - CHARTEVENT_CUSTOM, sparam); 69. break; 70. case CHARTEVENT_CHART_CHANGE: 71. if (bOk) break; 72. case CHARTEVENT_CUSTOM + evChartTrade_At_EA: 73. bOk = true; 74. EventChartCustom(ChartID(), evEA_At_ChartTrade, user00, 0, ""); 75. break; 76. } 77. } 78. //+------------------------------------------------------------------+ 79. void OnDeinit(const int reason) 80. { 81. switch (reason) 82. { 83. case REASON_REMOVE: 84. case REASON_INITFAILED: 85. EventChartCustom(ChartID(), evEA_At_ChartTrade, -1, 0, ""); 86. break; 87. } 88. delete GL_Decode; 89. } 90. //+------------------------------------------------------------------+
EA 源代码
现在的问题是:这段代码是如何运作的?在回答之前,让我们注意上一篇文章中发生的一个小变化,当时 Chart Trade 开始指示它正在显示哪个合约。如果您查看 Chart Trade 代码 - 更具体地说是 C_ChartFloatingRAD 类 - 您会看到在第 351 行添加了一条新信息。该信息涉及合约名称,并在 EA 交易代码的第 36 行解码。这是一个很小的细节,但值得一提。现在,让我们研究一下 EA 交易代码的工作原理。
请注意,允许用户或操作员选择合约类型的代码(之前位于 Chart Trade 中)现在位于 EA 交易的第 45 行和 47 行。然而,我们在该 EA 交易中的任何地方都无法访问 C_Terminal 类。为什么呢?因为我们还没有连接到服务器,该 EA 交易仍处于演示模式。无论如何,请观察第 49 行,该行声明了一个变量,用于防止任何图表变化不必要地触发 Chart Trade 事件。在第 53 行,该变量被初始化为 false。在第 73 行,它被设置为 true。并在第 71 行进行检查以避免发送不必要的消息。
请注意:与指标一样,EA 交易的第一个事件是 CHARTEVENT_CHART_CHANGE,在第 70 行声明。该变量为 false。因此,如果执行 Chart Trade 请求某事,就会发生相同的过程。这使我们进入第 72 行。结果,在第 74 行触发了一个事件,其值为 0 或 1。这就是为什么第 45 行的枚举必须与显示的顺序匹配。更改顺序将会在 Chart Trade 中产生不正确的结果。仅当您了解后果时才修改这些值,否则,Chart Trade 将显示错误的合约。
第二部分涉及 EA 交易向 Chart Trade 发出信号,表明它不再可用。这需要 Chart Trade 隐藏订单按钮。当执行第 79 行的例程时就会发生这种情况。正常情况下,MetaTrader 5 会触发 DeInit 事件来从图表中删除一个对象。变量 reason 包含本次调用的原因。在第 81 行,我们检查这个原因。如果符合指定的条件,则在第 85 行触发事件。请注意,事件类型后面的值为 -1。这告诉 Chart Trade EA 交易不再可用,导致订单控件自动隐藏。
其余代码与 Chart Trade 通信文章中解释的内容保持不变。因此,我认为这个阶段已经完成。
最后的探讨
尽管我已经展示了如何轻松实现通信来初始化 EA 交易和 Chart Trade 指标,但目前我还不确定本文中提供的代码是否会被实际使用。这是由于挂单系统,这将被证明是我们的“眼中钉”。到目前为止,用户或操作员只需更改一个系统参数,就可以使用迷你合约或完整合约向服务器发送订单。这部分相对简单。本系列前面的文章中概述了必要的步骤。然而,与简单的应用程序间消息传递不同,管理挂单信息会使流程变得非常复杂。
无论如何,到目前为止所描述的变化结果可以在视频中看到。此外,视频中使用的可执行文件附在下面,让您可以查看和准确理解已实现的内容。
本文由MetaQuotes Ltd译自葡萄牙语
原文地址: https://www.mql5.com/pt/articles/12537
注意: MetaQuotes Ltd.将保留所有关于这些材料的权利。全部或部分复制或者转载这些材料将被禁止。
本文由网站的一位用户撰写,反映了他们的个人观点。MetaQuotes Ltd 不对所提供信息的准确性负责,也不对因使用所述解决方案、策略或建议而产生的任何后果负责。
卡尔曼滤波器在外汇均值回归策略中的应用
利用 Python 实现价格走势离散方法
价格行为分析工具包开发(第十六部分):引入四分之一理论(2)—— 侵入探测器智能交易系统(EA)
使用Python和MQL5进行多品种分析(第三部分):三角汇率