在MQL5程序中使用断言(基础篇)
在MQL5里用断言兜底逻辑
MQL5 提供了 assert() 宏,用于在调试阶段强制校验关键条件。一旦表达式为假,终端会中断并抛出诊断信息,避免错误状态被悄悄带进实盘逻辑。 对价格行为策略来说,断言最适合卡住那些‘不该发生’的边界:比如指标句柄无效、数组长度为零、或计算出的 lot 超出经纪商限制。外汇与贵金属杠杆高、点差跳变频繁,这类隐性错误若不被拦下,可能在一次非农行情里放大成异常下单。 下面是一个最小可用示例,验证均线句柄是否成功创建: [CODE] #include <assert.mqh> int ma_handle = iMA(_Symbol, PERIOD_M15, 14, 0, MODE_EMA, PRICE_CLOSE); assert(ma_handle != INVALID_HANDLE); [/CODE] 逐行拆解:第一行引入断言头文件;第二行取当前品种 M15 周期、周期14、偏移0、EMA 模式、收盘价计算的均线句柄;第三行断言句柄不等于 INVALID_HANDLE,若创建失败则立即终止并报警,省去后续 CopyBuffer 拿到空数据的麻烦。 把这类断言留在调试版本,上线前用 #define NDEBUG 关掉,就能在 MT5 里低成本排雷。
class="macro">#include <assert.mqh> class="type">int ma_handle = iMA(_Symbol, PERIOD_M15, class="num">14, class="num">0, MODE_EMA, PRICE_CLOSE); assert(ma_handle != INVALID_HANDLE);
◍ 用断言给 EA 逻辑上保险
断言本质是一段内嵌的检查代码,用来在程序任意位置验证『这里本该成立』的假设。它通常以独立函数或宏的形式存在,运行时判断某个表达式是否为真;一旦为假,就抛出提示并可能终止执行,让错误点直接暴露而不是埋进后续乱流。 举个具体例子:你写 EA 时认定变量 X 在任何正常分支下都不该为负,就可以插一句『我确定 X>=0』。若某次 X 跑到 -1.5,断言立刻报警,你不用翻全盘日志就能定位是哪段计算越界。 在 MT5 里做多模块复用的指标或面板时,断言价值最高——部件被反复改写后,旧假设很容易悄悄失效。但它只该盯『正常运行绝不该发生』的异常,不能拿去当业务判断。 经验上的红线:断言只活在与调试相关的构建里。最终给实盘用的 ex5 编译时,必须通过条件编译(如 #ifdef _DEBUG)整体剔除,否则既拖慢 tick 处理又可能误杀正常运行。
「在MT5里手写断言宏的两种路数」
标准断言通常要干七件事:回显被检查的表达式原文、报错时给出源文件名、报出函数签名、标出源码行号、附上开发者自写消息、出错即终止、以及能用条件编译把整套断言从成品里剔掉。MQL5 目前的标准函数只差第六点(程序终止)还没原生支持,其余都能用宏加 #ifdef 拼出来。 下面这段宏是温和版:定义了 DEBUG 才生效,不满足 condition 时只弹 Alert 把位置和信息打出来,不杀进程。关掉 DEBUG 后 assert 直接被替换成空语句,实盘编译无残留开销。 #define DEBUG #ifdef DEBUG #define assert(condition, message) \ if(!(condition)) \ { \ string fullMessage= \ #condition+", " \ +__FILE__+", " \ +__FUNCSIG__+", " \ +"line: "+(string)__LINE__ \ +(message=="" ? "" : ", "+message); \ Alert("Assertion failed! "+fullMessage); \ } #else #define assert(condition, message) ; #endif 强硬版在 Alert 之后故意对一个长度为 0 的数组 x 做 x[1]=0.0 赋值,借数组越界让 EA 在测试环境直接崩掉,逼你当场修逻辑。 #define DEBUG #ifdef DEBUG #define assert(condition, message) \ if(!(condition)) \ { \ string fullMessage= \ #condition+", " \ +__FILE__+", " \ +__FUNCSIG__+", " \ +"line: "+(string)__LINE__ \ +(message=="" ? "" : ", "+message); \ Alert("Assertion failed! "+fullMessage); \ double x[]; \ ArrayResize(x, 0); \ x[1] = 0.0; \ } #else #define assert(condition, message) ; #endif 外汇与贵金属杠杆高、滑点随机,拿断言做策略自检时只在回测或模拟盘开 DEBUG,实盘发布务必注释掉 #define DEBUG,否则 Alert 刷屏可能拖慢 tick 处理。
class="macro">#define DEBUG class="macro">#ifdef DEBUG class="macro">#define assert(condition, message) \ if(!(condition)) \ { \ class="type">class="kw">string fullMessage= \ class="macro">#condition+", " \ +__FILE__+", " \ +__FUNCSIG__+", " \ +"line: "+(class="type">class="kw">string)__LINE__ \ +(message=="" ? "" : ", "+message); \ Alert("Assertion failed! "+fullMessage); \ } class="macro">#else class="macro">#define assert(condition, message) ; class="macro">#endif class="macro">#define DEBUG class="macro">#ifdef DEBUG class="macro">#define assert(condition, message) \ if(!(condition)) \ { \ class="type">class="kw">string fullMessage= \ class="macro">#condition+", " \ +__FILE__+", " \ +__FUNCSIG__+", " \ +"line: "+(class="type">class="kw">string)__LINE__ \ +(message=="" ? "" : ", "+message); \ Alert("Assertion failed! "+fullMessage); \ class="type">class="kw">double x[]; \ ArrayResize(x, class="num">0); \ x[class="num">1] = class="num">0.0; \ } class="macro">#else class="macro">#define assert(condition, message) ; class="macro">#endif
用 assert 在调试期逼停程序
MQL5 里没有原生的一键紧急停止接口,想在开发阶段让错误条件立刻暴露,常见做法是自己写一个 assert 宏。思路是先定义 DEBUG 标识:写了 #ifdef DEBUG,全功能版宏才会编进程序;没写就编一个空操作版,线上跑不拖性能。 全功能宏先把传入的 condition 算一遍,若为 false,就把五样东西拼成一条 fullMessage:表达式原文、__FILE__ 文件名、__FUNCSIG__ 函数签名、__LINE__ 行号,以及你额外传的 message。拼完用 Alert 弹出来,再对不存在的数组元素赋值,故意触发运行错误让程序崩掉。 这种“崩法”对子窗口指标有副作用——指标终止后界面不会自动清,得手动关;若执行中建了终端全局变量或文件,也可能留垃圾。不能接受就改用第一型宏(不强制崩溃,只报警)。 宏可以单独存成 assert.mqh 丢进 <data folder>/MQL5/Include,EA 里 #include 就能用。下面这段就是个故意写错的例子:断言“0 比 1 大”,必为假,编译进 DEBUG 后初始化就会弹错并崩。外汇贵金属脚本调试本就高风险,崩在实盘前测出来好过瞒到线上。
class="macro">#include <assert.mqh> class="type">int OnInit() { assert(class="num">0 > class="num">1, "my message") class="kw">return(INIT_SUCCEEDED); } class="type">void OnDeinit(const class="type">int reason) { } class="type">void OnTick() { }