从基础到中级:定义(一)·进阶篇
🧩

从基础到中级:定义(一)·进阶篇

(2/3)· 递归之后,定义如何让你用一行指令切换常量、宏与代码版本而不动逻辑

偏理论进阶 第 2/3 篇
很多交易者写 EA 时改个手数或切换策略还要翻函数改硬编码,其实一条 #define 就能在编译期完成切换。理解定义的几种形态,是把重复劳动从写码阶段就消掉的关键一步。

斐波那契数列的两种 MT5 写法对比

在 MT5 脚本里算斐波那契数列,常见有递归和迭代两条路。下面这段脚本默认取第 6 项(def_Fibonacci_Element_Default = 6),OnStart 里同时打印两种算法的返回值,方便你直接开 MT5 跑一遍看差异。 递归版 Fibonacci_Recursive 逻辑直白:arg<=1 时直接返回 arg,否则返回前两项递归之和。代价是重复计算多,第 6 项尚可,项数一大栈开销和耗时就会明显膨胀。 迭代版 Fibonacci_Interactive 用 for 循环做状态累加,c 从 0 起、步长 2,i 与 v 交替进位,最后按 c==arg 决定返回 i 还是 v。同样算第 6 项,它只走三轮循环,效率远高于递归。 把默认宏改成 7 再跑一次,递归结果应为 13、迭代也为 13;外汇与贵金属杠杆高、波动剧烈,此类算法仅作工具验证,实盘信号请自担风险。

MQL5 / C++
class="macro">#class="kw">property copyright "Daniel Jose"
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="macro">#undef def_Fibonacci_Element_Default
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);
}

◍ 斐波那契交互函数的循环骨架

下面这段 MQL5 函数用极简循环生成类斐波那契序列,适合直接塞进指标里做动态层级计算。 传入参数 arg 默认取 def_Fibonacci_Element_Default,循环里用 c 计数、i 与 v 交替累加,每轮 c 加 2,因此 arg 为奇数偶数会走到不同返回分支。 当 c 恰好等于 arg 时返回 i,否则返回 v;这意味着调用 Fibonacci_Interactive(23) 会跑满 12 轮(c 从 0 到 22 再加 2 越界停止),返回的是越界前的 v 值。开 MT5 新建脚本粘贴下方代码,用 Print(Fibonacci_Interactive(23)) 即可核对数值。

MQL5 / C++
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);
}

「用编译指令切分斐波那契的两种算法」

在 MT5 里做同一段逻辑的多版本管理,不一定非得靠注释来回删改。MQL5 的 #define 配合 #ifdef / #ifndef / #else / #endif,能让你用一行开关决定编译器走哪段代码。 下面这段是最小可验证样本:第 4 行定义了 DEF_INTERACTIVE,于是第 13 行开始的 #ifndef 块(递归版)会被编译器直接忽略,真正进二进制的是后面 #ifdef 对应的迭代实现。把第 4 行注释掉再编译一次,终端输出就会换成递归版结果——两个版本只差一行,不碰函数体。 [CODE] //+------------------------------------------------------------------+

//控制斐波那契计算版本的最小样本

//+------------------------------------------------------------------+ #define DEF_INTERACTIVE #define def_Fibonacci_Element_Default 6 void OnStart(void) { Print("Result: ", Fibonacci()); } #ifndef DEF_INTERACTIVE uint Fibonacci(uint arg = def_Fibonacci_Element_Default) { if (arg <= 1) return arg; return Fibonacci(arg - 1) + Fibonacci(arg - 2); } #endif #ifdef DEF_INTERACTIVE uint Fibonacci(uint arg = def_Fibonacci_Element_Default) { uint a = 0, b = 1; for(uint i = 0; i < arg; i++) { uint t = a + b; a = b; b = t; } return a; } #endif [/CODE] 逐行拆一下关键点:第 4 行 #define 不需要赋值,存在即可被 #ifdef 捕获;第 13 行 #ifndef 表示「未定义才编译」,所以定义了就跳过递归版;第 25 行 #ifdef 正好相反,定义了才编译迭代版;第 6 行把默认项数 6 做成命名常量,改参只动这一处。 实测时保留第 4 行,默认算第 6 个斐波那契数得到 8;把第 4 行改成 //#define DEF_INTERACTIVE 后重编,递归版对同一个默认值同样返回 8,但调用栈深度差异巨大——递归版在 arg 稍大时(如 30)耗时是指数级,迭代版仍是线性。外汇 EA 里若把这类计算塞进 OnTick,版本选错可能直接拖垮执行性能。 顺带一提,#else 可以嵌在 #ifdef 块内,等价于 C 里 if/else 的编译期版本,省得写两遍对称结构。MQL5 没有 C 的 #if 数值判断,只能测「定义在不在」,所以别指望用 #define 值做条件表达式,老老实实靠有无定义切分支。

MQL5 / C++
class="macro">#define DEF_INTERACTIVE
class="macro">#define def_Fibonacci_Element_Default class="num">6

class="type">void OnStart(class="type">void)
{
   Print("Result: ", Fibonacci());
}

class="macro">#ifndef DEF_INTERACTIVE
class="type">uint Fibonacci(class="type">uint arg = def_Fibonacci_Element_Default)
{
   if (arg <= class="num">1) class="kw">return arg;
   class="kw">return Fibonacci(arg - class="num">1) + Fibonacci(arg - class="num">2);
}
class="macro">#endif

class="macro">#ifdef DEF_INTERACTIVE
class="type">uint Fibonacci(class="type">uint arg = def_Fibonacci_Element_Default)
{
   class="type">uint a = class="num">0, b = class="num">1;
   for(class="type">uint i = class="num">0; i < arg; i++)
   {
      class="type">uint t = a + b;
      a = b;
      b = t;
   }
   class="kw">return a;
}
class="macro">#endif

斐波那契函数里的两套实现路径

这段代码在同一文件里用 #ifdef DEF_INTERACTIVE 切出了两套斐波那契计算逻辑,编译时由宏开关决定走哪条路。关掉 DEF_INTERACTIVE 时是经典递归版,开启后则换成迭代版,两者默认都取 def_Fibonacci_Element_Default = 6 作为参数。 递归版在 arg <= 1 时直接返回自身,否则 Fibonacci(arg-1) + Fibonacci(arg-2),逻辑直观但重复计算多。迭代版用 for (c=0,i=0,v=1; c<arg; i+=v, c+=2) v+=i; 滚动累加,最后按 c == arg 判定返回 i 还是 v,避免了函数反复压栈。 实际在 MT5 里新建脚本,把 DEF_INTERACTIVE 注释掉跑一次,终端会打印 Testing 18 及结果 8;保留宏定义再跑,打印 Testing 32 且结果同为 8。外汇与贵金属杠杆高、滑点随机,这类纯算法验证请在策略测试器离线跑,别直接挂实盘。 想看迭代版到底省多少开销,把 def_Fibonacci_Element_Default 改成 30,递归版终端可能卡顿数秒,迭代版依旧瞬时返回——这条差异在写指标实时计算模块时值得记一笔。

MQL5 / C++
class="macro">#define DEF_INTERACTIVE
class="macro">#define def_Fibonacci_Element_Default   class="num">6
class="type">void OnStart(class="type">void)
{
   Print("Result: ", Fibonacci());
}
class="macro">#ifndef DEF_INTERACTIVE
class="type">uint Fibonacci(class="type">uint arg = def_Fibonacci_Element_Default)
{
   Print("Testing ", __LINE__);
   if (arg <= class="num">1)
      class="kw">return arg;
   class="kw">return Fibonacci(arg - class="num">1) +  Fibonacci(arg - class="num">2);
}
class="macro">#endif
class="macro">#ifdef DEF_INTERACTIVE
class="type">uint Fibonacci(class="type">uint arg = def_Fibonacci_Element_Default)
{
   class="type">uint v, i, c;
   Print("Testing ", __LINE__);
   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="macro">#endif
把编译期开关交给小布
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到不同编译宏下的策略表现对照,你专注决策就好。

常见问题

普通常量在运行期作为变量存在,而 #define 在编译期做文本替换,可生成宏或常量且不占运行时内存,适合做开关与轻量替换。
可以,小布盯盘的 AIGC 能按你给的策略参数输出含 #define 与版本控制的代码骨架,省去手敲编译指令的琐碎。
定义让编译器决定哪些代码参与编译,避免残留死代码与合并冲突,也方便在多个品种上快速切换同一套逻辑的不同变体。
需要一定嵌入式或跨语言编译经验,因为涉及外部环境的符号导出与联合编译,纯 MQL5 用户建议先从编译指令类定义上手。