English Русский Español Deutsch 日本語 Português
preview
开发回放系统(第 74 部分):新 Chart Trade(一)

开发回放系统(第 74 部分):新 Chart Trade(一)

MetaTrader 5示例 |
252 0
Daniel Jose
Daniel Jose

概述

在上一篇文章“开发回放系统(第 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

附加的文件 |
Anexo.zip (9.92 KB)
价格行为分析工具包开发(第七部分):信号脉冲智能交易系统(EA) 价格行为分析工具包开发(第七部分):信号脉冲智能交易系统(EA)
借助“信号脉冲(Signal Pulse)”这款MQL5智能交易系统(EA),释放多时间框架分析的潜力。该EA整合了布林带(Bollinger Bands)和随机震荡器(Stochastic Oscillator),以提供准确、高概率的交易信号。了解如何实施这一策略,并使用自定义箭头有效直观地显示买入和卖出机会。非常适合希望借助多时间框架的自动化分析来提升自身判断能力的交易者。
交易中的神经网络:搭配区段注意力的参数效率变换器(终篇) 交易中的神经网络:搭配区段注意力的参数效率变换器(终篇)
在之前的工作中,我们讨论了 PSformer 框架的理论层面,其中包括经典变换器架构的两大创新:参数共享(PS)机制,以及时空区段注意力(SegAtt)。在本文中,我们继续实现所提议方式的 MQL5 版本。
从基础到中级:联合(二) 从基础到中级:联合(二)
今天我们有一篇非常有趣的文章。我们将研究联合并尝试解决之前讨论的问题。我们还将探讨在应用程序中使用联合时可能出现的一些不寻常的情况。此处提供的材料仅用于教学目的。在任何情况下,除了学习和掌握所提出的概念外,都不应出于任何目的使用此应用程序。
分析交易所价格的二进制代码(第一部分):技术分析的新视角 分析交易所价格的二进制代码(第一部分):技术分析的新视角
本文提出了一种基于将价格波动转换为二进制代码的技术分析创新方法。作者展示了市场行为的各个方面——从简单的价格波动到复杂形态——如何被编码为一系列的0和1。