English Русский Español Deutsch 日本語 Português
preview
从基础到中级:事件(一)

从基础到中级:事件(一)

MetaTrader 5示例 |
31 0
CODE X
CODE X

概述

在上一篇文章,从基础到中级:结构(二)中,我们讨论了基本结构以及如何使用它们将值传递给函数和过程。尽管结构这一主题尚未得到深入探讨,但我认为现在还不是深入研究结构中存在和可能实现的某些方面的合适时机。这是因为,据我所知,在 MQL5 中,我们真的不需要在很高的层次上进行编程。请不要误会。

我的意思是,只需很少的知识,但有了发达的概念基础,你就可以创建和实现 MetaTrader 5 中使用的几乎任何类型的应用程序。而且,由于 95% 的时间(甚至更多)你实际上只会开发简单的应用程序,所以我认为目前没有必要深入探讨一些更复杂的细节。

因此,我将暂停一下与基本编程概念相关的解释。我们将开始了解如何使用纯 MQL5 实现 MetaTrader 5 应用程序。

由于本文旨在解释在 MQL5 中实现代码的这种方式所涉及的基础,我们将从最基本的实现开始。但在此之前,我们需要讨论另一个话题。


事件的基本概念

在这里,我不会赘述太多细节,因为讨论这个主题的标题所指的内容很容易就能写成一本完整的编程书籍 —— 我指的是字面意义上的。这是一个极其广泛的话题,包含许多细微差别。然而,在我们真正开始讨论如何实现将在 MetaTrader 5 中使用的应用程序之前,我们需要稍微调整一下我们的理解。

实际上,每个图形应用程序都使用事件。一般来说,生成特定事件的时刻并不取决于我们正在实现什么。在某些特殊情况下,我们可以从自己的应用程序中触发事件。然而,由于这些是特殊情况,您绝不应该将其视为控制代码的一种方式。事实上,在给定环境中实现某种代码的程序员通常不是传统意义上的“编程”。他们是在定义他们的代码应该如何响应每个到达的事件。

在一个应用程序中,某些事件可能会被忽略,而在另一个应用程式中,它们可能会优先于任何其他活动。对于许多初学者来说,理解这一点非常复杂和困难,因为到目前为止,所有展示的代码都不是为了处理事件而设计的。它只是执行了它被编程要做的事情。

然而,当我们在 MetaTrader 5 中编写指标甚至 EA 交易系统时,我们根本不关心事情是否真的发生了,这听起来可能很奇怪。我们真正感兴趣的是定义指标或 EA 交易应该如何对给定事件做出反应。

例如,当您使用Gimp这样的程序时,它允许您在窗口中绘图,您可能会认为该程序正在以某种方式工作。但从本质上讲,它只是对您正在生成的事件做出响应,例如鼠标点击、按键或拖动鼠标。这些都是事件。程序不知道,也不关心你在做什么。它只是等待事件发生并做出响应。最终,你会得到一张经过处理和绘制的图像。

MetaTrader 5 也是这样做的。它并不关心用户在做什么,它只是等待接收某种事件。当事件发生时,它会以某种方式做出反应。

但请稍等片刻,那么,究竟是谁负责制造这些事件呢?简单来说:就是你。然而,决定哪个图形应用程序将接收哪个事件的责任在于操作系统。作为用户,您无法决定这一点。您只需告诉操作系统,给定的应用程序应该执行某个操作,例如最小化、最大化,或者当您请求关闭应用程序时应该发生什么。

与许多人的想法相反,真正决定应用程序如何关闭的实体不是操作系统,而是应用程序本身。这就是为什么当您键入文本并请求关闭编辑窗口时,编辑器可能会询问您是否要保存更改。如果操作系统必须管理这一点,事情会复杂得多。最终,决定如何响应事件的是应用程序本身,而不是操作系统。因此,在我看来,声称一个系统比另一个系统好,是对事物实际运作方式的缺乏理解。

好的,尽管 MetaTrader 5 可以在 Windows 以外的操作系统上运行,但我们假设它仍然在 Windows 上运行。即使您使用的是 MetaTrader 5 终端的网页端版本,为了简化说明,我们将以 Windows 系统为基础。

为什么要提出这个概念?原因是,如果您有兴趣学习如何为 MetaTrader 5 编程,您需要了解,在 MetaTrader 5 中运行的任何应用程序都不会直接响应来自 Windows 的事件,而是响应来自 MetaTrader 5 本身的事件。再次强调:在某些特定情况下,MetaTrader 5 中运行的应用程序实际上会直接响应 Windows。但这是非常具体的,需要非常高级的编程水平,目前还不适用。

既然已经提出了这些观点,我们也有了一定的理解,我们可以把提到的一切都放进一个图像中。这就得到了下图所示的图像。

图 01

这张图片非常具有象征意义,因为它显示了从用户可能正在生成的事件到 MetaTrader 5 中运行的应用程序收到相同事件的路径。请注意,虽然我们将某个应用程序称为 EA 交易,但它实际上可以是任何可以在 MetaTrader 5 中执行的应用程序。然而,由于 EA 交易能够对大多数事件做出响应,我便用它来指示我们的应用程序在整个场景中的位置。

这是最容易的部分;现在到了有趣的部分。事件有两种类型:同步和异步。同步事件可能是每 x 个时间段发生一次的类型,因此以某种方式与系统时钟相关联。此类事件的一个例子是图表上出现新的柱形。它总是会不时发生。然而,我们也可以有源自异步事件的同步事件,这使得一些事情非常有趣。但在我们讨论源自异步事件的同步事件之前,我们需要了解异步事件是什么。

异步事件是指随机生成的任何事件。例如,按下键盘、移动鼠标,甚至打开或关闭一个仓位。在这种特定情况下,当价格达到某个特定水平时,就会开仓或平仓。简而言之,异步事件是指其发生时间和顺序无法预测的任何事件。

然而,正如前文所述,我们也可以基于异步事件来处理同步事件。例如,当您在 MetaTrader 5 图表上选择某个对象,然后拖动或删除该对象时,就会出现这种情况。每个事件发生的确切时刻并不重要,但它们是按照特定的顺序发生的。这就是为什么许多人在编写某些类型的应用程序时遇到困难。可能需要对源自异步事件的同步事件做出响应。总之,你现在不必担心这件事。随着文章的深入,我们将了解如何处理这种情况。


实践中的首次事件

我必须承认,这是一个有点挑战性的时刻。原因是我想向你们展示事件是如何发生的。与此同时,我不想创造一些没有多大意义的东西。因此,我们需要采取一些易于理解但同时又不言自明的方法。这样,我们就不会花太多时间试图解释事件是如何发生的以及如何处理的。对于那些了解事件的人来说,这种话题非常直观,但对于那些不了解事件的人们来说,它可能会非常令人困惑。因此,让我们首先尝试在实践中捕捉和处理事件。

在开始之前,我们需要了解 MQL5 中异常存在的一件事:不同类型应用程序之间的划分。基本上,MQL5 中有两种类型的应用程序:可以响应用户事件的应用程序和不能响应用户事件的应用程序,至少不能直接响应。这很简单。能够捕捉并响应用户交互事件的工具包括指标和 EA 交易。另一方面,那些无法响应用户事件的是脚本和服务,因为它们无法捕获此类事件。了解这一点非常重要,这样你才能选择最佳的替代方案。

到目前为止,我们基本上只使用脚本。在脚本中,我们只有一个入口点,即 OnStart 过程。从那时起,作为程序员,一切都必须由您生成和控制。但在目前的情况下,我们想捕捉事件,无论它们是什么。因此,我们需要了解 MetaTrader 5 中允许的每种模型能为我们提供什么。

如果您正在阅读这篇文章,那么您肯定已经使用默认附带的应用程序对 MetaTrader 5 进行了实验(至少是部分实验)。在这种情况下,您一定已经注意到,我们可以在同一张图表上显示多个指标,但是,只能有一个 EA 交易。因此,根据您的目标,我们最终可能会在指标或 EA 交易中实现某些功能。

然而,即使在开始实现代码本身之前,也需要了解一个小细节。这一细节指的是指标和 EA 交易中所存在的局限性。是的,它也有局限性。

例如,指标无法与订单系统建立联系。这是 EA 交易独有的功能。另一方面,EA 交易至少不能以简单的方式绘制线条和执行旨在指示图表上某些内容的计算。原则上,EA 交易无法访问 MQL5 库函数来绘制图表。这些是指标特有的。因此,了解如何使用这些模型非常重要。这一点将在本文中逐步体现。

好吧,你可能会想:“但是,我们无法创建真正有用的东西,因为存在一些限制,阻止我们创建真正独特的应用程序。”事实上,亲爱的读者,MetaTrader 5 被设计成一个非常稳定和安全的平台。因此,有一些机制允许不同应用程序之间的通信,从而使我们有可能创建真正有趣且易于长期维护的东西。

鉴于这是对该主题的第一次简要介绍,让我们首先看看事件是如何被捕捉到的。目前,我们暂且不考虑如何处理它们,只考虑如何捕捉它们。为此,我们将使用一个指标。下面可以看到它最简的代码形式。

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. int OnInit()
05. {
06.    return INIT_SUCCEEDED;
07. };
08. //+------------------------------------------------------------------+
09. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
10. {
11.    return rates_total;
12. };
13. //+------------------------------------------------------------------+

代码 01

我们从最少的代码开始,使事件的概念更容易理解。简单地复制代码而不真正理解为什么需要以这种方式实现是没有意义的。

当这个指标被放在图表上时,一些事情会按照一定的逻辑顺序发生。如果你想开始创建自己的代码,了解这一点非常重要。在此代码 01 中,捕获了两个事件。MetaTrader 5 与这些事件的协同运作方式将决定我们应该如何处理它们。我知道一开始这可能有点令人困惑。然而,我们可以让我们的应用程序进行交互,并做开发人员在考虑如何实现 MetaTrader 5 时无法想象的事情。但要实现这一点,我们需要了解每个事件将如何以及何时触发。

当然,由于我们仍处于早期阶段,并且只专注于教学,我不会展示如何 “强迫” MetaTrader 5 执行许多人认为不可能的事情。但是,如果你认真学习和练习,你将能够使用 MQL5 和 MetaTrader 5 做几乎任何事情:例如视频或图像编辑器。MetaTrader 5 的主要目的是让我们买卖在电子市场交易的资产,因此它并不适用于这些用途。

非常好,现在我们可以将图 01 放大,以显示代码 01,这是一个指标。由此,我们得到以下消息流。

图 02

图 02 显示了图 01 的延续。我们在图 02 中看到的是在处理事件的最后一点内部会发生什么。现在,由于在这里我们将按照一定的方向处理事情,因此您可能会对我在这些文章中不会涉及的其他类型的事件有更多的了解。

为此,我建议您查看 MQL5 文档中的 “ 事件处理函数”部分。在那里,您将更详细地了解 MQL5 存在和涵盖了多少事件,每个事件都有自己的目的和目标。

但回到我们的问题,让我们了解一下代码 01 中看到的每个事件何时会被触发。为了理解这一点,我们需要做的就是添加一个我们在前几篇文章中已经使用过的小命令。因此,原来的代码 01 现在将如下所示:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. int OnInit()
05. {
06.    Print(__FUNCTION__);
07. 
08.    return INIT_SUCCEEDED;
09. };
10. //+------------------------------------------------------------------+
11. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
12. {
13.    Print(__FUNCTION__);
14. 
15.    return rates_total;
16. };
17. //+------------------------------------------------------------------+

代码 02

好的,现在我们知道幕后发生了什么。但看到这个代码 02,你可能会想:"但是这段代码完全没有任何用处。为什么要创建这样的代码?"好吧,亲爱的读者,事实上,这段代码对我们想要做的事情非常有用。请注意以下几点:此代码只会在终端中打印一条消息,指示正在捕获哪个事件。这是在第 06 行和第 13 行完成的。但是,看看当我们把它放在任何图表上会发生什么。

动画 01

在动画 01 中,我们可以看到 MetaTrader 5 何时触发了由代码 02 中显示的指标捕获的事件。亲爱的读者,这正是您所注意到的。尽管我们的简单代码显得毫无用处,但它能够向我们展示 MetaTrader 5 触发的事件是如何导致我们编程的事情发生的。在这种情况下,我们通过在终端打印消息来处理捕获的事件 —— 乍一看,这似乎不会发生。注意触发事件的顺序。还要注意的是,第 13 行打印了两次:第一次是在指标被放置在图表上时,第二次是在交易品种价格发生变化时。

理解这一点至关重要,对我们大有裨益。由于我们正在处理来自 MetaTrader 5 的事件,这意味着我们在使用指标和 EA 交易时需要改变代码的实现方式,因为这些指标和 EA 交易是唯一对 MetaTrader 5 触发的事件做出响应的工具。

非常好,现在让我们对代码 02 进行另一次更改,以便添加另一个事件处理程序。下面可以看到要执行的代码:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. int OnInit()
05. {
06.    Print(__FUNCTION__);
07. 
08.    return INIT_SUCCEEDED;
09. };
10. //+------------------------------------------------------------------+
11. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
12. {
13.    Print(__FUNCTION__);
14. 
15.    return rates_total;
16. };
17. //+------------------------------------------------------------------+
18. void OnDeinit(const int reason)
19. {
20.    Print(__FUNCTION__);
21. };
22. //+------------------------------------------------------------------+

代码 03

我们再次有了一段非常简单的代码,它显然没有任何实际作用。但是,当我们执行它时,看看会发生什么。

动画 02

就像在第一个例子中,我们试图观察我们的应用程序如何响应 MetaTrader 5 触发的事件一样,这里我们有另一种类型的捕获。我希望你们明白以下几点:图表上显示了该指标,并且该图表使用的是给定的时间周期。一旦我们改变时间周期,就会触发一个事件,该事件又会被代码 03 中显示的指标捕获。

现在到了我们感兴趣的部分。当图表上出现代码 02 所示的指标时,如果时间周期发生变化,我们就丢失了这个事件。更确切地说,我们忽略了 MetaTrader 5 会触发该问题的事实,而只是简单地说了以下这些话:

“听着,MetaTrader 5,只要能保证一切正常运行,你什么都行,我没意见。”

MetaTrader 5 触发了该事件,但代码 02 忽略了它。因此,我们的印象是,该指标只会知道在这种情况下如何表现。然而,这并不总是我们真正想要发生的事情。在这种情况下,我们可以使用代码 03 中第 18 行所示的过程序来捕获此事件,从而采取适当的措施,以防止发生其他意外事件。

我知道你们中的许多人,尤其是那些已经熟悉 MQL5 编程的人,可能会认为我们不需要担心指标中发生的许多事件。然而,在某些情况下,如果你只是忽略某些事件,你可能会遇到 MetaTrader 5 报告的奇怪故障。如果您的应用程序没有采取适当的措施,MetaTrader 5 将采取一切必要措施,确保代码中的问题不会干扰平台的整体功能。

尽管许多人认为指标几乎不可能有错误,但错误确实会发生。当这种情况发生时,MetaTrader 5 会触发一个我们的代码忽略的事件,我们可能会出现平台性能问题,例如速度减慢甚至冻结。但这不是 MetaTrader 5 的错,而是您的应用程序对触发的事件没有正确响应。

为了演示这种情况,让我们实现一段非常简单的代码,其目标非常简单但又非常有趣:告诉我们在给定的时间段内 OnCalculate 事件触发了多少次。这段代码如下所示:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. uint gl_Counter;
05. //+------------------------------------------------------------------+
06. int OnInit()
07. {
08.    gl_Counter = 0;
09. 
10.    Print(__FUNCTION__);
11. 
12.    return INIT_SUCCEEDED;
13. };
14. //+------------------------------------------------------------------+
15. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
16. {
17.    if (!gl_Counter)
18.    {
19.       uint arr[];
20. 
21.       if (FileLoad(_Symbol, arr) > 0)
22.          gl_Counter = arr[0];
23.    }
24.    
25.    Print(__FUNCTION__, " :: ", ++gl_Counter);
26. 
27.    return rates_total;
28. };
29. //+------------------------------------------------------------------+
30. void OnDeinit(const int reason)
31. {
32.    uint arr[1];
33. 
34.    arr[0] = gl_Counter;
35.    FileSave(_Symbol, arr);
36. 
37.    Print(__FUNCTION__);
38. };
39. //+------------------------------------------------------------------+

代码 04

这段代码中没有我们没有看到和讨论过的内容,因此任何研究和实践这些文章中所示内容的人都可以理解。或许唯一可能引起一些疑问的部分是与 FileLoadFileSave 函数相关的部分。由于官方文档中已经解释得非常清楚,本文不再赘述。

但是,执行此代码 04 时,将生成类似于下面显示的内容。

动画 03

请注意,尽管改变了时间周期,计数器仍然继续递增。这是因为在代码 04 的第 30 行,我们捕获了 MetaTrader 5 在我们的代码需要执行某些操作时触发的事件。在这种情况下,我们不会处理导致 MetaTrader 5 触发事件的任何原因。我们只是告诉 MetaTrader 5 将某个变量保存到磁盘。我们可以通过许多其他方式做到这一点,我们很快就会讨论。但我们的目标是表明,在某些情况下我们可能需要处理一个事件,而在其他情况下,我们可以忽略同一事件。一切都取决于我们想要实现什么,以及我们打算如何实现我们的最终目标。

请注意,如果第 30 行的过程不存在,或者我们没有正确处理它,则每次我们更改时间周期时,计数器都会从零开始重新开始。这是由于第 8 行代码所致,该行代码开始统计 OnCalculate 执行的次数。这段代码很有意思,不是吗?虽然很简单,但它能帮助我们理解与 MetaTrader 5 生成的事件相关的几个问题。


最后的探讨

在这篇文章中,我们开始获得更多乐趣。这是我们将探讨事件处理的一系列文章中的第一篇。我将尝试以一种有趣、简单和说教的方式来处理这个话题,向您展示我们不应该总是使用另一个程序员提出的解决方案。这是因为,在很多情况下,一个人提出的解决方案可能并非你当下最理想的解决方案。

我知道许多人可能渴望了解下一篇文章将涵盖的内容。但在尝试深入研究将变得越来越复杂的事情之前,请尝试练习和研究本文所涵盖的内容。我保证,努力练习并尝试理解这里介绍的内容是值得的。作为帮助您思考可以练习什么的提示和激励,请尝试修改下面附带的代码 04,以便仅在发生时间周期更改事件时存储计数器值。对于 MetaTrader 5 可能触发的任何其他类型的事件,计数器应再次重置为零。

在下一篇文章中,我将展示如何以一种非常安全和简单的方式做到这一点。所以赶紧开始学习吧,祝你学习愉快。

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

附加的文件 |
Anexo.zip (1.14 KB)
MQL5 简介(第 18 部分):沃尔夫波浪形态简介 MQL5 简介(第 18 部分):沃尔夫波浪形态简介
本文详细解释了沃尔夫波浪形态,涵盖了看跌和看涨两种变体。它还分解了用于基于这种高级图表形态识别有效买卖设置的分步逻辑。
数据科学和机器学习(第 35 部分):MQL5 中的 NumPy — 用更少代码制作复杂算法的艺术 数据科学和机器学习(第 35 部分):MQL5 中的 NumPy — 用更少代码制作复杂算法的艺术
NumPy 库几乎为所有 Python 语言编程的机器学习算法提供核心动力,在本文中我们即将实现一个类似的模块,其收集了所有复杂的代码,辅助我们构建各种类的复杂模型和算法。
新手在交易中的10个基本错误 新手在交易中的10个基本错误
新手在交易中会犯的10个基本错误: 在市场刚开始时交易, 获利时不适当地仓促, 在损失的时候追加投资, 从最好的仓位开始平仓, 翻本心理, 最优越的仓位, 用永远买进的规则进行交易, 在第一天就平掉获利的仓位,当发出建一个相反的仓位警示时平仓, 犹豫。
市场模拟(第八部分):套接字(二) 市场模拟(第八部分):套接字(二)
用套接字实现一些实用功能怎么样?在今天的文章中,我们将开始创建一个迷你聊天室。让我们一起来看看这是怎么做到的 —— 这会非常有趣。请注意,此处提供的代码仅用于教育目的。它不应用于商业目的或现成的应用程序,因为它不提供数据传输安全性,并且可以访问通过套接字传输的内容。