您需要了解的有关MQL5程序结构的所有信息·进阶篇
「外部参数与全局变量的初始化位置」
MQL5 里用 input 修饰符声明的变量是外部参数,编译后出现在 EA 属性面板的「输入」页。用户在 MT5 里改的是值,修饰符本身在程序运行期锁死,不能由代码改写。每次加载程序,这些 input 都会在 OnInit() 被调用前完成重初始化。 看一段典型声明:MA_Period 默认 20、MA_Shift 默认 0、MA_Method 默认 MODE_SMA。后面三行加了注释,面板里就会显示「Moving Average Period」这类说明文字,方便实盘切换周期和偏移时少猜含义。 全局变量必须写在事件处理函数之外、和函数同级的区域。上面代码里的 Globalvar 就是例子。它的作用域覆盖整个程序,任意函数都能读能写,且只在加载时初始化一次——早于 OnInit 或 OnStart 的执行点。 外汇与贵金属杠杆高、滑点随机,外部参数乱填可能让策略在极端波动中频繁重算。开 MT5 把这段贴进 EA 头部,改 MA_Period 从 20 到 50,观察面板与初始化日志的差异最直接。
class="kw">input class="type">int MA_Period=class="num">20; class="kw">input class="type">int MA_Shift=class="num">0; class="kw">input ENUM_MA_METHOD MA_Method=MODE_SMA; class="kw">input class="type">int MA_Period=class="num">20; class=class="str">"cmt">//Moving Average Period class="kw">input class="type">int MA_Shift=class="num">0; class=class="str">"cmt">//Moving Average Shift class="kw">input ENUM_MA_METHOD MA_Method=MODE_SMA; class=class="str">"cmt">//Moving Average Type class="type">int Globalvar; class=class="str">"cmt">// Global variable before or outside the event handler and functions class="type">int OnInit() { ... }
函数和类能塞在程序哪个位置
MQL5 里函数和类不强制写在固定区域。自定义类尤其灵活:可以直接写在主文件任意处,也能抽进 include 文件再用 #include 挂进来——这和预处理器那节说的指令是同一套机制。 它们可以排在事件处理程序之前或之后,也能落在 input 变量和全局变量下方。实际写 EA 时,把类定义丢在全局变量之后、OnTick 之前,是最不容易报重定义错误的摆法。 下面这段是 MQL5 里函数与类的最小骨架,注意返回类型、参数括号和类尾的分号都省不掉:
returnedDataType functionName(param1, param2) { bodyOfFunction } class Cobject { class="type">int var1; class=class="str">"cmt">// variable1 class="type">class="kw">double var2; class=class="str">"cmt">// variable1 class="type">void method1(); class=class="str">"cmt">// Method or function1 };
returnedDataType functionName(param1, param2) { bodyOfFunction } class Cobject { class="type">int var1; class=class="str">"cmt">// variable1 class="type">class="kw">double var2; class=class="str">"cmt">// variable1 class="type">void method1(); class=class="str">"cmt">// Method or function1 };
◍ MT5事件处理函数怎么挂到程序生命周期上
写EA、指标或脚本,核心不是算指标,而是把代码挂到对的事件节点上。行情每来一个tick,终端就触发OnTick;EA初始化和退出分别走OnInit与OnDeinit,指标同理。脚本没有持续运行需求,启动即跑OnStart,跑完即退。 不同程序类型能用的处理函数不一样:脚本只能用OnStart;指标用OnInit、OnDeinit、OnCalculate、OnChartEvent等;EA最全,从OnTick、OnTimer到OnTrade、OnBookEvent、OnTester系列都能接。OnCalculate有两种重载,一种吃单个price数组,一种吃OHLCV加spread的完整时间序列,写自定义指标时选错签名会直接编译不过。 回测和优化阶段也有专门钩子:OnTester在单轮历史测试结束时跑,OnTesterInit和OnTesterDeinit框住整次优化,OnTesterPass在收到新数据帧时触发。想做参数寻优后自动导出结果,这几个函数比手动记日志靠谱。 外汇和贵金属杠杆高、滑点跳空频繁,事件触发顺序不保证严格实时,OnTrade里的仓位判断要做容错,别假设成交回报一定先到。
class="type">int OnStart(class="type">void); class="type">void OnStart(class="type">void); class="type">int OnInit(class="type">void); class="type">void OnInit(class="type">void); class="type">void OnDeinit( class="kw">const class="type">int reason class=class="str">"cmt">// deinitialization reason code ); class="type">void OnTick(class="type">void); class="type">int OnCalculate( class="kw">const class="type">int rates_total, class=class="str">"cmt">// price[] array size class="kw">const class="type">int prev_calculated, class=class="str">"cmt">// number of handled bars at the previous call class="kw">const class="type">int begin, class=class="str">"cmt">// index number in the price[] array meaningful data starts from class="kw">const class="type">class="kw">double& price[] class=class="str">"cmt">// array of values for calculation ); class="type">int OnCalculate( class="kw">const class="type">int rates_total, class=class="str">"cmt">// size of class="kw">input time series class="kw">const class="type">int prev_calculated, class=class="str">"cmt">// number of handled bars at the previous call class="kw">const class="type">class="kw">datetime& time, class=class="str">"cmt">// Time array class="kw">const class="type">class="kw">double& open, class=class="str">"cmt">// Open array class="kw">const class="type">class="kw">double& high, class=class="str">"cmt">// High array class="kw">const class="type">class="kw">double& low, class=class="str">"cmt">// Low array class="kw">const class="type">class="kw">double& close, class=class="str">"cmt">// Close array class="kw">const class="type">long& tick_volume, class=class="str">"cmt">// Tick Volume array class="kw">const class="type">long& volume, class=class="str">"cmt">// Real Volume array class="kw">const class="type">int& spread class=class="str">"cmt">// Spread array ); class="type">void OnTimer(class="type">void); class="type">void OnTrade(class="type">void); class="type">void OnTradeTransaction() class="kw">const MqlTradeTransaction& trans, class=class="str">"cmt">// trade transaction structure
「事件回调函数的参数结构」
MT5 的预定义回调把交易、深度簿、图表和回测四类事件拆成了固定签名,写 EA 时直接套用就不会漏参。 OrderSendAsync 的回调里,request 是 MqlTradeRequest 的常引用,装委托单的所有字段;result 是 MqlTradeResult 的常引用,回装柜台应答。两者都用 & 传引用,避免结构体拷贝。 OnBookEvent 只收一个 symbol 字符串常引用,市场深度变动时触发,用来定位是哪个品种簿变了。 OnChartEvent 给得最杂:id 是事件编号,lparam 和 dparam 分装长整型和双精度参数,sparam 兜底字符串,比如鼠标点击的坐标或对象名都从这儿取。 回测侧有 OnTester 返回 double 做优化目标,OnTesterInit / OnTesterDeinit 管初始化和反初始化,OnTesterPass 在每帧参数组合跑完时回调。注意原文里 OnTesterInit 出现了 int 和 void 两种返回,实际只能留一个,编译会报重定义。
class="kw">const class="type">MqlTradeRequest& request, class=class="str">"cmt">// request structure class="kw">const class="type">MqlTradeResult& result class=class="str">"cmt">// response structure ); class="type">void OnBookEvent( class="kw">const class="type">class="kw">string& symbol class=class="str">"cmt">// symbol ); class="type">void OnChartEvent( class="kw">const class="type">int id, class=class="str">"cmt">// event ID class="kw">const class="type">long& lparam, class=class="str">"cmt">// class="type">long type event parameter class="kw">const class="type">class="kw">double& dparam, class=class="str">"cmt">// class="type">class="kw">double type event parameter class="kw">const class="type">class="kw">string& sparam class=class="str">"cmt">// class="type">class="kw">string type event parameter ); class="type">class="kw">double OnTester(class="type">void); class="type">int OnTesterInit(class="type">void); class="type">void OnTesterInit(class="type">void); class="type">void OnTesterDeinit(class="type">void); class="type">void OnTesterPass(class="type">void);
从脚本骨架看 MQL5 结构取舍
写 MQL5 程序不必把整套结构都堆上去。#include 引外部文件、自定义类或函数,都是按需选用,脚本类程序往往连事件处理函数都只用 OnStart() 一个入口。 上面这段脚本只做一件事:让用户在 MT5 弹窗里填两个整型数,相加后把结果打到「专家」标签页。注意 #property script_show_inputs 这一行,少了它输入参数界面不会弹出,这是脚本交互的关键开关。 代码逐行拆解: #property copyright / link / version 是元数据声明,编译后显示在文件属性里;#property script_show_inputs 强制脚本运行前显示输入框。 input int userEntryNum1、userEntryNum2 是外部可填变量;int result 为全局变量暂存求和值。 void OnStart() 是脚本唯一事件入口,大括号内 result=两输入相加,Print("Result: ", result) 输出到终端。 换 EA 或指标时,事件处理器得跟着变:EA 常用 OnTick() 接新分时触发,指标多用 OnCalculate()。先认清楚每段结构挂在哪种程序类型下,才不会把脚本写法硬套进 EA 里。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Script program example.mq5 | class=class="str">"cmt">//| Copyright class="num">2023, MetaQuotes Ltd.| class=class="str">"cmt">//| [MQL5官方文档] | class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//class="kw">property preprocessor class="macro">#class="kw">property copyright "Copyright class="num">2023, MetaQuotes Ltd." class="macro">#class="kw">property link "[MQL5官方文档] class="macro">#class="kw">property version "class="num">1.00" class="macro">#class="kw">property script_show_inputs class=class="str">"cmt">//inputs class="kw">input class="type">int userEntryNum1; class="kw">input class="type">int userEntryNum2; class=class="str">"cmt">//global variable class="type">int result; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Script program start function | class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//event handler class="type">void OnStart() { result=userEntryNum1+userEntryNum2; Print("Result: ", result); } class=class="str">"cmt">//+------------------------------------------------------------------+
◍ 把结构顺序刻进编译习惯
MQL5 程序的编译顺序不是装饰,而是硬约束。预处理器指令必须排在宏替换之前,宏替换先于程序属性和包含文件,导入函数块要在全局变量之前就位,否则 MT5 编辑器会直接报『unexpected token』。 事件处理函数有固定入口名:脚本走 OnStart,EA 跑 OnInit、OnDeinit、OnTick,指标用 OnCalculate,定时器触发 OnTimer,成交相关走 OnTrade 与 OnTradeTransaction,深度回测还有 OnTester 系列四个钩子。把这些名字写错一个字母,程序能编译但事件不会响应。 打开下载包里的 Script_program_example.mq5(约 1.01 KB)对照看,文件里从 #property 到 OnStart 的排列就是上述序列的极简实例。外汇与贵金属杠杆高、滑点突发行情多,实盘前务必在策略测试器用历史数据跑通事件链。 下次新建 EA 时,先按这个顺序搭空骨架再填逻辑,比边写边改少踩一半编译坑。结构顺了,后面接 CMF、肯特纳或 Aroon 系统都只是往函数里塞代码的事。