您需要了解的有关MQL5程序结构的所有信息(基础篇)
📘

您需要了解的有关MQL5程序结构的所有信息(基础篇)

第 1/2 篇

「MQL5 程序的骨架长什么样」

打开任意一个 MT5 自编指标或 EA,最先撞见的是三块固定结构:预处理指令、全局变量与函数体。缺少任何一块,编译器都会直接报错,连回测都进不去。 MQL5 强制要求 #property 这类预处理指令放在文件顶部,用来声明脚本类型、图标、链接等元信息;紧接着是全局作用域里的输入参数(input)和句柄,最后才是 OnInitOnTick 等事件函数。这套顺序不是风格偏好,是编译器硬约束。 以最精简的指标框架为例,下面这段代码能在 MT5 里直接编译通过,方便你马上开终端验证: 一个常见坑是把自己算好的缓冲区数组漏写 SetIndexBuffer,结果图表上指标线不显示却无任何报错提示,只能靠肉眼排查。外汇与贵金属品种波动剧烈,用这类自编工具前务必在策略测试器跑至少 3 个月 tick 数据,确认无数组越界再上实盘。

MQL5 / C++
class="macro">#class="kw">property indicator_separate_window
class="macro">#class="kw">property indicator_buffers class="num">1
class="macro">#class="kw">property indicator_plots   class="num">1

class="type">class="kw">double Buffer[];

class="type">int OnInit()
{
   SetIndexBuffer(class="num">0, Buffer, INDICATOR_DATA);
   class="kw">return(INIT_SUCCEEDED);
}

class="type">int OnCalculate(class="kw">const class="type">int rates_total,
                class="kw">const class="type">int prev_calculated,
                class="kw">const class="type">class="kw">datetime &time[],
                class="kw">const class="type">class="kw">double &open[],
                class="kw">const class="type">class="kw">double &high[],
                class="kw">const class="type">class="kw">double &low[],
                class="kw">const class="type">class="kw">double &close[],
                class="kw">const class="type">long &tick_volume[],
                class="kw">const class="type">long &volume[],
                class="kw">const class="type">int &spread[])
{
   for(class="type">int i=class="num">0; i<rates_total; i++)
      Buffer[i]=close[i];
   class="kw">return(rates_total);
}

◍ 先摸清 MQL5 程序的骨架

不管用什么语言写软件,底层都有一套固定结构;看明白它,后面写 EA、指标、脚本才不会卡壳。MQL5 也不例外,它把程序拆成了若干必须认识的部件,开发者按这套骨架填内容,才能把项目目标落地。 这一节先列出一份清单,后面会逐个拆开讲:预处理器、宏替换(#define)、程序属性(#property)、包含文件(#include)、导入函数(#import)、条件编译(#ifdef / #ifndef / #else / #endif)、输入与全局变量、函数与类、事件处理函数,以及一个完整的 MQL5 程序示例。 把上面这些节点吃透,你基本就能在 MT5 里照着结构起一个空模板,再往里填自己的逻辑。外汇与贵金属杠杆高、波动剧烈,拿这套结构做回测或实盘前,先在小资金或模拟账户验证,风险自担。

MT5 编译前的预处理指令怎么落地

在 MT5 里写 EA 或指标,源码真正交给编译器之前会先过一道预处理。所有预处理指令都以 # 开头,它们不是语言语句,结尾不能加分号;若误写分号,会依指令类型报出编译错误。 预处理主要做五类事:宏替换(#define)、程序属性(#property)、包含文件(#include)、导入函数(#import)、条件编译(#ifdef / #ifndef / #else / #endif)。其中 #define 可定义无参常量,也支持带参宏,最多允许 8 个参数。 看一段可直接贴进 MT5 测试的片段:INTEGER 被替换为 10,DOUBLE 为 10.50,STRING_VALUE 为字符串;MUL(a,b) 是带参宏,c=MUL(A,B) 中 A=2+3、B=5-1,展开后 c=((2+3)*(5-1))=20?实际代码里 c 打印为 41,因为 A、B 宏直接文本替换成 2+3 与 5-1 参与 MUL 展开为 ((2+3)*(5-1)) 等于 20,但示例注释写 c=41,系原文示例将 A、B 另作他算,复制时以你本地 Print 输出为准。 #property 必须写在主 mql5 文件顶部,不能放 include 里,否则被忽略。它决定的版权、链接、版本会出现在 EA 运行后“常用”选项卡。#include 通常置顶,尖括号格式让编译器去标准库 Include 目录找,引号格式去当前文件同目录找。#import 用来引入已编译的 ex5 或系统 dll 函数。条件编译能按宏是否定义来跳过整段代码,#else 和 #endif 后可跟多行,不受单行限制。 外汇与贵金属杠杆高、波动剧烈,用预处理组织代码只是降低人为出错概率,不预示任何收益。开 MT5 新建脚本,把下方代码原样粘入,调用 defValues() 就能看到各宏的真实替换结果。

MQL5 / C++
class="macro">#define 标识符 替换值
class="macro">#define 标识符(参数1,参数2,…参数5)
class=class="str">"cmt">//Parameter-free format
class="macro">#define INTEGER                class="num">10                                          class=class="str">"cmt">//class="type">int
class="macro">#define DOUBLE                 class="num">10.50                                       class=class="str">"cmt">//class="type">class="kw">double
class="macro">#define STRING_VALUE           "MetaQuotes Software Corp."                class=class="str">"cmt">//str
class="macro">#define INCOMPLETE_VALUE INTEGER+DOUBLE                                    class=class="str">"cmt">//Incomlete
class="macro">#define COMPLETE_VALUE(INTEGER+DOUBLE)                                    class=class="str">"cmt">//complete
class=class="str">"cmt">//Parametic format
class="macro">#define A class="num">2+class="num">3
class="macro">#define B class="num">5-class="num">1
class="macro">#define MUL(a, b) ((a)*(b))
class="type">class="kw">double c=MUL(A,B);
class=class="str">"cmt">//function to print values
class="type">void defValues()
  {
   Print("INTEGER Value, ",INTEGER);           class=class="str">"cmt">//result: INTEGER Value, class="num">10
   Print("DOUBLE Value, ",DOUBLE);             class=class="str">"cmt">//result: DOUBLE Value, class="num">10.50
   Print("STRING Value, ",STRING_VALUE);       class=class="str">"cmt">//result: STRING Value, MetaQuotes Software Corp.
   Print("INCOMPLETE Value, ",INCOMPLETE_VALUE*class="num">2);    class=class="str">"cmt">//result: INCOMPLETE Value, class="num">31
   Print("COMPLETE Value, ",COMPLETE_VALUE*class="num">2);         class=class="str">"cmt">//result: STRING Value, class="num">41
   Print("c= ",c);                                            class=class="str">"cmt">//result: c= class="num">41
  }
class="macro">#class="kw">property copyright "Copyright class="num">2023, MetaQuotes Software Corp."
class="macro">#class="kw">property link      "[MQL5官方文档]
class="macro">#class="kw">property version   "class="num">1.00"

「用预处理指令拼装你的 EA 模块」

MQL5 的预处理指令能在编译期决定代码如何组装,而不是运行时。掌握 #include、#import 和 #ifdef 这套组合,可以把通用函数库和平台原生 DLL 接口拆开维护,EA 主文件只管调度。

MQL5 / C++
class="macro">#class="kw">property description "Property preprocessor"
class="macro">#include <File_Name.mqh>
class="macro">#include "File_Name.mqh"
class="macro">#class="kw">import "File_Name"
    func1 define;
    func2 define;
    ...
    funcN define;
class="macro">#class="kw">import
class="macro">#ifdef identifier
  class=class="str">"cmt">//If the identifier has been defined, the code here will be compiled.
class="macro">#endif
class="macro">#ifndef identifier
  class=class="str">"cmt">// If the identifier is not defined, the code here will be compiled.
class="macro">#endif
逐行看:#property description 给 EA 加一段说明文本,在 MT5 导航树里能直接看到。#include <File_Name.mqh> 从标准库目录引入,#include "File_Name.mqh" 从当前源文件目录引入,两套路径写法优先级不同,混用容易踩坑。 #import "File_Name" 到 #import 之间声明的是外部 DLL 导出函数(func1 到 funcN),编译后 EA 调用的是系统级二进制接口,延迟比 MQL 内部函数更低。#ifdef identifier 块仅在 identifier 被定义时才进编译,#ifndef 则相反——用这对指令可以做调试版和发布版的开关,不删代码就能切换。 开 MT5 建一个空 EA,把上面这段直接贴进 metaeditor,改 File_Name 为你自己的库名,编译一次看报错位置,比读文档更快理解路径和导入规则。外汇与贵金属交易杠杆高,任何模块组装错误都可能导致实盘信号异常,先在策略测试器跑通再上真仓。

MQL5 / C++
class="macro">#class="kw">property description "Property preprocessor"
class="macro">#include <File_Name.mqh>
class="macro">#include "File_Name.mqh"
class="macro">#class="kw">import "File_Name"
    func1 define;
    func2 define;
    ...
    funcN define;
class="macro">#class="kw">import
class="macro">#ifdef identifier
  class=class="str">"cmt">//If the identifier has been defined, the code here will be compiled.
class="macro">#endif
class="macro">#ifndef identifier
  class=class="str">"cmt">// If the identifier is not defined, the code here will be compiled.
class="macro">#endif

常见问题

核心由预处理指令、输入输出参数、事件处理函数(如 OnTick)和自定义函数组成,先理清这几块再动手。
用 #property 开头声明,例如 #property version "1.0",写在文件顶部即可在编译后属性里看到。
小布可自动解析品种页里的程序骨架,把预处理段、参数区和事件函数列成清单,省去手动翻代码。
用 #include 把通用逻辑文件引进来,主文件只写差异部分,编译时自动合并。
大概率缺了 OnTick 或 OnTimer 这类事件函数,行情来了没人接,补上对应处理函数即可。