在编程领域,“初始化”这一术语被用于多种不同上下文。在 MQL5 中,该术语同样具有多重性。在 初始化 章节中,我们曾用该术语表示设置变量的初始值。随后,我们讨论了指标和 EA 交易中的初始化事件OnInit。尽管这两种初始化的含义相近(均使程序进入可工作状态),但实际上它们代表了 MQL 程序启动准备的两个不同准备阶段:系统初始化和应用初始化。
一个完整的 MQL 程序的生命周期可以概括为以下主要阶段:
脚本和服务默认不具备 OnInit和 OnDeinit 处理程序,因此它们不存在步骤 3 和步骤 5,且步骤 4 简化为单次调用 OnStart。
系统初始化(步骤 2)与加载阶段不可分割,即系统初始化总是紧随加载阶段之后执行。最终清理始终在卸载前执行。然而,指标和 EA 交易在不同场景下的加载和卸载机制存在差异。因此,OnInit和 OnDeinit 调用(步骤 3 和 5)是能够为 EA 交易和指标提供一致的应用初始化和反初始化的参考点。
在以下场景下执行指标和 EA 交易加载:
场景 |
|
|
|---|---|---|
用户在图表上启动程序 |
+ |
+ |
启动终端(如果程序在终端上次关闭前已在图表上运行) |
+ |
+ |
加载模板(如果模板包含已附加到图表的程序) |
+ |
+ |
配置文件变更(如果程序附加到其中一个配置文件图表) |
+ |
+ |
成功重新编译后(如果程序之前已附加到图表) |
+ |
+ |
更改活动账户 |
+ |
+ |
- |
- |
- |
更改指标所附加的图表的交易品种或周期 |
+ |
- |
更改指标的输入参数 |
+ |
- |
- |
- |
- |
连接账户(授权),无论账户号码是否发生变化 |
- |
+ |
可以更简洁的形式制定以下规则:EA 交易不会经历完整的生命周期,即当图表的交易品种/时间范围发生变更时,以及当输入参数变更时,它们不会重新加载。
因此,在卸载程序时可以观察到类似不对称性。卸载指标和 EA 交易的原因如下:
场景 |
|
|
|---|---|---|
从图表中移除程序 |
+ |
+ |
关闭终端(当程序附加到图表时) |
+ |
+ |
在正在运行程序的图表上加载模板 |
+ |
+ |
关闭正在运行程序的图表 |
+ |
+ |
更改配置文件(如果程序已附加到配置文件的其中一个图表) |
+ |
+ |
更改终端所连接到的账户 |
+ |
+ |
- |
- |
- |
更改指标所附加的图表的交易品种和/或周期 |
+ |
- |
更改指标的输入参数 |
+ |
- |
- |
- |
- |
将另一个或同一个 EA 附加到当前 EA 已在运行的图表上 |
- |
+ |
调用 ExpertRemove 函数 |
- |
+ |
反初始化的原因可以在程序中通过函数 UninitializeReason或标志 _UninitReason 来查找(请参阅 检查停止 MQL 程序的状态和原因章节)。
请注意,更改图表的交易品种或时间范围时,以及更改输入参数时,EA 交易将保留在内存中,即不会执行步骤 6 至 7(最终清理和卸载)以及步骤 1 至 2(加载和主内存分配),因此全局变量和静态变量的值不会进行重置。在这种情况下,会分别针对旧的和新的交易品种/时间范围(或者旧的和新的设置)依次调用 OnDeinit和 OnInit 处理程序。
由于 EA 交易中的全局变量不会被清除,因此反初始化代码 _UninitReason在OnInit 处理程序中保持不变,可供分析使用。只有在下一个事件发生时,且在调用 OnDeinit之前,新代码才会被写入变量中。
在 OnInit函数结束之前接收到的针对 EA 交易的所有事件都会被跳过。
当 MQL 程序首次启动时,设置对话框会在步骤 1 和步骤 2 之间显示。当更改输入参数时,设置对话框会根据程序类型以不同方式插入到主循环中:对于指标,对话框仍会在步骤 2 之前显示,对于 EA 交易,则会在步骤 3 之前显示。
本书附带名为LifeCycle.mq5的指标和 EA 交易模板。它会在OnInit/OnDeinit处理程序中记录全局初始化/最终清理步骤。将程序放置在图表上,观察以下各种用户操作会触发哪些事件:加载/卸载、更改参数、切换交易品种/时间范围。
脚本仅在添加到图表时加载一次。如果脚本正在循环运行,重新编译不会导致其重启。
服务通过终端界面上下文菜单命令进行加载和卸载。若某个已在运行的服务被重新编译,该服务将重新启动。请记住,处于活动状态的服务实例将在终端启动时自动加载,并在终端关闭时自动卸载。
在接下来两个章节,我们将从事件处理程序的层面探讨启动不同 MQL 程序的特性。