从基础到中级:定义(一)·综合运用
🧩

从基础到中级:定义(一)·综合运用

(3/3)· 递归与 include 之后,收尾篇带你把宏、常量与跨语言导入玩成可控的实验台

含代码示例实战向 第 3/3 篇

很多交易者写 EA 时直接改硬编码参数,回测一次换一个数,版本乱到自己也分不清哪版用了什么止损。定义的价值正在于把这类微调收拢成一行指令,改一处全网生效,却少有人把它当成日常调试的默认习惯。

◍ 用宏切换斐波那契的迭代与递归实现

在 MT5 脚本里通过预处理器宏控制编译分支,可以同一份源码切换两种斐波那契算法。定义 DEF_INTERACTIVE 时走迭代,未定义时走递归,运行时由编译器裁掉另一半代码。 第一段示例里默认元素数设为 6,迭代循环用 c、i、v 三个变量步进:c 从 0 起每次加 2,i 与 v 交替累加,返回第 6 项时得到数值 8(序列 0,1,1,2,3,5,8)。 第二段把默认元素数改成 11,并新增 def_OPERATION 宏拼接日志前缀。此时若保留 DEF_INTERACTIVE,终端会打印 "Result of Interactive operation of element 11 : 89";注释掉该宏则走递归分支,输出 "Recursive" 前缀且同样返回 89。 别把宏当运行时开关 宏在编译期就定死分支,想在线切换算法必须改代码重编译,不适合做参数面板里的实时选项。真要对比性能,分别编译两次用 Print 计时更直观。

MQL5 / C++
class="macro">#class="kw">property copyright "Daniel Jose"
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#define DEF_INTERACTIVE "Interactive"
class=class="str">"cmt">//+----------------+
class="macro">#define def_Fibonacci_Element_Default   class="num">6
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnStart(class="type">void)
{
   Print("Result to ",
   class="macro">#ifdef DEF_INTERACTIVE
      DEF_INTERACTIVE, " : "
   class="macro">#else
      "Recursive : "
   class="macro">#endif
   , Fibonacci());
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">uint Fibonacci(class="type">uint arg = def_Fibonacci_Element_Default)
{
   class="macro">#ifdef DEF_INTERACTIVE

      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="macro">#else

      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=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#class="kw">property copyright "Daniel Jose"
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#define DEF_INTERACTIVE
class=class="str">"cmt">//+----------------+
class="macro">#ifdef DEF_INTERACTIVE
   class="macro">#define def_OPERATION "Interactive"
class="macro">#else
   class="macro">#define def_OPERATION "Recursive"
class="macro">#endif
class=class="str">"cmt">//+----------------+
class="macro">#define def_Fibonacci_Element_Default   class="num">11
class=class="str">"cmt">//+----------------+
class="macro">#define def_MSG_TERMINAL "Result of " + def_OPERATION + " operation of element"
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnStart(class="type">void)
{
   Print(def_MSG_TERMINAL, " ", def_Fibonacci_Element_Default, " : ", Fibonacci());
}
class=class="str">"cmt">//+------------------------------------------------------------------+

斐波那契的迭代与递归两套实现

这段 MQL5 代码给出了同一个 Fibonacci 函数的两种编译路径,由 DEF_INTERACTIVE 宏决定走哪条。默认不定义该宏时,走的是第 34–37 行的递归版本,直接 return Fibonacci(arg-1)+Fibonacci(arg-2),逻辑直观但重复计算严重。 若放开第 04 行的 // #define DEF_INTERACTIVE 注释,则启用第 25–30 行的迭代版:用 c、i、v 三个变量在 for 里滚动累加,循环步长 c+=2,最终按 c==arg 返回 i 或 v。实测 arg=11(即 def_Fibonacci_Element_Default 默认值)时,递归与迭代都给出 89 这个第 11 项斐波那契数。 代码后半段的宏封装(BEGIN_PROC、ENTER_POINT、Z_SET_NUMBERS 等)把语法糖到近乎伪代码,例如 ENTER_POINT 展开就是 void OnStart(void),MINOR 等价于 <。开 MT5 把这段贴进脚本,注释/取消第 04 行各跑一次,能从终端输出里直接比对两种算法的耗时差异。外汇与贵金属交易用这类数列做周期测算时,需注意杠杆品种的高风险,数列本身不预示价格方向。

MQL5 / C++
class="type">uint Fibonacci(class="type">uint arg = def_Fibonacci_Element_Default)
{
class="macro">#ifdef DEF_INTERACTIVE

   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="macro">#else   

   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=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#class="kw">property copyright "Daniel Jose"
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">// class="macro">#define DEF_INTERACTIVE
class=class="str">"cmt">//+----------------+
class="macro">#ifdef DEF_INTERACTIVE
    class="macro">#define def_OPERATION "Interactive"
class="macro">#else
    class="macro">#define def_OPERATION "Recursive"
class="macro">#endif
class=class="str">"cmt">//+----------------+
class="macro">#define def_Fibonacci_Element_Default   class="num">11
class=class="str">"cmt">//+----------------+
class="macro">#define def_MSG_TERMINAL "Result of " + def_OPERATION + " operation of element"
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#class="kw">property copyright "Daniel Jose"
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#define BEGIN_PROC         {
class="macro">#define END_PROC           }
class="macro">#define RETURN             class="kw">return
class="macro">#define ENTER_POINT        class="type">void OnStart(class="type">void)
class="macro">#define Z_SET_NUMBERS      class="type">long
class="macro">#define EQUAL              ==
class="macro">#define IS                 =
class="macro">#define MINOR              <
class="macro">#define OR                 ||

「用宏切换斐波那契的两种算法」

这段脚本用预编译宏把同一套斐波那契计算切成了交互迭代和递归两套实现,编译期靠 DEF_INTERACTIVE 是否定义来决定走哪条路。默认元素序号定在 11,终端会打印出「Result of Interactive operation of element 11 : 」这样的前缀,方便你直接看当前跑的是哪种模式。 交互分支里用了 for 循环做累加:c 从 0 计步、i 初值 0、v 初值 1,每次 v 累加 i、c 自增 2,直到 c 不小于目标序号;最后用三目运算返回第 11 项,实际算出来是 89(Fib(11)=89)。递归分支则是经典双递归,arg 小于等于 1 直接返回,否则返回前两项之和,代码短但重复计算多。 把第 15 行注释掉再编译,def_OPERATION 会变成 "Recursive",打印前缀随之改变,你能直观对比两种路径在 MT5 里的输出差异。外汇与贵金属波动剧烈,这类算法仅用于指标逻辑验证,实盘前务必充分回测,高风险。

MQL5 / C++
class="macro">#define MORE                +=
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#define DEF_INTERACTIVE
class=class="str">"cmt">//+----------------+
class="macro">#ifdef DEF_INTERACTIVE
    class="macro">#define def_OPERATION "Interactive"
class="macro">#else
    class="macro">#define def_OPERATION "Recursive"
class="macro">#endif
class=class="str">"cmt">//+----------------+
class="macro">#define def_Fibonacci_Element_Default   class="num">11
class=class="str">"cmt">//+----------------+
class="macro">#define def_MSG_TERMINAL "Result of " + def_OPERATION + " operation of element"
class=class="str">"cmt">//+------------------------------------------------------------------+
ENTER_POINT BEGIN_PROC  
    Print(def_MSG_TERMINAL, " ", def_Fibonacci_Element_Default, " : ", Fibonacci());
END_PROC
class=class="str">"cmt">//+------------------------------------------------------------------+
Z_SET_NUMBERS Fibonacci(Z_SET_NUMBERS arg IS def_Fibonacci_Element_Default)
BEGIN_PROC  
class="macro">#ifdef DEF_INTERACTIVE

    Z_SET_NUMBERS v, i, c;

    for (c IS class="num">0, i IS class="num">0, v IS class="num">1; c MINOR arg; i MORE v, c MORE class="num">2)
        v MORE i;

    RETURN(c EQUAL arg ? i : v);

class="macro">#else    

    if ((arg EQUAL class="num">1) OR(arg MINOR class="num">1))
        RETURN arg;

    RETURN Fibonacci(arg - class="num">1) + Fibonacci(arg - class="num">2);

class="macro">#endif
END_PROC
class=class="str">"cmt">//+------------------------------------------------------------------+

◍ 收束

这一节把前面拆开的五个 MQL5 示例都收进了随附的 Anexo.zip(2.65 KB),你解压后能在 MT5 的 MQEditor 里直接打开跑一遍,重点不是背语法,而是借这些奇怪的小例子把语言结构摸熟。 下一篇会转到 #define 的第二种用法——宏定义,和今天这五个例子用的是同一套练习程序,所以现在不练熟,后面很容易混淆。 外汇和贵金属杠杆高、回撤快,拿这些代码做实验务必先在策略测试器里用模拟品种跑,别直接上真实账户。

把重复劳动交给小布
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到编译指令层面的异常提示,你专注策略逻辑而非找括号漏写。

常见问题

预处理阶段 #define 做文本替换不占内存,const 是类型化只读变量;需要条件编译或宏展开时用 define 更灵活,纯数值约束用 const 更安全。
可以,小布盯盘在品种页会对外部参数与宏定义做静态扫描,标出未使用或冲突的定义,省去你手动翻代码的麻烦。
通常用 #import 配合外部头文件里的宏与常量,要求你了解原语言的调用约定;纯 MQL5 用户建议先吃透编译指令再碰嵌入式部分。
宏是全文替换,可能意外改到同名标识符;用更具前缀的宏名并配合编译警告,能降低这类隐性覆盖概率。