Summary: 一篇技术向的MT4程序运行机制深度解读,涵盖EA、指标、脚本的加载卸载规则和事件队列机制,以及品种切换时程序状态的独家解析。




我盯着MT4图表,一头雾水。刚挂上去的EA在EURUSD上跑得好好的,一切到GBPUSD就不交易了。没报错,没警告,就只是沉默。重启MT4,重新挂EA,又活了。但只要一切品种,又死了。

那时候我才意识到,我压根不了解MT4到底是怎么加载和运行程序的。我知道怎么把EA拖到图表上,但后台发生了什么,我一无所知。于是我翻了一遍MQL4文档,发现了一些完全改变了我调试方式的东西。

三种程序类型——行为各不相同



先理清楚我们面对的是什么。MT4有三种MQL4可执行程序:

  • 智能交易系统(EA): 连续运行的程序,挂在图表上,响应报价、定时器和图表事件。除非你主动移除或关掉图表,否则它会一直挂着。

  • 自定义指标: 也是连续运行,但它们共享图形界面线程。这个很关键——如果一个指标陷入死循环,整个终端都会卡死。

  • 脚本: 一次性程序。跑完就自动卸载。


  • 每种类型的加载和卸载规则都不一样。大部分混乱就来自这里。

    步骤1:EA到底是怎么加载的——不只是拖放



    把EA拖到图表上,后台其实做了这么一串事:

  • EA文件被加载到终端内存。

  • 所有全局变量初始化。如果全局变量是类类型且有构造函数,构造函数会在这个阶段执行。

  • 程序开始等待终端发来的事件。

  • 如果有OnInit()事件处理器,就调用它。


  • 关键点来了: 每个MQL4程序至少得有一个事件处理器函数。如果没有,程序加载了也不会执行。我见过有人花好几个小时调试一个编译通过了但图表上啥都不干的EA,结果只是忘了写OnTick()OnStart()

    EA在以下情况也会被加载
    终端启动时,如果EA在上次关闭前已经挂在图表上。
    加载包含这个EA的模板时。
    切换包含这个EA的配置文件时。
    一个容易忽略的点: 重新连接账户时——哪怕连的是同一个账户。如果你断开再连上账户,EA会重新加载。

    步骤2:品种/周期切换——让我顿悟的那个瞬间



    这就是我在EURUSD和GBPUSD之间切换时遇到的问题。根据MQL4文档,当你在挂有EA的图表上切换品种或周期时,EA不会被卸载再重新加载。

    等等,什么?我看了两遍才确认。

    官方文档的原话是:切换品种或周期时,终端会在旧品种/周期上调用OnDeinit(),然后再在新品种/周期上调用OnInit()。全局变量和静态变量不会重置,它们的值会保留下来。

    我的独家解读: 这是功能设计,不是bug,但也是陷阱。我见过很多EA就因为假设了品种相关的数据(比如点值或小数位数)不会在运行中变化而翻车。如果你的EA在OnInit()里初始化了品种相关的东西——比如把止损距离按点数存成固定值——从EURUSD切到GBPUSD时这个值就失效了,因为点值变了。

    我现在用的方法: 品种相关的值永远在OnTick()OnTimer()里重新计算,不要只在OnInit()里算。或者更稳妥一点,把品种名存成一个全局变量,每个报价来了都检查一下有没有变。

    步骤3:EA卸载时发生了什么



    EA会在以下情况被卸载:
    从图表上移除。
    在同一图表上挂新的EA(旧的会被卸掉)。
    关闭终端。
    加载的模板覆盖了EA。
    关闭挂了EA的图表。
    切换配置文件(如果EA挂在被切换的图表上)。
  • 这个很重要: 终端切换账户时。

  • EA内部调用ExpertRemove()函数时。


  • EA被卸载时,终端会做两件事:
  • 调用OnDeinit(),带一个原因代码。

  • 做全局变量的反初始化——重置字符串变量、释放动态数组、调用类对象的析构函数。


  • 步骤4:指标的加载——共享线程的问题



    指标的加载规则和EA基本一样,但有一个关键区别:所有指标共享图形界面线程

    这意味着如果你有一个指标卡在死循环里,卡住的不只是那个指标,而是整个MT4终端。我早年踩过这个坑,在OnCalculate()里不小心写了个while(1)循环,MT4直接卡死,只能从任务管理器强行杀进程。

    这也是为什么指标开多了会变慢。 它们都在抢同一个线程的资源。10个图表上跑10个指标,就是10个程序抢一个线程。

    参考来源:MQL4官方文档,“程序运行”。

    步骤5:脚本——最简单,但有个坑



    脚本是MQL4程序里最简单的类型。挂上去就执行OnStart(),然后自动卸载。

    坑在哪里: 脚本和EA一样跑在独立的线程里。但脚本的信息在终端会话之间是不保存的。重启MT4之后,之前挂过的脚本不会自动重启。

    也就是说,如果你用脚本做关键任务,每次重启终端都得手动重新挂。

    步骤6:重新编译和输入参数——文档没细说的行为



    还有一件事让我意外过。如果正在图表上运行的EA被重新编译了,旧版本会被移除,新版本会自动加载。

    但这里有个重点: 如果源代码里的输入参数没变,之前设置的值会保留。但如果参数的数量、顺序、名称或类型变了,就会用新代码里的默认值。

    我被这个坑过。给一个运行中的EA加了一个新的输入参数,重新编译之后新参数默认值是false,但我以为它会默认true。EA的交易行为变了,我过了一个小时看日志才发现。

    我现在的工作流: 改完参数之后,即使文档说会自动重载,我也会手动把EA重启一遍。

    步骤7:事件队列——为什么你的EA可能会漏掉报价



    每个MQL4程序都有自己的事件队列。终端把事件发到图表上,程序按顺序一个一个处理。

    重点: NewTick事件是合并的。如果队列里已经有一个NewTick事件在排队或正在处理,新的NewTick事件不会被加进队列。Timer和ChartEvent也一样。

    这意味着如果你的OnTick()处理时间太长,你会漏掉报价。终端不会排队等你——直接丢弃。我见过有人在OnTick()里做大量计算甚至调Sleep(),EA卡住,漏掉几个报价,然后又恢复正常。

    我的建议: 不要在OnTick()里做重活。用OnTimer()做周期性计算,或者把工作分到不阻塞主线程的函数里去。

    参考来源:MQL4官方文档,“程序运行——事件队列”。

    步骤8:DLL导入——老生常谈但总出问题



    如果你的EA用到外部DLL,加载行为会不一样。DLL必须放在terminal_dir\MQL4\Libraries文件夹里。EA加载的时候会尝试导入DLL函数。如果DLL不存在或者函数签名不匹配,EA初始化会失败。

    一个我没在文档里看到明确说明的点: 有些DLL依赖系统DLL(比如Visual C++运行库)。如果那些依赖缺失,导入会静默失败——你只会在“EA”标签里看到“DLL调用不被允许”或“错误126”。这不是MT4的问题,是Windows依赖环境的问题。

    MQL4官方文档只讲了导入语法,系统级别的依赖是微软那边的事。参考来源:微软官方文档,“Visual C++ Redistributable”。

    步骤9:自动交易按钮——总开关



    还有一个经常把人搞晕的东西:MT4窗口顶部的“自动交易”按钮。这是所有EA的全局开关。

    就算你的EA加载成功了,OnInit()也跑完了,如果自动交易按钮是关的,EA下不了单。EA照样可以分析价格跑计算,但OrderSend()这类交易函数都用不了。

    参考来源:MetaQuotes官方帮助中心,“自动交易设置”章节。

    最后说几句



    折腾了这么一圈我明白了。EA不是一段傻跑的代码。它有生命周期——加载、初始化、运行、反初始化、卸载——每个阶段都有规则影响EA的行为。

    如果你的EA在EURUSD上能跑,切到GBPUSD就不行了,别一上来就怀疑代码。先搞清楚切换品种时发生了什么。记住OnInit()只跑一次,品种相关的值可能会过期。还要记住事件队列会丢掉处理太慢时的报价。

    搞懂MT4管理程序的底层机制之后,调试简单多了。很多“bug”其实根本不是bug——只是我不了解平台的工作原理而已。

    参考来源:
  • MQL4官方文档 – “程序运行”(docs.mql4.com/runtime/running)。

  • MQL4官方文档 – “MQL4程序”(docs.mql4.com/runtime/)。

  • 微软官方文档 – “Visual C++ Redistributable最新支持下载”。


  • 本文首发于FXEAR.com,原创内容,未经授权禁止转载。