开发回放系统(第 37 部分):铺平道路 (一)
概述
在本文中,我们将讨论一个非常重要的问题。我建议您尽可能集中精力理解本文的内容。我指的不仅仅是阅读,我想强调的是,如果你不理解这篇文章,你可能就是完全放弃了理解以后文章内容的希望。
我在这篇文章中要展示、解释和讲述的一切,对你在至少最低程度上理解以后的文章至关重要。
这不是玩笑。因为我觉得很多人都还不知道我们的能力。我们使用纯 MQL5 语言来使得不同元素在 MetaTrader 5 中以某种方式运行。
一直以来,我都在撰写文章,向亲爱的读者解释如何在 MetaTrader 5 中进行某些操作。我一直在推迟谈论某个话题,绕来绕去,试图谈谈我在程序中已经做了很久的事情。
这些内容使我能够以这样一种方式调整我的系统,即我通常需要更多地修改源代码,而不是仅使用 MQL5 来显示它。
你们一直以来看到的所有代码实际上都是修改过的代码。它们与我最初使用的完全不同,但现在我正处于十字路口,不知道还能做什么。要么我显示原始代码,要么显示非常接近我实际使用的代码,否则我将无法继续回放/模拟系统项目。
问题甚至不在于编写代码本身。在我看来,要做到这一点非常简单。问题是,我想给你必要的知识,让你不仅能使用系统,还能修改它。这一点很有必要,这样你就可以让它按照自己的需要运行,从而进行自己的分析。
我希望每个人都能在没有外界帮助的情况下,完全学会自己工作。我希望这一点是清楚的。
本文将展示我们如何使用指标和一些 EA 交易来实现非常具体的行为和非常模块化的编程,甚至创建某种复杂的系统。但问题是:我们要编译的东西越少越好。
回放/模拟系统目前包含 3 个可执行文件:EA 交易、服务和指标。然而,这使我们能够开发订单系统。请记住,这样的系统将取代交易服务器,然后我们就可以在模拟或真实账户上进行交易分析。
我们仍需将过去的一些东西引入回放/模拟系统。例如 Chart Trader。因此,我们需要某种系统,但它必须稳定且易于使用。尽管我们在从零开始开发 EA 交易(第 30 部分):把图表交易作为指标?!这篇文章中做了介绍,这不适合我们。
我们需要以更加透明的方式行事,并且不会给我们带来任何不便。
让我们用一个更简单的例子来说明我们可以做得更好。如果我们直奔所需的应用,我几乎可以绝对肯定,即使有,也只有很少人能跟上我们正在讨论的内容。但我不希望这样的事情发生。那么,现在让我们来看看如何继续。准备好进入我的宇宙吧。
开始构建指标
对于不了解 MetaTrader 5 运行方式的人来说,我们要做的事情非常困难。我不会在最后添加任何附件。我希望你们能听懂我的解释,这样你们就能体验到你们在这里看到的一切,并详细了解一切是如何运作的。
让我们先创建一个指标。然后打开 MetaEditor 并执行以下操作:
图 01 - 选择要创建的内容。
图 02 - 创建目录。
图 03 - 给指标命名。
图 04 - 保持简单。
如图 04 所示,无论我们要制作什么类型的指标,我们都使用相同的设置。现在图 04 中这样设置,我们都还可以在以后根据需要添加或更改事件管理功能。所以不用担心,你可以像往常一样继续创建指标。
图 05 - 点击 "完成
因此,您将看到如图 05 所示的内容。此时,我们只需点击 "完成 "按钮。
我们的想法是创建一个指标,放在零号窗口中。不过,同样的概念也可用于在任何窗口中放置指标。不过,还是那句话,别担心,注意文章的主旨。
过程的最后,您将在 MetaEditor 窗口中看到以下代码:
#property copyright "Daniel Jose" #property link "" #property version "1.00" #property indicator_chart_window //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- indicator buffers mapping //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) { //--- //--- return value of prev_calculated for next call return(rates_total); } //+------------------------------------------------------------------+
我知道很多人喜欢使用 MetaEditor 的本地格式。这是可以理解的,但对我来说,这种格式用起来不太顺手。但这是每个人的个人习惯,真正重要的是,我们可以轻松阅读并理解我们的代码。
所以,让我们开始修改这些标准代码,使其符合我们的需要吧。
如果您尝试编译该标准代码,将会收到一个警告信息。
图 06 - 编译器输出。
虽然图 06 显示代码已经编译,但它并没有完全正确地生成。许多程序员会忽视编译器发出的错误警告。在红色箭头处可以看出这一点。
发生错误,即使是一个看似非关键的错误,也会给代码带来风险。因此,如果编译器报告代码有缺陷,在任何情况下都不能使用它,永远不要这样做。
要解决这个问题,让我们做一件非常简单的事:告诉编译器,我们知道使用该指标的作用。
#property copyright "Daniel Jose" #property link "" #property version "1.00" #property indicator_chart_window #property indicator_plots 0 //+------------------------------------------------------------------+ int OnInit() { return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) { return rates_total; } //+------------------------------------------------------------------+
通过添加突出显示的一行代码,我们让编译器知道我们知道自己在做什么。结果如图 07 所示。
图 07 - 理想编译的结果。
无论何时编译代码,都要注意编译器告诉你的情况是否与图 07 相同。这是获得完美编译代码而不出现关键问题的唯一方法。
你可能认为你已经知道了这一切。好吧,但我想说清楚。我不希望任何人抱怨系统因为某些修改而不起作用或以错误的方式运行。我鼓励大家根据自己的需要修改和调整该系统。而要做到这一点,你首先需要了解基本知识,拥有扎实、完善的知识和思想基础。
现在我们有了一个基本指标,让我们创建一个基本的 EA 交易。
开始构建 EA 交易
我们将建立一个 EA 来实现我们的需求。让我们一步一步来。
图 08 - 我们要构建的内容。
图 09 - 确定要使用的目录。
图 10 - 确定可执行文件的名称。
正如你所看到的,我们所遵循的步骤与指标阶段相同,唯一不同的是我们在创建开始时所做的选择。
图 11 - 保留默认设置。
图 12 - 点击完成。
完成所有这些步骤后,一个文件就会出现在 MetaEditor 中,如下图所示:
#property copyright "Daniel Jose" #property link "" #property version "1.00" //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- } //+------------------------------------------------------------------+
与指标一样,我也会在这里做一些格式上的改动。但与指标不同的是,当我们要求 MetaEditor 编译这段代码时,会从编译器收到以下信息。
图 13 - 编译请求的结果。
这意味着什么?冷静下来,亲爱的读者,不要担心。你很快就会明白我们要做什么。
现在,我们已经有了指标和 EA 交易的基本系统,让我们开始本文的工作:关联它们。
重要的是要知道如何创建一个指标或如何创建一个 EA 交易,以及如何使它们互动和协同工作。很多人都知道使用指标的基础知识,这样 EA 交易就可以使用指标计算出的数据。
如果您不知道如何操作,我建议您先学习一下。这篇文章就是一个很好的起点:从零开始开发 EA 交易(第 10 部分):访问自定义指标。它非常简单地解释了如何访问指标计算出的数值。同时,我们还将研究如何以一种非常简单的方式初始化指标,甚至在我们可以访问根据我们将要做的事情而计算出来的任何信息之前。
所有这一切都非常漂亮和简单,因为利用图 14 中的思路,你可以读取指标的内容。现在,您可以使用图 15,通过使用 EA 交易而创建一个指标。
图 14 - EA 读取指标数据。
图 15 - 通过 EA 创建指标。
尽管如此,当我们需要做类似使用 Chart Trader 所做的操作时,这种方法就不起作用了。当我们将 Chart Trader 变成一个指标时,我们不得不使用另一种方法。即使在那个时候,我就已经提到,这种情况今后会有所改变。在那篇文章中,我们采用了图 16 所示的方案。
图 16 - 双向通信
当需要在各方之间,或者说在不同流程之间传输信息时,我们需要一定的手段来实现。在 MetaTrader 5 中,我们可以使用全局终端变量。同样的概念早已用于回放/模拟系统。这就确保了服务能以某种方式与指标进行交互。
还有其他方法可以促进同样的通信,但我不想使用无法充分利用 MQL5 和 MetaTrader 5 所有优势的技术。当我们充分利用平台及其语言时,我们就能从未来的任何改进中获益。如果我们开始发明不能充分利用所提供功能的解决方案,那么如果 MQL5 甚至 MetaTrader 5 有所改进,我们又如何受益呢?
虽然图 16 所示的系统非常适合许多情况,但它并不能带来太多好处。
要理解这一点,让我们试着理解以下事实:我们应该在图表上放置多少个指标才能在回放/模拟系统中获得良好体验?一个?两个?五个?事实是,我们无法真正知道这一点。而我们已经知道的是:我们至少需要两个指标,并且这只是现在,而不是以后。我们现在就需要它们。我们在回放/模拟系统中至少需要两个指标。
你可能想知道为什么是两个。第一个指标用于控制回放/模拟。我们已经有了这个指标。现在我们需要另一个:Chart Trader。这样我们就可以直接下订单了。我们需要 Chart Trader,因为在回放/模拟中,我们不需要上角的按钮(图 17)。
图 17 - 快速交易按钮。
我们确实需要一种下订单的方式,但无论我们使用的是模拟账户还是真实账户,下订单的方式都应该是一样的。我们应该用同样的方法与平台互动。
虽然图 16 实现了 EA 与指标之间的互动以发送订单,但这并不是最好的方法。因为在这种情况下,我们需要保留全局变量,而这些变量最好是用于其他目的。我们可以采取完全不同的方式来建立我们所需要的通信。
在此基础上,我们开始采用一种新的方法,如图 18 所示。
图 18 - 新的通信协议。
看到这个图,你可能会觉得我疯了。如何让 EA 和指标直接通信?这个故事的重点在哪里?不,这不是玩笑,不,我也没疯。事实上,我们有办法直接进行这种沟通。从图 14 和图 15 中可以看出这一点。但是,除了这些手段之外,我们还可以利用另外一种手段。我们真正需要的是了解如何以最好的方式利用这些手段。
在我继续之前,请允许我问你:您是否测试或尝试过以不同于其他人的方式使用 MetaTrader 5?您是否使用 MQL5 进行过实验,以至于您说某些事情可以用这种方式完成?如果这两个问题的答案都是 "否",我建议你看一看,看看这里有多少秘密。
初始化进程之间的通信
正如您从前面的主题中看到的,我们将在指标和 EA 交易之间建立一种通信方式,以便除特殊情况外不使用全局终端变量。这将促进高效的信息交流。
想法很简单,概念更简单。但是,如果不了解一些细节,就不会成功。正确理解我接下来要向你们解释的内容非常重要。不要以为您已经知道如何使用 MQL5 编程就足够了。我要解释的内容超出了 MetaTrader 5 和 MQL5 语言的正常和熟悉使用范围。
我们应该从某些事情入手 - 这就像制定一项协议。不管你怎么想,只要沿着这条路走下去,就一定会有结果,而偏离则会失败。
在指标代码中,我们首先添加第一行,如下所示。
1. #property copyright "Daniel Jose" 2. #property link "" 3. #property version "1.00" 4. #property indicator_chart_window 5. #property indicator_plots 0 6. //+------------------------------------------------------------------+ 7. #define def_ShortName "SWAP MSG" 8. #define def_ShortNameTmp def_ShortName + "_Tmp" 9. //+------------------------------------------------------------------+ 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. 26. return INIT_SUCCEEDED; 27. } 28. //+------------------------------------------------------------------+ 29. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 30. { 31. return rates_total; 32. } 33. //+------------------------------------------------------------------+
看起来一团糟?但这里不会造成混淆。此时,我会强制 MetaTrader 5 确保图表上只有一个指标。我该怎么做?简单来说,我检查指标是否在图表上。
如果需要,您可以使用上述代码强制 MetaTrader 5 在图表上只保留一个指标。但要了解其工作原理,就需要将其分解,列出并解释关键代码行,以便更容易理解整个解释。
在第 7 行,我们定义了指标的名称。只要指标还在图表上,该名称就会保存在 MetaTrader 5 中。
要确定这个名称,我们使用第 24 行中的方法。在这里,我们设置了指标将使用的名称。我们定义第 8 行的原因与 MetaTrader 5 的工作方式有关。
MetaTrader 5 是一个基于事件的平台。这意味着当发生任何事件时,无论是价格变动、图表上的时间变化、鼠标移动、按键或从图表中添加或删除某些内容,MetaTrader 5 都会触发某种类型的事件。每一类事件都有其目的和后果。
MetaTrader 5 触发更新事件时,必须以某种方式更新图表上的所有对象、指标、EA 交易和其他元素。如果是脚本,只需从图表中重置即可;如果是指标和 EA,则需要调用新的 OnInit 函数。如果发生意外,需要从头开始更新指标,MetaTrader 5 将强制调用第 14 行。有什么问题呢?出现这个问题的原因是,我们需要指标有一个 MetaTrader 5 能识别的简短名称,否则就无法执行第 18 行所示的检查。
你可能认为我们可以把第 24 行移到第 18 行之前,但问题就出在这里。如果我们在第 18 行之前添加第 24 行,当第 18 行标记的指标已经出现在图表上时,我们将得到一个确定结果。这会导致 MetaTrader 5 从图表中重置指标,而实际上我们希望的是在执行第 20 行时保留指标。但我们只希望有一个实例出现。
希望你能听懂我的解释。由于上述原因,我们需要在第 8 行定义一个临时名称,在第 18 行检查指标是否已在图表上之前,我们先对指标的短名称稍作修改。我们使用第 17 行设置指标的临时名称。注意:该名称必须是唯一的,否则会出现问题 。
如果该指标已经出现在图表上,第 18 行将允许执行第 20 行,从而移除试图进入图表的指标。为了通知用户发生错误,我们在第 21 行在 MetaTrader 5 消息框中显示一条消息。指标将返回第 22 行,表示无法在图表上显示。
如果指标出现在图表上,第 24 行将更正指标名称,因此基本上无法在图表上放置新指标。但有一个 "漏洞 ",实际上并不是 MetaTrader 5 的漏洞。幸运的是,当我们在图表中添加新指标时,MetaTrader 5 可以区分不同的情况。为了理解这一点,第 10 行有一段代码。
如果用户在图表上放置指标时不修改变量值(变量在第 10 行声明),MetaTrader 5 将认为该指标与图表上的指标相同(如果图表上已经有一个指标在运行)。如果用户修改了数值,MetaTrader 5 会根据是同一指标还是添加新指标做出两种完全不同的决定。
- 第一种情况是没有指标的时候,它会被放置在图表上。
- 第二种情况是指标已经存在。在这种情况下,如果变量中指定的值与图表上已显示的指标不同,则 MetaTrader 5 会将其视为不同的指标。如果数值相同,则 MetaTrader 5 会将它们识别为同一个指标。
使用这种方法可以将图表上同名指标的数量限制为一个。如果需要,您还可以限制同名指标的最大数量。您只需修改第 18 行的检查条件即可。所以,可以对 MetaTrader 5 进行配置,使其接受 3 个同名指标。然而,如果用户试图放置第四个同名指标,MetaTrader 5 将禁止这样做。更确切地说,这将由指标代码完成,它将阻止放置第四个指标的尝试。
正如您所看到的,我们可以对系统进行自定义和限制,从而不允许在图表上重复显示指标。这一点非常重要,将在本系列后面的回放/模拟系统中详细讨论。
在这个指标的代码中有几个要点,我稍后会加以说明,这样在不使用全局终端变量的情况下,EA 与指标之间也可以进行清晰的互动。我说的是第 5 行和第 10 行。了解这种编程方式背后的逻辑非常重要。
为了了解实际情况,您可以创建上文详细描述的代码,并在 MetaTrader 5 平台上运行。
结论
本文向您展示了如何在 MetaTrader 5 平台上创建并执行代码,从而阻止或限制在图表上放置多个指标。您可以利用这些知识来简化许多其他任务,这将使您作为交易者和 MetaTrader 5 平台用户的生活更加轻松,为您提供更好的使用体验。
虽然从本文中可能看不出其中的原因,但所介绍的知识如果使用得当,将使我们能够做得更多,并避免因图表上出现重复甚至不必要的指标而带来的一系列问题。
对 MetaTrader 5 缺乏经验的用户在图表上多次放置相同指标的情况并不少见,这使得使用和配置这些指标变得非常麻烦。而这一切都可以用相对简单的代码来避免(正如你自己看到的那样)。它完全有能力完成设计的任务。
希望这些知识对你有用。在下一篇文章中,我们将探讨如何在 EA 交易和指标之间建立直接通信,这是进一步实施回放/模拟系统所必需的。
本文由MetaQuotes Ltd译自葡萄牙语
原文地址: https://www.mql5.com/pt/articles/11585