开发回放系统(第 74 部分):新 Chart Trade(一)
概述
在上一篇文章“开发回放系统(第 73 部分):不寻常的通信(二) ”中,我们完成了应用程序开发的第二阶段,该阶段负责生成回放/模拟器。换句话说,我们已经设法使整个系统以一种结构良好、功能齐全的方式运行。更具体地说,我们现在有一个能够呈现与真实市场走势密切相关的东西的系统。
然而,到目前为止,我们所做的一切只是整个任务的一部分。我们现在正进入开发的第三阶段。在这个阶段,我们的重点将从回放/模拟器本身转移。现在的重点将是直接使用实时交易服务器。换句话说,我们将开始开发必要的工具来打开、管理和关闭头寸。
其中一些工具在本系列的前面已经讨论过。然而,由于回放/模拟应用程序随着时间的推移发生了许多变化,我们需要重新创建或至少调整旧工具以适应新模型。我们将要研究的第一个工具是 Chart Trade。此工具旨在帮助我们开仓、平仓、执行市价单。MetaTrader 5 已经在其标准安装中包含了这样的功能。它有一键交易按钮,如下图所示:

虽然这些按钮非常有用,而且工作得很好,但它们对我们没有用,因为它们被设计成直接与实时交易服务器通信。然而,我们的回放/模拟系统并不是真正的服务器。它是一种服务,或者更确切地说,是一组应用程序,旨在模拟真实的服务器。
因此,我们需要创建自己的工具。因此,我们将能够复制 MetaTrader 5 的内置功能,由于我们在模拟环境中工作,因此无法使用。
这就引出了一个重要的问题:我在这里所做的所有努力都是为了决定只在 MQL5 中构建整个系统。从技术角度来看,这种约束使开发过程特别有趣。纯粹在 MQL5 中创建一个行为像真实交易服务器的东西一直是一个有趣的挑战。从纯粹的编程角度来看,使用套接字以 C 或 C++ 创建应用程序要简单得多。这样的应用程序可以实现所有必要的协议,以便 MetaTrader 5 将其视为真正的服务器。
虽然这种替代方法可以简化许多方面,允许我们在标准配置中使用 MetaTrader 5,但它无助于我们对 MQL5 的理解。如果一切都是用 C/C++ 实现的,就根本不需要使用 MQL5。这意味着错过了大量关于如何在 MQL5 中有效编程的宝贵知识。自从我接受了使用纯 MQL5 构建像模拟器这样复杂的东西的挑战以来,到目前为止,我一直在努力实现这一目标。但现在,我们正进入一个暂停回放/模拟器的阶段。有一段时间,你会看到一些东西在演示帐户中运行,但实现的设计总是考虑到模拟器。
新 Chart Trade 的诞生
我们上次讨论 Chart Trade 是在文章“开发回放系统(第 47 部分):Chart Trade 项目(四) “。尽管从那时起我们就没有再处理过这个问题,但现在是时候重新审视它了。然而,考虑到自那篇文章以来发生了很大变化,许多旧代码不再有用。它现在已经完全过时了,应该被删除。尽管如此,当时的许多核心概念仍然具有相关性和适用性。因此,我们将继续开发,但有一个关键的区别:代码现在将被完全重组,以纳入到目前为止开发的新概念。
您现在应该了解一件重要的事情:指标不能打开、修改或关闭头寸或订单。该责任完全由 EA 交易承担。然而,我们不会将 Chart Trade 作为 EA 交易来实现。相反,Chart Trade 将被开发为一种与 EA 交易进行通信以执行所需操作的指标。
如果您已经完全理解了本系列前面的文章,您会记得指标和服务之间的通信是通过自定义事件完成的。使用这些自定义事件是迄今为止在回放/模拟环境中程序之间传输数据的最实用方法。因此,即使我们暂时将注意力从回放/模拟器转移开,所有开发仍将与该上下文保持一致。考虑到这一点,第一步将是定义一些新的自定义事件值。新的 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. }; 43. //+------------------------------------------------------------------+
Defines.mqh 文件源代码
请注意,唯一引入的更改是在第 39、40 和 41 行。这些行定义了 Chart Trade 指标将触发的自定义事件,以通知 EA 交易需要采取某些行动。事实上,正是 EA 交易负责执行:开仓和平仓。重要的是要澄清,我们在这里谈论的不是订单本身。Chart Trade 指标的真正目的是取代 MetaTrader 5 的开仓和平仓默认系统,无论这意味着增加仓位规模还是执行部分平仓。Chart Trade 将执行相同的功能,但还有一个额外的好处,即允许我们稍后在回放/模拟器环境中应用相同的逻辑。在此背景下,操作将始终按市场执行。进一步地,我们将开始开发一个单独的系统,允许我们使用挂单。但让我们一步一步来吧。最简单的实现概念是市场执行。
有了这一点,我们现在需要重新启动并运行旧指标。您不会真的认为我们要从头开始重写整个指标吧?这绝不是我们的目的。这样做是完全不合理的。
因此,让我们来看看更新的指标代码,随着我们的深入,您会理解其中的几个原因。我们首先检查头文件 C_AdjustTemplate.mqh。完整代码如下所示:
001. //+------------------------------------------------------------------+ 002. #property copyright "Daniel Jose" 003. //+------------------------------------------------------------------+ 004. #include "../Auxiliar/C_Terminal.mqh" 005. //+------------------------------------------------------------------+ 006. #define def_PATH_BTN "Images\\Market Replay\\Chart Trade" 007. #define def_BTN_BUY def_PATH_BTN + "\\BUY.bmp" 008. #define def_BTN_SELL def_PATH_BTN + "\\SELL.bmp" 009. #define def_BTN_DT def_PATH_BTN + "\\DT.bmp" 010. #define def_BTN_SW def_PATH_BTN + "\\SW.bmp" 011. #define def_BTN_MAX def_PATH_BTN + "\\MAX.bmp" 012. #define def_BTN_MIN def_PATH_BTN + "\\MIN.bmp" 013. #define def_IDE_RAD def_PATH_BTN + "\\IDE_RAD.tpl" 014. #define def_IDE_RAD "Files\\Chart Trade\\IDE_RAD.tpl" 015. //+------------------------------------------------------------------+ 016. #resource "\\" + def_BTN_BUY 017. #resource "\\" + def_BTN_SELL 018. #resource "\\" + def_BTN_DT 019. #resource "\\" + def_BTN_SW 020. #resource "\\" + def_BTN_MAX 021. #resource "\\" + def_BTN_MIN 022. #resource "\\" + def_IDE_RAD as string IdeRad; 023. //+------------------------------------------------------------------+ 024. class C_AdjustTemplate 025. { 026. private : 027. string m_szName[], 028. m_szFind[], 029. m_szReplace[], 030. m_szFileName; 031. int m_maxIndex, 032. m_FileIn, 033. m_FileOut; 034. bool m_bFirst; 035. //+------------------------------------------------------------------+ 036. public : 037. //+------------------------------------------------------------------+ 038. C_AdjustTemplate(const string szFile, const bool bFirst = false) 039. :m_maxIndex(0), 040. m_szFileName(szFile), 041. m_bFirst(bFirst), 042. m_FileIn(INVALID_HANDLE), 043. m_FileOut(INVALID_HANDLE) 044. { 045. ResetLastError(); 046. if (m_bFirst) 047. { 048. int handle = FileOpen(m_szFileName, FILE_TXT | FILE_WRITE); 049. FileWriteString(handle, IdeRad); 050. FileClose(handle); 051. } 052. if ((m_FileIn = FileOpen(m_szFileName, FILE_TXT | FILE_READ)) == INVALID_HANDLE) SetUserError(C_Terminal::ERR_FileAcess); 053. if ((m_FileOut = FileOpen(m_szFileName + "_T", FILE_TXT | FILE_WRITE)) == INVALID_HANDLE) SetUserError(C_Terminal::ERR_FileAcess); 054. } 055. //+------------------------------------------------------------------+ 056. ~C_AdjustTemplate() 057. { 058. FileClose(m_FileIn); 059. FileClose(m_FileOut); 060. FileMove(m_szFileName + "_T", 0, m_szFileName, FILE_REWRITE); 061. ArrayResize(m_szName, 0); 062. ArrayResize(m_szFind, 0); 063. ArrayResize(m_szReplace, 0); 064. } 065. //+------------------------------------------------------------------+ 066. void Add(const string szName, const string szFind, const string szReplace) 067. { 068. m_maxIndex++; 069. ArrayResize(m_szName, m_maxIndex); 070. ArrayResize(m_szFind, m_maxIndex); 071. ArrayResize(m_szReplace, m_maxIndex); 072. m_szName[m_maxIndex - 1] = szName; 073. m_szFind[m_maxIndex - 1] = szFind; 074. m_szReplace[m_maxIndex - 1] = szReplace; 075. } 076. //+------------------------------------------------------------------+ 077. string Get(const string szName, const string szFind) 078. { 079. for (int c0 = 0; c0 < m_maxIndex; c0++) if ((m_szName[c0] == szName) && (m_szFind[c0] == szFind)) return m_szReplace[c0]; 080. 081. return NULL; 082. } 083. //+------------------------------------------------------------------+ 084. void Execute(void) 085. bool Execute(void) 086. { 087. string sz0, tmp, res[]; 088. int count0 = 0, i0; 089. 090. if ((m_FileIn != INVALID_HANDLE) && (m_FileOut != INVALID_HANDLE)) while ((!FileIsEnding(m_FileIn)) && (_LastError == ERR_SUCCESS)) 091. if ((m_FileIn == INVALID_HANDLE) || (m_FileOut == INVALID_HANDLE)) return false; 092. while (!FileIsEnding(m_FileIn)) 093. { 094. sz0 = FileReadString(m_FileIn); 095. if (sz0 == "<object>") count0 = 1; 096. if (sz0 == "</object>") count0 = 0; 097. if (count0 > 0) if (StringSplit(sz0, '=', res) > 1) 098. { 099. if ((m_bFirst) && ((res[0] == "bmpfile_on") || (res[0] == "bmpfile_off"))) 100. sz0 = res[0] + "=\\Indicators\\Replay\\Chart Trade.ex5::" + def_PATH_BTN + res[1]; 101. sz0 = res[0] + "=\\Indicators\\Chart Trade.ex5::" + def_PATH_BTN + res[1]; 102. i0 = (count0 == 1 ? 0 : i0); 103. for (int c0 = 0; (c0 < m_maxIndex) && (count0 == 1); i0 = c0, c0++) count0 = (res[1] == (tmp = m_szName[c0]) ? 2 : count0); 104. for (int c0 = i0; (c0 < m_maxIndex) && (count0 == 2); c0++) if ((res[0] == m_szFind[c0]) && (tmp == m_szName[c0])) 105. { 106. if (StringLen(m_szReplace[c0])) sz0 = m_szFind[c0] + "=" + m_szReplace[c0]; 107. else m_szReplace[c0] = res[1]; 108. } 109. } 110. if (FileWriteString(m_FileOut, sz0 + "\r\n") < 2) return false; 111. }; 112. 113. return true; 114. } 115. //+------------------------------------------------------------------+ 116. }; 117. //+------------------------------------------------------------------+ 118. #undef def_BTN_BUY 119. #undef def_BTN_SELL 120. #undef def_BTN_DT 121. #undef def_BTN_SW 122. #undef def_BTN_MAX 123. #undef def_BTN_MIN 124. #undef def_IDE_RAD 125. #undef def_PATH_BTN 126. //+------------------------------------------------------------------+
C_AdjustTemplate.mqh 文件源代码
这段代码中的一些行已被删除,现在让我们了解一下为什么会发生这种情况。第 14 行被修改并替换为现在第 13 行的代码。此更改反映了模板文件重新定位到新路径。不要担心,这些文件包含在附件中,以防您还没有。您需要做的就是保持附件中提供的相同文件夹结构。这样,就可以毫无问题地定位此处声明的资源。除了这一变化,其他几行也被删除了。
第 45 行已被删除,原因已在前面的文章中讨论过。也就是说,有时 _LastError 变量包含一个值,不是因为在执行过程中发生了错误,而是由于其他一些内部原因。因此,除非情况特殊且真正相关,否则我不会关心_ LastError 的值。
现在让我们看一下第 90 行。为何删除此行?因为 _LastError 可能保存 ERR_SUCCESS 以外的值,即使它不是由我们的应用程序引起的。为了避免潜在的误解或冲突,我们不得不在这里做出一些调整。
曾经的过程现在已经转变为函数。让我们重新审视第 84 行,您会看到它已被删除,并由第 85 行替换。为什么要做出这一更改?原因很简单:我们可能会偶尔遇到错误,最好不要严重或直接依赖 _LastError 变量。
您需要了解,该指标在非常严格的环境中运行。与挂单不同,挂单有时(稍后我们将看到为什么是“有时”)可以容忍小错误,但该指标不允许这种情况。这是因为它涉及市场执行。
此外,还有一个更关键的复杂问题,我们将在以后的文章中介绍。目前,我们必须确保模板文件中的数据绝对准确。
因此,如果第 91 行或第 110 行指示失败,我们必须将此情况报告给位于其他地方的调用者。但是,如果一切正常,模板文件将被更新,并在第 113 行返回成功。就这么简单。
在结束对头文件的解释之前,我想提请您注意第 101 行的内容。第 100 行已被删除并替换为这一行。现在我们有的是 101 行,这是一个字符串。它指定了 Chart Trade 指标的预期位置。换句话说,位置已经改变。
现在您需要确保指标准确放置在该字符串指定的位置,并且其名称与指示的名称匹配。这是因为当使用该字符串时,我们实际上是在访问指标中声明为资源的位图文件。如果您更改任何内容,无论是代码、指标名称还是其文件夹位置,MetaTrader 5 将无法访问位图文件。结果,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.74" 06. #property icon "/Images/Market Replay/Icons/Indicators.ico" 07. #property link "https://www.mql5.com/pt/articles/12413" 08. #property indicator_chart_window 09. #property indicator_plots 0 10. #property indicator_buffers 1 11. //+------------------------------------------------------------------+ 12. #include <Market Replay\Chart Trader\C_ChartFloatingRAD.mqh> 13. //+------------------------------------------------------------------+ 14. #define def_ShortName "Indicator Chart Trade" 15. //+------------------------------------------------------------------+ 16. C_ChartFloatingRAD *chart = NULL; 17. //+------------------------------------------------------------------+ 18. input long user00 = 0; //ID 19. input ushort user01 = 1; //Leverage 20. input double user02 = 100.1; //Finance Take 21. input double user03 = 75.4; //Finance Stop 22. //+------------------------------------------------------------------+ 23. double m_Buff[]; 24. //+------------------------------------------------------------------+ 25. int OnInit() 26. { 27. bool bErr; 28. 29. chart = new C_ChartFloatingRAD(user00, "Indicator Chart Trade", new C_Mouse(user00, "Indicator Mouse Study"), user01, user02, user03); 30. chart = new C_ChartFloatingRAD(def_ShortName, new C_Mouse(0, "Indicator Mouse Study"), user01, user02, user03); 31. 32. if (_LastError >= ERR_USER_ERROR_FIRST) return INIT_FAILED; 33. 34. if (bErr = (_LastError != ERR_SUCCESS)) Print(__FILE__, " - [Error]: ", _LastError); 35. 36. SetIndexBuffer(0, m_Buff, INDICATOR_DATA); 37. ArrayInitialize(m_Buff, EMPTY_VALUE); 38. 39. return (bErr ? INIT_FAILED : INIT_SUCCEEDED); 40. return INIT_SUCCEEDED; 41. } 42. //+------------------------------------------------------------------+ 43. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 44. { 45. (*chart).MountBuffer(m_Buff, rates_total); 46. 47. return rates_total; 48. } 49. //+------------------------------------------------------------------+ 50. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 51. { 52. if (_LastError < ERR_USER_ERROR_FIRST) 53. (*chart).DispatchMessage(id, lparam, dparam, sparam); 54. (*chart).MountBuffer(m_Buff); 55. 56. ChartRedraw(); 57. } 58. //+------------------------------------------------------------------+ 59. void OnDeinit(const int reason) 60. { 61. switch (reason) 62. { 63. case REASON_INITFAILED: 64. ChartIndicatorDelete(ChartID(), 0, def_ShortName); 65. break; 66. case REASON_CHARTCHANGE: 67. (*chart).SaveState(); 68. break; 69. } 70. 71. delete chart; 72. } 73. //+------------------------------------------------------------------+
Chart Trade 指标源代码
乍一看,这段代码可能看起来很复杂,因为有许多删除线。删除这么多行的原因是因为这是原始代码的修改版本。
如果你还没有看过,可以在文章中找到原始版本“开发回放系统(第 47 部分):Chart Trade 项目(四) “。但不要试图使用那篇文章中的代码。此版本的工作方式非常不同。虽然这看起来很复杂,但实际上很简单。因为所有的复杂性都转移到了头文件中。因此,为了充分理解我们稍后将研究的头文件中发生的事情,准确理解这段代码的工作原理是非常重要的。
让我们一步一步来。在大多数情况下,您在这里看到的是删除,而不是实际的修改。我们删除的第一行是第 10 行,如您所见,它已被删除。
现在事情似乎变得棘手了。通过删除指定所用缓冲区数量的指标属性,我们告诉编译器该指标使用零缓冲区。这是因为这是此属性的默认值。换句话说,该指标不会输出任何可通过 CopyBuffer 访问的内部缓冲区数据。
这听起来可能是一个严重的问题。如果你仍然不明白没有缓冲区对指标意味着什么,这可能意味着你还没有完全理解 MetaTrader 5 和 MQL5 环境中的工作原理。我将尝试为您分解它,特别是如果您仍在学习并渴望精通 MQL5。
指标的作用是协助我们(通过执行计算、发出信号,或者像本例中那样,提供用于交互的用户界面)。在这种用例中,特别是最后一个用例中,从技术上讲,您可以使用其他类型的程序,如脚本。然而,脚本的问题在于,当您切换时间框架时,它们会被删除并且不会自动重新加载。您必须手动将它们重新应用到图表中,这可能会带来不便。这就是为什么我们在这种情况下使用指标。
但指标也有自己的问题,尤其是当你想使用计时器时。如果向指标添加计时器,则会影响同一图表上的所有其他指标。因此,当我们需要定时执行而不依赖脚本时,我们通常会求助于 EA 交易或服务。它们之间的选择完全取决于定时的目的。
当然,Chart Trade 可以作为一项服务或 EA 来实现。它不一定是一个指标。如果我们希望 Chart Trade 能够在所有图表上运行,那么使用服务甚至会很有用。然后,该服务可以确保 Chart Trade 对象在每个相关图表中正确显示。
这种方法的问题在于事件处理,服务没有内置的事件处理程序。它们本质上是与任何图表无关的脚本。因此,使用 Chart Trade 服务意味着需要创建一个指标。该指标只处理事件,例如按钮点击或其他用户交互。
而这会使设置变得比需要的更复杂。在 EA 中使用 Chart Trade 怎么样?这听起来更合理。从某种程度上来说,确实如此。但这也不切实际。
为什么呢?一个词:多样化。如果您在 EA 中包含 Chart Trade,它将仅存在于该单个 EA 中。如果您后来决定构建另一个 EA,无论出于何种原因,您都必须重新包含整个 Chart Trade 代码。当然,您可以使用 #include 指令将其模块化,就像我们在第 12 行看到的那样,这样就可以了。但是如果您稍后改进 Chart Trade 代码会怎样?您必须重新编译每个使用它的 EA。这是低效且容易出错的。
好吧,我们可以考虑将 Chart Trade 作为 EA 的一部分,而无需在升级时重新编译它们。这也是值得思考的事情。我们可以使用类似 DLL 的东西,但这会带来比它解决的更多的复杂性。所以最终,我决定保留 Chart Trade 作为指标。但与以前不同的是,此版本不使用任何缓冲区来存储外部访问的数据。现在事情的处理方式不同了,我们会在适当的时候讨论这一点。让我们回到代码,因为还有一些方面需要理解。
看一下第 14 行。这个定义出现在第 12 行的 #include 之后,这非常重要。与全局变量不同,#define 出现在 #include 之前还是之后可以控制事物的行为方式,甚至可以控制包含文件中某些选项是否可用。所以请注意这一点。
由于第 14 行中的定义是在 C_ChartFloatingRAD.mqh 包含之后进行的,因此在该文件中它将不可见或不可用。如果我们将其放在 #include 之前,那么它将在 C_ChartFloatingRAD.mqh 头文件的内容中可见。所以是的,在编程中,顺序很重要。让我们继续,因为前面还有很多有趣的事情。
第 18 行已被删除,因为不再需要。用户现在负责将图表 Chart Trade 添加到图表中,因此我们不需要定义图表 ID。第 23 行也被删除了,但不是因为第 10 行被删除了。这两行毫无关联。第 10 行用于告诉我们指标是否存在。如果答案是肯定的,那么它表明通过 CopyBuffer 从指标外部访问了多少个缓冲区。但即使我们不允许从外部访问缓冲区,我们仍然可以有内部缓冲区。然而,由于我们不再需要,第 23 行被删除了。同样的道理也适用于第 36 行和第 37 行。
您会注意到 OnInit 中的很多代码已被删除。由于变化,其中大部分已被删除,我们将在下一篇文章中解释,其中我们将探讨头文件 C_ChartFloatingRAD.mqh。在所有删除的代码中,只有三行在功能上很重要。让我们从第 30 行开始逐一查看。它初始化 C_ChartFloatingRAD 类,这是使用 “new” 运算符完成的。但您可能知道,构造函数不返回任何值。那么我们如何检查初始化是否失败?我们需要另一种机制。
在更模块化的程序中,构造函数应该只初始化变量和内部状态。但由于没有严格的规则,我们经常让构造函数做更多的事情。这就是事情变得棘手的地方。如果没有报告构造函数失败的机制,我们就无法解决初始化问题。
幸运的是,MQL5 允许我们这样做。虽然这种方法可能不是最合适的,但它确实有效。在第 32 行,我们检查 _LastError 的值。如果它包含预定义的错误代码,则我们认为初始化失败。我不确定是否可以保留这个结构,但多亏了第 52 行,我可以。不过,我们稍后会讨论这个问题。如果第 32 行检测到初始化失败,我们将从 OnInit 返回一个错误常量。这会导致 MetaTrader 5 调用 OnDeInit,从而从图表中删除指标。否则,第 40 行返回一个成功常数。
现在看第 45 行和第 54 行。这两者之前都用于设置缓冲区。然而,由于我们不再使用缓冲区,这些行现在已经过时并且已被删除。现在请注意第 52 行的检查,这个条件现在可能没有多大意义,但如果没有它,第 53 行肯定会失败。当我们在下一篇文章中查看 C_ChartFloatingRAD 类时,为什么会发生这种情况将会变得清晰。
最后,让我们看一下 OnDeInit() 过程。当指标从图表中移除时,它会由 MetaTrader 5 自动触发。现在,OnDeInit 有了一些不同寻常的东西。我指的是第 64 行。这一行为什么会出现在这里?乍一看,这没有意义。这是因为您在查看代码时不知道 C_ChartFloatingRAD 类。如果只考虑这个文件,OnDeInit(REASON_INITFAILED) 的唯一原因就是第 32 行。但 C_ChartFloatingRAD 类中还有其他原因。这就是我在这里集中进行此调用的原因。因此,出现了第 64 行。此行确保 Chart Trade 指标从图表中删除。下一篇文章将提供更深层次的推理。
最后的探讨
在本文中,我介绍了对 Chart Trade 指标所做的部分更改。在没有先阅读下一篇解释 C_ChartFloatingRAD 类的文章的情况下,请勿尝试完全理解或使用此代码。否则,你可能会犯严重的错误。请耐心等待下一篇文章,因为它告诉你的东西真的很值得。您将看到一个类,其中的 Chart Trade 指标被准确实现。
如果您想要编译此代码,请确保您已获得 C_AdjustTemplate.mqh 头文件所使用的数据。这些都包含在附件中。也就是说,您还可以为 Chart Trade 创建自己的图像和模板模型。只需确保它们与 C_AdjustTemplate 类设置的期望兼容。目前就是这样,别忘了看看这个系列的下一部分。
本文由MetaQuotes Ltd译自葡萄牙语
原文地址: https://www.mql5.com/pt/articles/12413
注意: MetaQuotes Ltd.将保留所有关于这些材料的权利。全部或部分复制或者转载这些材料将被禁止。
本文由网站的一位用户撰写,反映了他们的个人观点。MetaQuotes Ltd 不对所提供信息的准确性负责,也不对因使用所述解决方案、策略或建议而产生的任何后果负责。
价格行为分析工具包开发(第七部分):信号脉冲智能交易系统(EA)
交易中的神经网络:搭配区段注意力的参数效率变换器(终篇)
从基础到中级:联合(二)
分析交易所价格的二进制代码(第一部分):技术分析的新视角