掌握 MQL5:从入门到精通(第一部分):开始编程

By yimfx1987, 30 June, 2024

不管怎样,还是有人想“了解一切”。这时,分步指南(从简单到复杂解释事情)可能会派上用场。哦,用户找不到 MQL5 语言的这种分步指南。

我所见过的那些手册都假设一个人已经具有一些编程经验,并且了解其工作原理。因此,那些指南会解释其中的细微差别。

而我则是专门为初学者撰写这一系列文章,适合那些初次接触编程并愿意学习的人。这些文章适用于那些不想再依赖程序员并希望了解一切如何运作的交易者。文章提供一致且详细的信息,并配有图片和示例,引导读者达到“熟练掌握 MQL5 语言”的水平。

该语言不仅可用于自动化交易,还可以解决其他相关的交易任务。例如,您可以使用它来绘制指标、优化结果和任何自定义形状,创建文件(例如分时报价列表或当前交易情况的屏幕截图),向 Telegram 和其他消息机器人发送消息,使用数据库等等。

我想逐步展示该语言的最大能力:使用“虚拟交易品种”、使用数学开发库进行操作、等等。这次我们从基础开始。

基本技术术语
 

几乎所有现代计算机都是基于冯·诺依曼架构构建的。为了能够成功地阅读文献,您需要了解程序员使用的术语。
 

  • 中央处理单元 (或 CPU)。该模块负责所有计算和逻辑。
  • 随机存取存储器RAM,内存)。RAM 存储数据和程序。当您关闭计算机或关闭程序时,RAM 中的数据将被删除。
     
  • 外部驱动器 (闪存驱动器、HDD、SSD 等)。CPU 可以将数据以文件形式写入它们。
     
  • 文件是指写在外部媒体(而不是 RAM)上的信息。
  • 执行线程。在运行过程中,CPU 从 RAM 获取数据,“理解”它是命令还是数据,执行命令并将结果写回到 RAM 或正在运行的程序所指示的外部驱动器。然后它再读取下一个内存片段,依此类推,直到程序结束。这整个过程被称为“执行线程”。
     

有多种衡量信息的标准。

  • 位(Bit)是信息的最基本单位。实际上,它是一个决定特定、最小内存块状态的“开关”。一个位包含“0”(禁用)或“1”(启用)。
  • 字节(Byte) 可以解释为“音节”。它是等于8个位的最小信息“包”。在计算机发明早期,计算机很大并占据了整个楼层,而 RAM 的容量很小,并且文件存储在 打孔卡上,8 位是速度(最多可以同时读取 8 个孔!)和媒体大小之间最方便的技术折衷。
     

对于程序员来说,了解程序占用多少空间有时很重要。在现代世界中,程序(尤其是其中的数据)通常占用大量空间,因此在“字节”一词中添加了度量前缀。

当测量物理量而非信息量时,每个这样的前缀都会把之前大小的增加 1000 倍。例如,一千米等于 1000 米,一兆伏等于 1000 千伏或 1,000,000 伏。
 

然而,当我们谈论计算机数据时,每下一个度量前缀都比前一个大 1024 倍

例如, 千字节 (KB) 等于 1024 字节兆字节 (MB) 包含 1024 千字节),等等。其他前缀有:gigaterapeta等。
 

我们将在后续文章中处理内存管理、处理器命令、使用 MQL5 写入文件和其他常规事项。

文章中经常会出现使用粗体表示的术语,我在这里一开始就对其进行描述。而其他术语将在第一次遇到的地方进行介绍。我不能没有术语,但我会尝试尽量少用它们。
 


 

开始使用 MQL5 编程所需的软件

为了使计算机对人类有用,您需要为每种类型的活动编写一个程序:交易、浏览图片、听音乐等。

虽然完全可以直接在处理器命令中编写程序,但这样的效率很低

因此,在现代世界中,有许多辅助程序可以帮助将接近人类语言的文本转换为处理器可以理解的命令。

以下是程序员所需的此类程序的最低限度列表:
 

  1. 文本编辑器。您可以在这个软件中编写代码。可以是任何你喜欢的编辑器。但是,它不应该在您输入的文本中添加任何额外的字符。当编辑器可以突出显示您需要的语言(在本例中为 MQL5)的语法时,这会非常方便。

    当您安装 MetaTrader 时,一个名为 MetaEditor 的非常强大的编辑器会自动安装在系统中。它可以满足您的一切需求。

    但是,如果由于某种原因您不喜欢它,您可以自由选择其他任何软件。例如,程序员使用的非常流行的编辑器之一是 Visual Studio Code (VSCode)。使用插件,您可以将其配置为与 MQL5 一起工作。

  2. 编译器。编译器将程序员在编辑器中编写的文本转换为机器代码。只有经过转换之后,CPU才能执行这段代码。

    对于 MQL5 语言,编译器内置于 MetaEditor 中。对于其他语言,例如 C++,编译器可能是一个单独的程序。

    有些语言,例如 Python,没有显式的编译器。它们相当于直接从您的文件中执行文本。

    读取此类语言文件的程序称为“解释器”,而语言本身也被称为“解释型”。然而,重要的是要理解,即使是解释器最终也会执行机器代码。不同之处在于从文本到代码的转换不是那么明显。
     

  3. 调试器。此工具可帮助您查找并修复程序中的错误。它允许分步执行代码,旨在程序在任何给定时间的每一步的都能检查正在处理的数据。

    MetaEditor 提供了内置调试器。

  4. 版本控制系统(VCS)。这样的系统有助于跟踪您代码中的变化。

    如果您已经编写了大量代码,并突然意识到一天前的版本运行得更好,或者您通常需要采用不同的路径,则 VCS 将允许您“恢复”到文件的先前状态(甚至整个项目),只需点击几下鼠标或从键盘输入文本命令即可。

    最重要的是,VCS 通常将数据存储在远程服务器上,而不是本地计算机上,以确保如果发生故障(例如,硬盘崩溃),您可以快速恢复您的工作。

    这两个重要因素使得不同的版本控制系统对于企业工作来说几乎是必须的,因为它们还允许开发人员之间进行协作。使用这些工具的习惯在独立工作时也有很大帮助,因为可能会发生不同的事情。

    还有第三方解决方案,例如 Git。不过,MetaEditor 中也内置了这样的工具,内置 VCS 的功能对于初学者来说已经足够了。

这是现代编程不可缺少的工具包。程序员可能会单独使用版本控制工具。但在现代世界,这种情况相对较少发生。更常见的是,用户会选择诸如 MetaEditor 之类的软件,其中包含所有这些功能(甚至更多)。

包含上述所有组件(文本编辑器、编译器、调试器等)的软件解决方案称为 集成开发环境 (IDE,Integrated Development Environment)


 

MQL5 程序的主要类型

MetaTrader 5 中可执行的应用程序可分为四种类型:

  1. 脚本(Scripts)。它们可以显示信息、交易或显示图形对象。为了执行该脚本,您应该在每次需要时重新运行它。
     
  2. 指标(Indicators)。它们旨在显示每次交易时的交易策略信息。它们并不自行进行交易。
  3. EA 交易(Expert Advisors)。这些是为交易而设计的程序。与指标一样,它们在每次分时报价时执行,但它们不具备绘制箭头或有用线条的工具。
  4. 服务(Services)。与所有以前类型的程序不同,服务不需要任何特定图表来运行。它们在终端 启动时运行一次。您可以强制它们保留在内存中,直到终端关闭(甚至在那之后的一段时间),但它们无法响应终端事件,例如新的分时报价或鼠标单击。


 

MQL5 中可用程序类型的比较

与 MQL5 程序使用相关的所有内容都可以总结在一个简单的表格中:

表格1 - 比较主要 MQL5 程序类型的功能。
 

程序功能
脚本指标EA 交易服务

链接到特定图表

可以交易

当时间框架改变时从内存中卸载
 

每次报价时执行

终端打开时执行一次

在图表窗口打开时运行

可以处理各种终端或图表事件

可以启动和停止计时器

自动调用初始化和去初始化函数

图表上只能执行一个程序实例
 


 


 

函数

每个程序都应该一些事情,也就是执行一些函数

任何函数都是一组导致某些(通常是基本的)结果的程序动作。例如,它们打印一行文本、绘制一个字母、或开启一笔交易等等。

有些编程语言不需要明确声明函数。这样的声明在 MQL5 中是强制性的:每个程序必须至少有一个声明的启动函数,否则会出现错误消息。对于每种类型的程序,初始函数的调用方式有所不同。但是不管怎样,程序必须要有函数,否则它什么都不做就变得毫无意义了。

几乎任何编程语言都允许创建自定义函数来分离重复的代码片段,然后使用一行代码来重复使用这些片段,而不是一遍又一遍地重写它们。此外,所有语言都具有一组预定义的函数。正是这些预定义的函数将一种编程语言与所有其他语言区分开来。

实际上,当谈论一种语言时,作者们常常试图描述其中预定义了哪些函数以及如何最好地使用它们来获得所需的结果。

MQL5 也不例外。它包含一组内置函数并允许创建自定义函数。可以使用上下文帮助方便地研究该语言的内置函数。如果要调用帮助,请在 MetaEditor 中打开任意代码(即使是别人编写的代码),点击任意突出显示的单词,然后按 <F1> 键。
 

某些类型的程序无法调用某些内置函数。正如前面提到的,有些函数必须完全按照帮助中描述的那样来实现。此外,不同类型的程序需要不同的函数。

但除此之外,我们的创造力几乎是无限的。我们可以创建任何辅助函数,以任何方便的方式组织它们,甚至可以使用其他语言(例如 C++ 或 Python)描述的函数。我们一定会在以后的文章中讨论所有这些功能。

但是现在让我们从 MQL5 语言允许我们创建的最简单的东西开始:我们将创建一个脚本,当用鼠标将其拖到图表上时就会执行一次。使用标准 MQL5 语言函数,在此脚本中,我们将在终端底部的标准面板中为用户显示常规文本消息。
 

启动并设置 IDE
 

最后,我们要开始实践了!目前,我们将在 MetaEditor 中完成所有工作。它可以使用快捷方式(从桌面或开始菜单)启动,也可以直接从 MetaTrader 终端使用下图所示的方法之一启动(图 1)。

Image removed.

图1. 打开 MetaEditor 的三种方法:1 - 工具栏按钮,2 - 菜单,3 - 热键 (F4)

一旦编辑器窗口打开,我建议立即输入您在 mql5.com 网站上的用户名和密码(或者,如果您尚未注册,请注册)。登录后,您可以使用 MetaEditor 内置的版本控制解决方案将项目直接保存到服务器。除非您允许访问,否则没有人会看到您保存的文件,因此将您的工作保存在 MetaQuotes 云中与在互联网上存储信息一样安全,这是一种很好的做法(不仅将您的工作存储在您的计算机上)。
 

Image removed.

图 2. MQL5.com 授权对话框
 

要打开授权对话框,请在编辑器窗口的主菜单中选择“工具”->“设置”,然后转到“社区”选项卡。此设置仅需执行一次。这些数据将保存在编辑器中,您将能够顺利使用版本控制系统和其他有用的 MQL5 服务。


 

第一个脚本
 

现在...只需右键单击左侧仪表板中我们要创建脚本的文件夹并选择“新建文件”即可。或者,您可以按 <Ctrl>+<N>... 在出现的窗口中,选择脚本。在下一个窗口中,输入文件名。

Image removed.

图 3. 创建新文件的方法。

Image removed.

图 4. 文件创建向导对话框 - 选择要创建的程序类型。

Image removed.

图 5. 文件创建向导对话框 - 键入要创建的程序的名称。
 

这样,您就将获得一个包含以下内容的新窗口:

//+------------------------------------------------------------------+ //|                                                   HelloWorld.mq5 | //|                                       Oleg Fedorov (aka certain) | //|                                   mailto:coder.fedorov@gmail.com | //+------------------------------------------------------------------+ #property copyright "Oleg Fedorov (aka certain)" #property link      "mailto:coder.fedorov@gmail.com" #property version   "1.00" //+------------------------------------------------------------------+ //| Script program start function                                    | //+------------------------------------------------------------------+ void OnStart()   { //---      } //+------------------------------------------------------------------+

例 1. 由向导生成的脚本模板。
 

看上去没什么复杂,只有17行代码。

我们来看看这里到底写了什么。

  • 首先,开头有几行,以双斜杠字符( // )开头。程序的中间也有类似的代码行。

这些是注释。它们是为人而编写的,因此编译器在生成字节码时会忽略它们。以双斜杠开头的注释称为单行注释。它们被放置在行的末尾。规则非常简单:双斜杠前面 的所有内容都会被编译器视为代码,而双斜杠后面的 所有内容对于人类来说都是文本

在这个向导创建的模板中,此上下文中不需要“前面”的代码。
 

在 MetaEditor 中,您可以快速注释掉几行:选择它们并按键盘快捷键“<Ctrl>+< ' >(Ctrl + 单引号)。如需取消注释这些行,请再次选择它们并按键盘快捷键 <Ctrl>+< ; >

还有多行注释。它们以字符 /* 开头,以相反的序列 */ 结尾。可以在代码中任何可以接受空格的地方插入多行注释。在编译的时候,编译器也会先“删除”这样的序列,然后再去弄清楚作者想要实现什么。

在实际使用中,注释很少插入到代码中间。大多数情况下,这只会带来不便。但是,它们在注释掉大块不必要的代码或者描述函数头时非常有用。

如果我们使用多行注释,该文件的标题可能如下所示(如果我自己编写代码,而不是由 MetaEditor Wizard 生成代码):

/*                                                    HelloWorld.mq5                                        Oleg Fedorov (aka certain)                                    mailto:coder.fedorov@gmail.com */

例 2.多行注释。
 

或者像这样:

/******************************************************************* *                                                  HelloWorld.mq5 * *                                      Oleg Fedorov (aka certain) * *                                  mailto:coder.fedorov@gmail.com * *******************************************************************/

例 3.修饰多行注释。
 

一般来说,在字符 /* 和 */ 之间 或 // 之后 可以是任何语言的任意文本。我是说无论任何语言,因为现代编辑器完全支持 Unicode 编码。不过,还是建议使用英语,因为这样可以节省切换语言的时间(这在大型项目中非常重要)。
 

  • 第二个有趣的代码块由以 # 开头的行组成。在这个文件中,这个块的每一行的起始词都是 #property。

一般来说,每个以井号开头的单词都是预处理器指令。编译器在将文本转换为机器代码之前不仅必须删除所有注释。它还必须做好其他准备。通常需要进行大量的准备工作,因为在大型项目中,需要从多个文件组装成最终的解决方案,定义一些数据(例如常数,如数字 Pi 或类似的东西,尽管有更复杂的情况),等等。我们将稍后探讨所有这些指令。

现在,为了理解生成的代码,我们需要知道的是 #property 指令描述了生成的程序的一些属性。通常,这些属性会影响初始窗口,用户通过将其图标拖到图表上来启动我们的程序后,该窗口会立即出现。

为了指明这一点,我们在代码中再添加一行:

#property copyright "Oleg Fedorov (aka certain)" #property link      "mailto:coder.fedorov@gmail.com" #property version   "1.00" #property script_show_inputs

例4. 添加属性 script_show_inputs
 

此行代码将允许您显示输入窗口,然后才开始执行主代码。如果没有此行,脚本将立即开始执行而不显示任何消息。如果有任何输入参数,脚本将使用默认值。

因此,将所需的代码写入文件中的所需位置(紧接着“version”属性之后)后,我们将运行程序的第一次编译。

选择图中所示的方法之一(例如,按 <F7> 键)。
 

Image removed.

图 6. 编译程序的几种方法。
 

等待几秒钟,然后 - 程序已经编译完毕(图 7)。

Image removed.

图 7. 编译结果。在这种情况下,编译成功了(0 个错误)。它耗时 527 毫秒,为 64 位处理器创建了字节码。
 

图 7 表明编译成功。如果文本文件中有错误,有关错误的消息将会出现在同一个窗口中。

编译完成后,脚本图标将自动出现在您的交易终端的脚本列表中。要切换到终端,您可以在编辑器中按 <F4> 键,这将会节省一些时间。
 

使用鼠标将脚本拖到图表上并查看属性窗口。

Image removed.

图 8. 脚本输入窗口。
(1)正在运行的脚本的图标显示在主窗口的右上角。
(2)“普通”选项卡显示使用 #property 指令描述的属性的使用情况。
(3)MetaEditor 已为“link”属性中指定的地址添加了附加参数。
如果我们使用网站链接,这可能就会显示访问者来自哪里。
 

让我们关闭窗口,返回编辑器并尝试指示脚本做一些有用的事情,例如,向用户显示一条消息。

为此,注释掉显示参数窗口的行(我们暂时还不需要更改任何内容)并添加另一行写入消息的代码:

//+------------------------------------------------------------------+ //|                                                   HelloWorld.mq5 | //|                                       Oleg Fedorov (aka certain) | //|                                   mailto:coder.fedorov@gmail.com | //+------------------------------------------------------------------+ #property copyright "Oleg Fedorov (aka certain)" #property link      "mailto:coder.fedorov@gmail.com" #property version   "1.00" //#property script_show_inputs //+------------------------------------------------------------------+ //| Script program start function                                    | //+------------------------------------------------------------------+ void OnStart()   { //---    Print("Hello, MQL5 world!");   } //+------------------------------------------------------------------+

例 5. 将字符串打印到用户日志的脚本。

  • 注意预定义的单词。

在此代码中,预定义的单词是 #property、copyright、link、version、void、OnStart和 Print。尝试在 MetaEditor 中点击其中任意一个并按 <F1> 打开内置帮助。
 

  • 在向导生成的 OnStart 函数中,我们调用了另一个标准函数 Print。使用括号中列出的参数 (也称为 arguments),我们告知打印函数它应该打印什么。

在示例 5 中,Print 函数仅接收一个参数:引号中写的文本。一般情况下,可能会有几种这样的参数。例如,可以使用任意数量的参数(最多 64 个)来调用此 Print 函数。

如果一个函数需要接收多个参数,那么这些参数应该用逗号分隔,如示例6:

Print("Hello, MQL5 World!", " I have written my own program. ", "Hooray!");

例6. 接受三个参数的函数
 

在每个分隔参数的逗号后,可以添加一个换行符(使用 <Enter> 键),以免在引号中混淆。

还要注意第二个参数里面的空格 。Print 函数将其参数“合并”为一行,因此程序员必须注意所有标点符号和单词之间的附加分隔符。
 

OnStart 函数在脚本服务启动时自动执行一次。

我们按照已知的方式编译并运行该脚本。

Image removed.

图 9. 第一个有用的操作:将字符串输出到日志。
 


 

结论

EA 交易和指标也具有预定义的函数,但名称不同。它们的运行原理和脚本函数是一样的:我们的代码主要写在内部,而属性写在外部。但也存在差异。

例如,EA 交易和指标中的某些函数 在每次分时报价 (tick) 时由终端执行,而不仅仅是像 OnStart 那样执行一次。
 

因此,我建议那些想检查自己从文章中学到了什么(并更新自己的知识)的人自己做一点“家庭作业”:

  1. 使用文件创建向导来生成本文示例未涵盖的所有其他类型的程序。
     
  2. 研究一下使用文章中介绍的工具是否可以找出每种类型的程序启动时调用了哪些函数以及它们的调用顺序。
  3. 作为一项额外任务,您可以检查如果有以下情况会发生什么:
     
    • 您将 OnStart 函数添加到指标和 EA 交易程序中
    • 您可以将向导为其他类型的程序创建的函数添加到脚本和服务中
       

也许这些研究会给您带来新的思路,并帮助您更好地理解 MQL5 语言中的程序究竟是如何工作的。

祝你好运,我们在下篇文章中再见!

Comments