English Русский Español Deutsch 日本語 Português
preview
开发回放系统(第 38 部分):铺路(II)

开发回放系统(第 38 部分):铺路(II)

MetaTrader 5示例 | 26 八月 2024, 11:33
156 0
Daniel Jose
Daniel Jose

概述

在上一篇文章《 开发回放系统(第 37 部分):铺路(I)》中,我们已看到了一种简单的方式来防止用户在图表上重叠指标。在本文中,我们验证了通过非常简单的代码(仅包含几行代码),当我们仅需要一个副本指标出现,MetaTrader 5 平台能帮助我们避免在图表上出现第二个指标。重要的是,指标不能重叠。

我希望您理解这个想法,以及如何实现预期的结果。在任何情况下该指标都不应重叠。

在确认 MetaTrader 5 中不重叠原则的同时,请注意,指标和智能系统之间的双向通信还有很长的路要走。我们离此还很远。不过,保证指标不会在图表上重叠至少可以令人心态平和。因为当指标和 EA 交互时,我们能知道我们正在与正确的指标打交道。

在本文中,我们将开始通过 EA 的眼睛来看待事物。但我们还必须对指标进行一些修改,因为不幸的是,上一篇文章中的指标根本无法对 EA 做出反应。当 EA 想知道某些事情时,就会发生这种情况。


在流程之间创建第一个交互

为了更好地解释正在发生的事情,我们需要创建一些控制点。我们从指标代码开始。别担心,这一切都很容易理解。

完整的指标代码如下所示:

01. #property copyright "Daniel Jose"
02. #property link      ""
03. #property version   "1.00"
04. #property indicator_chart_window
05. #property indicator_plots 0
06. //+------------------------------------------------------------------+
07. #define def_ShortName       "SWAP MSG"
08. #define def_ShortNameTmp    def_ShortName + "_Tmp"
09. //+------------------------------------------------------------------+
10. input double user00 = 0.0;
11. //+------------------------------------------------------------------+
12. long m_id;
13. //+------------------------------------------------------------------+
14. int OnInit()
15. {
16.     m_id = ChartID();
17.     IndicatorSetString(INDICATOR_SHORTNAME, def_ShortNameTmp);
18.     if (ChartWindowFind(m_id, def_ShortName) != -1)
19.     {
20.             ChartIndicatorDelete(m_id, 0, def_ShortNameTmp);
21.             Print("Only one instance is allowed...");
22.             return INIT_FAILED;
23.     }
24.     IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName);
25.     Print("Indicator configured with the following value:", user00);
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. //+------------------------------------------------------------------+


请注意,代码的唯一添加是第 25 行。这将是我们的验证行,至少对于第一次。此刻,我们对 EA 如何与指标交互,以及我们是否可以向其发送信息或数据感兴趣。

注意,在第 10 行中,指标预期的信息类型为 double(双精度)。这是有意为之的,因为目标是创建一种机制,可以替换全局终端变量系统,用于 EA 和指标之间的通信。如果开发或使用的手段被证明是不合适的,那么我们可以轻松地用终端全局变量模型替换这个模型。

您还可用其它类型的数据来提供通信,至少对于从 EA 到指标的数据传输。

现在我们已经修改了指标代码,我们可以继续讨论 EA 代码。它的完整代码显示如下:

01. #property copyright "Daniel Jose"
02. #property link      ""
03. #property version   "1.00"
04. //+------------------------------------------------------------------+
05. input double user00 = 2.0;
06. //+------------------------------------------------------------------+
07. int m_handle;
08. long m_id;
09. //+------------------------------------------------------------------+
10. int OnInit()
11. {
12.     m_id = ChartID();
13.     if ((m_handle = ChartIndicatorGet(m_id, 0, "SWAP MSG")) == INVALID_HANDLE)
14.     {
15.             m_handle = iCustom(NULL, PERIOD_CURRENT, "Mode Swap\\Swap MSG.ex5", user00);
16.             ChartIndicatorAdd(m_id, 0, m_handle);
17.     }
18.     Print("Indicator loading result:", m_handle != INVALID_HANDLE ? "Success" : "Failed");
19.     
20.     return INIT_SUCCEEDED;
21. }
22. //+------------------------------------------------------------------+
23. void OnDeinit(const int reason)
24. {
25.     ChartIndicatorDelete(m_id, 0, "SWAP MSG");
26.     IndicatorRelease(m_handle);
27. }
28. //+------------------------------------------------------------------+
29. void OnTick()
30. {
31. }
32. //+------------------------------------------------------------------+


对于许多人来说,这个 EA 代码也许看似十分奇怪。我们看看它是如何工作的。这是我们真正需要做的第一步。

在第 05 行,我们让用户有机会设置传递给指标的数值。记住:我们打算测试,并了解通信将如何发生。第 07 行和第 08 行在 EA 代码中包含两个内部全局变量。我通常不喜欢使用全局变量,但在这种情况下,您可以做一个例外。这些变量的内容将自第 10 行开始的 OnInit 命令的代码中判定。

这也许看似有点复杂。这是因为在这个阶段我们需要了解一些事情。

在第 12 行,我们初始化一个变量,该变量是正在运行的 EA 时间帧索引。然后来到第 13 行,该处初学者经常会遇到问题。许多人根本不将此行添加到他们的代码当中。缺少此行不会立即中断您的代码,但包含它可以防止发生某些类型的错误。其中许多错误是运行时错误,随时可能发生,因此难以解决。

现在我们已经在第 13 行添加了代码,如果 MetaTrader 5 已在图表上加载了我们所需的指标,它将返回该指标的句柄。因此,我们就无需费心将指标放在图表上。

但是,如果第 13 行显示所需的指标不在图表上,我们就会运行将指标添加到图表中的代码。记住以下几点:我们需要并将使用的指标是自定义指标。因此,我们称之为 iCustom,可以在第 15 行看到。

现在棘手的部分来了:为什么第 15 行的 iCustom 是这样声明的?您知道为什么吗?为了明白这一点,我们来看看文档是怎么说的:

int  iCustom(
                 string            symbol,   // symbol name
                 ENUM_TIMEFRAMES   period,   // period
                 string            name      // folder/custom indicator name
                 ...                         // list of indicator parameters
             );

从上表中,您可看到函数的第一个参数是品种名称。但在代码的第 15 行中,第一个参数为 NULL。为什么会这样?为什么我们不使用 _Symbol 常量?原因是对于编译器来说,NULL_Symbol 是相同的。这样,指标将取 EA 所在图表的相同品种创建。

清单中的下一个参数是图表周期。同样,我们有一些与许多人期望不同的东西。在第 15 行,我们采用值 PERIOD_CURRENT,但为什么呢?原因是我们想要指标与智能系统在同一周期保持同步。一个细节:如果您希望指标在不同的图表周期进行分析,只需在该参数中指定所需的周期即可。因此,指标将在特定周期内固定,而 EA 能够在不同的周期前行。

当涉及到自定义指标时,我认为这是可能导致严重后果的第三个参数。许多人不知道该在此处放什么,有时会留空或指定错误的位置,因为他们不理解其含义。如果我们指定了错误的位置,MetaTrader 5 就无法找到我们需要的指标。

查看第 15 行的第三个参数中声明的内容。我们不仅指定名称,还指定名称和路径。这些信息来自哪里?为了找出答案,我们必须回到上一篇文章,找出这些信息的来源。参见图例 01。

图例 01

图例 01 - 创建一个指标

在图例 01 中,您可以看到第三个参数的信息来自何处。这些实际上是相同的事情。进行以下更改:首先我们删除根目录,在本例中,它将是 indicators。这是因为 MetaTrader 5 将首先在该目录中查找指标。第二点是我们添加了 .ex5 文件。以这种方式,我们就能获得可执行文件的正确位置。当 MetaTrader 5 将 EA 放在图表上,并执行第 15 行时,它将知道在哪里、以及使用哪个指标。

下面我们将查看可能浮现的一些故障和问题。但首先,我们把基础知识弄清楚。

第三个参数和随后的参数是可选的。如果我们想为指标提供任何值,我们必须从第四个参数开始这样做。但是,您必须确保操作时,按照参数在指标上所示的相同顺序进行。您还需要确保它们的类型正确。如果指标预期接收浮点值长整数值,则不能输入双精度值。在这种情况下,MetaTrader 5 将在启动指标后立即生成错误。

还有其它方式可以将参数传递给指标,但现在,为了简单起见,我们将调用 iCustom。如果您查看指标代码,您就会看到在第 10 行中预期为双精度值。该值通过 EA 提供给指标,位于 EA 代码第 15 行的第四个参数之中。

现在出现了一个许多人忽视的重要问题,其会导致整个系统的问题。它是 EA 代码中的第 16 行。为什么这一行如此重要?

在某些情况下,当涉及到自定义指标时,可以忽略此行。当我们想要 MetaTrader 5 仅支持在图表上放置一个实例时,这对于那些我们需要的指标来说此刻为真。我们不能以任何方式或形式忽略第 16 行,当存在第 16 行时,也必须添加第 25 行。

这两行避免了问题。第 16 行将在图表上运行该指标。因此,如果用户尝试在同一图表上运行指标的另一个实例,MetaTrader 5 和指标代码将阻止新实例运行。如果从 EA 代码中删除第 16 行,用户可能会意外地在图表上放置指标的一个新实例,从而导致用户(而不是 EA)对指标执行的计算感到困惑。

对于所计算指标,我们遇到了一定的问题,但是对于像 ChartTrader 这样的指标,问题就更加复杂了。因为在这种情况下,我们正在与图形对象打交道。了解如何正确地运作每件事很重要。然后会更容易理解如何运作更复杂的事情。

我认为现在很清楚第 16 行为何重要了。25 行起什么作用?它将从图表中删除指定的指标。请注意一件事:我们需要指定要索要删除指标的名称。它放置在指标代码的第 24 行的指标当中。名称必须相同,否则 MetaTrader 5 会无法理解需要删除哪个指标。此外,在某些情况下,我们还必须判断使用哪个子窗口。由于我们没有用到子窗口,因此我们将此值保持等于

然后,在第 26 行,我们删除了指标处理程序,因为我们不再需要它。

现在我们来查看 MetaTrader 5 如何使用 EA 和指标之间的这种交互方案。为了理解这一点,您需要做一些事情。因此,请密切关注该过程的细节。此外,您应该测试每处修改,并尝试生成的代码,因为每处修改都会为 MetaTrader 5 带来自己的处理状况的方式。重要的是,不仅要阅读上述解释,还要了解正在发生的事情。为了理解当 EA 以某种方式编写时会发生什么,或者当同一 EA 以不同的方式编程时会发生什么,有必要考虑三种情况。EA 和指标中使用的代码,与我们到目前为止解释的代码相同,唯一的区别在于 EA 代码行的存在与否。

现在执行以下操作:

  • 首先,编译包含所有行的代码,并查看系统在 MetaTrader 5 中是如何工作的。
  • 然后删除第 16 行和第 25 行,并在 MetaTrader 5 中重新测试系统。
  • 最后,仅删除第 16 行,保留第 25 行。之后,再次测试系统。

您也许认为这一切都很无趣,而您作为一位有经验的程序员,永远不应犯这样的错误。但是,了解每行代码的作用,以及在运行 MetaTrader 5 时产生的结果,比看起来更重要。

如果您已经达到了这个阶段,那么您就会明白第二阶段,在这个阶段中,我们可以在不使用全局终端变量的情况下将数据从 EA 传输到指标。您已经朝着成为一位高品级程序员迈出了一大步。但如果您仍然不明白,不要气馁。返回到本文或上一篇文章的开头,并尝试理解该步骤。现在事情变得非常复杂,我没有开玩笑。我们需要让指标向 EA 发送数据。

在此,指标将数据发送到智能系统。在某些情况下,这可能非常简单。但是任何关注我文章的人都可能已经注意到,我喜欢把事情发挥到极致,让电脑汗流浃背,并要求它以不同的方式做事。这是因为我真的希望语言和平台都能在可能的极限下工作。


IndicatorCreate 或 iCustom — 采用哪一个?

许多初学者可能想知道如何调用自定义指标。这可以通过两种方式完成。我们在上一个主题中介绍了第一个,其中我们解释了 iCustom 的运用。但是还有另一种方式,在某些类型的建模中可能更具吸引力,而对于其它,调用 iCustom 就足够了。

这与必须使用一种方法或另一种无关。我在此的目的是展示如何调用 IndicatorCreate 来加载您自己的指标。

在开始之前,我们来了解一件事。IndicatorCreate 函数不是特殊函数。这实际上是一个基本函数。这是什么意思?基本函数是一种函数,它本质上是为提请另一个函数,但有一个细节:“派生”函数使用基本函数,但令其更易于使用。

这是因为不需要像我们对基本函数所期望的那样对调用进行建模。我们可以使用更简单的建模。其它函数就是这样产生的。您可以在文档中将它们视为技术指标。在这些指标中,有 iCustom,它本质上是一个派生函数,可以简化建模。

那么,若我们使用 IndicatorCreate 替代 iCustom,那么本文开头讨论的智能系统的源代码会如何?它的代码如下所见:

01. #property copyright "Daniel Jose"
02. #property link      ""
03. #property version   "1.00"
04. //+------------------------------------------------------------------+
05. input double user00 = 2.2;
06. //+------------------------------------------------------------------+
07. int m_handle;
08. long m_id;
09. //+------------------------------------------------------------------+
10. int OnInit()
11. {
12.     MqlParam params[];
13.     
14.     m_id = ChartID();       
15.     if ((m_handle = ChartIndicatorGet(m_id, 0, "SWAP MSG")) == INVALID_HANDLE)
16.     {
17.             ArrayResize(params, 2);
18.             params[0].type = TYPE_STRING;
19.             params[0].string_value = "Mode Swap\\SWAP MSG";
20.             params[1].type = TYPE_DOUBLE;
21.             params[1].double_value = user00;
22.             m_handle = IndicatorCreate(NULL, PERIOD_CURRENT, IND_CUSTOM, ArraySize(params), params);
23.             ChartIndicatorAdd(m_id, 0, m_handle);
24.     }
25.     Print("Indicator loading result:", m_handle != INVALID_HANDLE ? "Success" : "Failed");
26.     
27.     return INIT_SUCCEEDED;
28. }
29. //+------------------------------------------------------------------+
30. void OnDeinit(const int reason)
31. {
32.     ChartIndicatorDelete(m_id, 0, "SWAP MSG");
33.     IndicatorRelease(m_handle);
34. }
35. //+------------------------------------------------------------------+
36. void OnTick()
37. {
38. }
39. //+------------------------------------------------------------------+

比较上面的代码与我们文章开头展示的代码之间的差别。两者都做同样的事情。主要问题是:为什么调用这个 IndicatorCreate 的代码需要以这种方式执行?

iCustom 函数实际上隐藏了我们需要做的事情,但我们要弄清楚发生了什么。

首先,我们需要声明一个变量,该变量在第 12 行声明为数组。以这种方式完全能做到。如果图表上没有该指标,我们就需要创建它。为此,我们必须告诉 IndicatorCreate 函数该做什么、以及如何做。

除了我们在指标中所用的参数数量之外,我们还必须告诉 IndicatorCreate 函数指标的名称。因此,如果我们只输入指标的名称,则第 17 行的 ArrayResize 函数不应选择两个元素,而应是一个元素。如果我们向指标传递了 5 个参数,我们将不得不分配 6 个元素,依此类推。我们来查看我们需要发送多少个参数,并分配一个额外的位置。

如此,现在我们需要配置这个数组。此处第 22 行含有 IndicatorCreate 函数,表示我们将使用自定义指标。这是使用 IND_CUSTOM 枚举完成的。我们需要做若干件事。在第 18 行,我们看到如何继续处理数组的第一个位置。记住,MQL5 与 C++ 很相似,因此我们从零开始计数。第一个位置的信息类型必须是 STRING,因此声明的编写方式如第 18 行所示。

在第 19 行,我们输入所用自定义指标的名称。注意,指标名称与调用 iCustom 时所要用的名称相同。唯一的区别是我们没有指定文件扩展名。这必须是扩展名为 .ex5 的可执行文件,因此指定扩展名是可选项。

重要:第 18 行和第 19 行中的所做必须在每次使用自定义指标时完成。如果您使用任何其它指标,无论它是什么,那么一旦您开始设置数组数据,您就必须指定参数,因为您之后还会看到它,即从第 20 行开始。

现在我们已经告诉 IndicatorCreate 函数要使用哪个自定义指标,我们开始填充将传递给它的参数。必须按正确的顺序指定这些参数。无论您使用哪个指标,类型声明错误都会导致运行时出错。因此,在填写此信息时要非常小心。

在第 20 行,我们指定第一个参数的类型为双精度。但它可以是 ENUM_DATATYPE 枚举中定义的任何类型。您可以选用它们中的任何一个,但有一些事项需要注意。

类型 TYPE_DOUBLETYPE_FLOAT 将用于 double_value,在本例中在第 21 行。

如果第 20 行使用了类型 TYPE_STRING,则在第 21 行中,该值将强制转换为 string_value 变量。对于在第 20 行声明的任何其它可能类型,我们在第 21 行输入值使用 integer_value 变量。

这非常重要,所以我们要把这些信息说清楚。为了更好地理解,请查看下表:

所用的标识符 数据类型 何处放置数值 

TYPE_BOOL  布尔 integer_value

TYPE_CHAR  字符 integer_value    
TYPE_UCHAR    无符号字符 integer_value    
TYPE_SHORT    短整数 integer_value    
TYPE_USHORT    无符号短整数 integer_value
   
TYPE_COLOUR    颜色 integer_value
   
TYPE_INT    整数 integer_value
   
TYPE_UINT    无符号整数 integer_value
   
TYPE_DATETIME    日期时间 integer_value
   
TYPE_LONG    长整数 integer_value      
TYPE_ULONG    无符号长整数 integer_value
   
TYPE_FLOAT    浮点数 double_value     
TYPE_DOUBLE    双精度 double_value
   
TYPE_STRING    字符串 string_value    

表格或对应关系。

该表清楚地显示了我们将在第 20 行使用的内容(所用的标识符),以及将在第 21 行接收哪个变量值。我们必须针对将要传递给自定义指标的每个参数执行此操作。由于我们只使用一个参数,因此我们只操控那一个。

注意,在我们的特定情况下,调用 IndicatorCreate 比其衍生函数 iCustom 要耗费更多劳力。

在我们设置了我们想要定义的所有参数之后,我们转到第 22 行,在该处我们会实际调用该函数。这样做是为了令我们可以很容易地增加或减少事物。所有字段都已填充,因此,如果您需要更改指标或参数数量,则不必再次编辑第 22 行。

我们的思路是始终简化事情,而非令它们复杂化。

由于在 MetaTrader 5 中设置相同类型的执行需要做额外的工作,因此我们不会经常在当前代码中看到 IndicatorCreate 函数。但没有什么能阻止您去使用它。

在结束本文之前,我想简要介绍一下另一个函数:IndicatorParameters。该函数允许检查有关未知指标的某些信息。假设您的图表上有若干个不同的指标,每个指标都以特定方式进行了初始化。您可能希望自动执行一些基于指标的策略,但由于市场变化如此突然,所有指标也许需要几分钟才能反馈正确设置。

为了稍微加快该过程,您可以调用 IndicatorParameters 函数。一旦调用该函数,MetaTrader 5 将填充它,从而准确告诉我们特定指标是如何配置的。然后调用另一个函数(通常是 IndicatorCreate)来更改该指标的配置。如果 EA 基于该指标买入或卖出,它会立即明白该怎么做,因为我们已有一个触发器。

这个问题曾有过详细讨论,并在一系列关于交易自动化的文章中进行了演示。在文章《创建一个自动工作的 EA(第 15 部分):自动化(VII)》中,我们研究了如何依据指标作为买入或卖出的触发器。

但正如我刚才提到的,我们可以调用 IndicatorParameters 函数来令同一 EA 更加有趣。不过,我们不会探讨 IndicatorParameters 函数的使用,至少在本系列的回放/模拟中不会。我只是想提一下这个函数有多么实用。


结束语

在本文中,我们见证了如何向指标发送数据。虽然我们使用智能交易系统执行此操作,但您亦可用其它类型的流程,例如脚本。不幸的是,至少在撰写本文时,由于服务的性质,不可能使用服务作为完成这项工作的一种方式。

但没关系。我们已尽我们所能配合它工作。然而,这个问题还没有完全完结。我们仍然需要了解如何将数据从指标传输到智能系统。我们需要一种方式在回放/模拟系统中使用 Chat Trader,但我们不想使用全局终端变量来做到这一点,我们不想编译多个程序,并冒着忘记编译其中任何一个程序的风险。

为了了解我们离 Chart Trader 所需的目标有多近,我们需要另一篇文章。因此,在本系列的下一篇文章中,我们将了解如何处理创建图表交易者所需的方式。

不要错过下一篇文章:这个话题会非常有趣和令人兴奋。

本文由MetaQuotes Ltd译自葡萄牙语
原文地址: https://www.mql5.com/pt/articles/11591

附加的文件 |
EA.mq5 (1.36 KB)
swap.mq5 (1.31 KB)
开发回放系统(第 41 部分):启动第二阶段(二) 开发回放系统(第 41 部分):启动第二阶段(二)
如果到目前为止,你觉得一切都很好,那就说明你在开始开发应用程序时,并没有真正考虑到长远的问题。随着时间的推移,你将不再需要为新的应用程序编程,只需让它们协同工作即可。让我们看看如何完成鼠标指标的组装。
构建和测试 Aroon 交易系统 构建和测试 Aroon 交易系统
在本文中,我们将学习在了解了 Aroon 指标(阿隆指标)的基础知识和基于该指标构建交易系统的必要步骤之后,如何构建 Aroon 交易系统。建立这个交易系统后,我们将对其进行测试,看看它是否能盈利,还是需要进一步优化。
开发多币种 EA 交易(第 1 部分):多种交易策略的协作 开发多币种 EA 交易(第 1 部分):多种交易策略的协作
交易策略是多种多样的,因此,或许可以采用几种策略并行运作,以分散风险,提高交易结果的稳定性。但是,如果每个策略都作为单独的 EA 交易来实现,那么在一个交易账户上管理它们的工作就会变得更加困难。为了解决这个问题,在一个 EA 中实现不同交易策略的操作是合理的。
种群优化算法:改变概率分布的形状和位移,并基于智能头足类生物(SC)进行测试 种群优化算法:改变概率分布的形状和位移,并基于智能头足类生物(SC)进行测试
本文研究了改变概率分布形状对优化算法性能的影响。我们将进行的实验,会用到智能头足类生物(SC)测试算法,从而评估优化问题背景下各种概率分布的效能。