MQL5 定义机制:从编译指令到宏控制的入门铺垫(基础篇)
(1/3)· 递归之后,为什么 #define 是代码微调与复用最省力的第一道开关
「先把信号频率这件事说清楚」
很多交易者刚接触 MT5 自带指标时,会默认「信号出现 = 该动手」。实际上在 1 小时图上跑一套常见均线交叉逻辑,过去 90 天里平均每周触发约 11 次信号,其中约 4 次是窄幅震荡里的假突破,真正走出 30 点以上空间的不到三成。 外汇与贵金属杠杆高、滑点随机,这类信号只能当概率参考,不能直接当作下单依据。 把信号频率摊开看,你才会意识到:多数噪音集中在亚盘重叠时段。打开 MT5 的「行情」窗口切到对应品种,把历史回放速度调到 4x,手动数一遍自己常用的周期,比任何笼统结论都实在。
◍ 递归与宏定义的前置认知
这套材料只作教学用途,不是可以直接上线的成品程序,目的是把思路讲透而不是把某个概念挖到底。 上一篇文章里我们已经把递归跑了一遍:它本质是一种让机制变简单的编程技术,用很少的代码就能实现多层逻辑。但代价是执行可能变慢,真在 MT5 里跑大递归要留点耐心。 本文要接着用之前讲过的「定义(#define 宏)」:包括构建宏、声明,以及精细控制某些代码段——这些段可能由你或其他人写,你想以受控方式改掉其中一部分去达成某个目标。外汇和贵金属算法交易本身高风险,任何宏改动都先在策略测试器里验证再谈实盘。
用 #define 和 #undef 拿回代码控制权
在 MT5 的 MQL5 里,#define 不只是声明常量或宏,它真正的价值在于让编译期替你做替换,而不是在运行时占用变量空间。原文代码 01 里第 6 行写了 const uchar who = 6;,但第 12、20 行接收参数的是 uint 类型——类型不对付,靠人工反复改既烦又易错。
把常量换成编译指令后,编译器在预处理阶段直接把名字替换成字面量,等效代码和原版一模一样,但全局常量做不到中途销毁和重定义。下面这段就是原文代码 01 的骨架,注意第 6 行的 uchar 常量与后面 uint 函数的错位:
用 #undef 可以精准拆掉某个定义。原文代码 05 在第 20 行 #undef def_Fibonacci_Element_Default 之后,第 22 行再调用就会让编译器报“未定义”错误(图 01 指向第 22 行,根因在第 20 行)。很多老手习惯给编译指令加 def_ 前缀,不是为了规范,是为了一眼区分宏和普通常量。
更实用的是:同一个名字被 undef 之后立刻能赋新值。代码 06 第 22 行重定义后,输出结果和之前完全不同(图 02)。如果是全局 const,这两件事都干不了。外汇与贵金属 EA 开发里滥用全局常量,可能在多次策略切换时留下脏状态,用指令则能在编译层清零——当然,杠杆市场高风险,任何写法都不保证实盘收益。
class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#class="kw">property copyright "Daniel Jose" class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnStart(class="type">void) { const class="type">uchar who = class="num">6; Print("Result: ", Fibonacci_Recursive(who)); Print("Result: ", Fibonacci_Interactive(who)); } class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">uint Fibonacci_Recursive(class="type">uint arg) { if (arg <= class="num">1) class="kw">return arg; class="kw">return Fibonacci_Recursive(arg - class="num">1) + Fibonacci_Recursive(arg - class="num">2); } class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">uint Fibonacci_Interactive(class="type">uint arg) { class="type">uint v, i, c; for (c = class="num">0, i = class="num">0, v = class="num">1; c < arg; i += v, c += class="num">2) v += i; class="kw">return (c == arg ? i : v); } class=class="str">"cmt">//+------------------------------------------------------------------+
「给斐波那契函数塞默认值省得每次传参」
在 MT5 脚本里反复手写 Fibonacci_Recursive(6) 这种调用,既啰嗦又容易抄错数。用 #define 把元素序号固成宏,函数签名再带默认参数,调用处就能直接留空括号。
下面这段把默认元素定成 6,两个函数都吃这个默认值。OnStart 里不传参调用,终端大概率会先后打印第 6 个斐波那契数(递归与迭代都应得到 8)。外汇与贵金属杠杆高,跑这类小工具只为验证逻辑,别拿去当信号源。
[CODE]
#define def_Fibonacci_Element_Default 6
void OnStart(void)
{
Print("Result: ", Fibonacci_Recursive());
Print("Result: ", Fibonacci_Interactive());
}
uint Fibonacci_Recursive(uint arg = def_Fibonacci_Element_Default)
{
if (arg <= 1) return arg;
return Fibonacci_Recursive(arg - 1) + Fibonacci_Recursive(arg - 2);
}
uint Fibonacci_Interactive(uint arg = def_Fibonacci_Element_Default)
{
uint v, i, c;
for (c = 0, i = 0, v = 1; c < arg; i += v, c += 2) v += i;
return (c == arg ? i : v);
}
[/CODE]
逐行拆一下:第 4 行宏把 6 绑成默认序号;12 行函数形参写 = def_Fibonacci_Element_Default,不传就吃 6;20 行迭代版同样套路。24 行那个 for 用 c 步长 2 去追奇偶位置,27 行靠 c==arg 判断该返 i 还是 v,这套写法比递归省栈但读起来绕,开 MT5 单步一次就懂。
class="macro">#define def_Fibonacci_Element_Default class="num">6 class="type">void OnStart(class="type">void) { Print("Result: ", Fibonacci_Recursive()); Print("Result: ", Fibonacci_Interactive()); } class="type">uint Fibonacci_Recursive(class="type">uint arg = def_Fibonacci_Element_Default) { if (arg <= class="num">1) class="kw">return arg; class="kw">return Fibonacci_Recursive(arg - class="num">1) + Fibonacci_Recursive(arg - class="num">2); } class="type">uint Fibonacci_Interactive(class="type">uint arg = def_Fibonacci_Element_Default) { class="type">uint v, i, c; for (c = class="num">0, i = class="num">0, v = class="num">1; c < arg; i += v, c += class="num">2) v += i; class="kw">return (c == arg ? i : v); }