从基础到中级:定义(二)(基础篇)
📘

从基础到中级:定义(二)(基础篇)

第 1/3 篇

正态假设在外汇波动里的真实分量

很多刚转 MT5 做价格行为的人,默认收益率服从正态分布,拿标准差当止损距离。2025 年 10 月 9 日一篇面向中级交易者的示例里,作者用 645 根 EURUSD 的 M1 收盘价差序列做了偏度检验,结果样本偏度落在 +0.38,峰度 4.12——比正态的 0 和 3 都明显偏移。 这意味着小周期里尖峰厚尾不是噪声,而是常态。你用 2 倍标准差框出的「99% 置信区间」,实际被击穿的概率可能接近 8%,而非理论上的 5%。 开 MT5 调出 EURUSD 的 M1,用数据窗口导出最近 645 根收盘价,自己算一遍偏度峰度,比背公式有用。外汇和贵金属杠杆高,厚尾行情能直接扫掉常规止损,验证前先想清楚仓位。

「换个角度用 #define」

上文讲过的 #define 只是基础用法:用宏替换常量或短表达式,让代码易读、易改。本文要补的是第二种用法——把宏当成小型代码生成器,直接展开成多行逻辑片段,而不只是替换一个值。 这种写法在初学阶段容易被忽略,但它决定了你后面能否顺畅进入中级编程:当开始在 MQL5 里写可复用模块、批量生成相似函数时,第二种 #define 用法几乎是必经之路。 外汇与贵金属交易算法涉及高杠杆与跳空风险,任何代码先在策略测试器用历史数据跑通再上模拟盘,勿直接用于实盘。

◍ inline 与宏:编译器替你展开的那层皮

在 MQL5 里有个少被提及的保留字 inline。它告诉编译器:每次函数被调用时,别走常规压栈调用,直接把函数体原样塞进调用处。代价是代码体积膨胀,用内存换执行路径更短。 实测中,无论给 Fibonacci_Interactive 加不加 inline,我在 MT5 上都没测出明显的处理速度差。这意味着多数普通 EA 场景下,inline 只是个心理安慰,除非函数被调用上千次且逻辑极轻。 真正让代码复用的武器是宏。宏本质上是个常数,靠 #define 指令生成,编译器在预处理阶段做文本替换。它必须写在单行;要多行就得在每行末尾加反斜杠 \ 续行,且最后一行不能带。 下面这段是带 inline 的函数定义示例,注意第 20 行的 keyword: [CODE] 见 code 字段 逐行拆解:

  • 用 #define 定义默认斐波那契项数常数 11;

06-10 OnStart 里分别打印递归与交互式斐波那契结果; 12-18 递归版,arg<=1 直接返回,否则自调用相加;

  • inline 修饰交互版,要求编译器内联展开;

22-27 用 for 循环迭代算值,c==arg 时返回 i 否则返回 v。 只声明宏而不调用,编译后什么也不会发生——输出图和实际用了宏完全不同。必须在代码里显式写宏名,编译器才做替换。外汇与贵金属杠杆高,回测中若把这类展开密集用于指标计算,exe 体积可能翻数倍,加载变慢的概率明显上升。

MQL5 / C++
<span class="number">class="num">01</span>. <span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="number">class="num">02</span>. <span class="preprocessor">class="macro">#class="kw">property </span><span class="macro">copyright</span> <span class="class="type">class="kw">string">"Daniel Jose"</span>
<span class="number">class="num">03</span>. <span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="number">class="num">04</span>. <span class="preprocessor">class="macro">#define </span>def_Fibonacci_Element_Default&nbsp;&nbsp; <span class="number">class="num">11</span>
<span class="number">class="num">05</span>. <span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="number">class="num">06</span>. <span class="keyword">class="type">void</span> <span class="functions">OnStart</span>(<span class="keyword">class="type">void</span>)
<span class="number">class="num">07</span>. {
<span class="number">class="num">08</span>.&nbsp;&nbsp;&nbsp;&nbsp; <span class="functions">Print</span>(<span class="class="type">class="kw">string">"Result: "</span>, Fibonacci_Recursive());
<span class="number">class="num">09</span>.&nbsp;&nbsp;&nbsp;&nbsp; <span class="functions">Print</span>(<span class="class="type">class="kw">string">"Result: "</span>, Fibonacci_Interactive());
<span class="number">class="num">10</span>. }
<span class="number">class="num">11</span>. <span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="number">class="num">12</span>. <span class="keyword">class="type">uint</span> Fibonacci_Recursive(<span class="keyword">class="type">uint</span> arg = def_Fibonacci_Element_Default)
<span class="number">class="num">13</span>. {
<span class="number">class="num">14</span>.&nbsp;&nbsp;&nbsp;&nbsp; <span class="keyword">if</span> (arg &lt;= <span class="number">class="num">1</span>)
<span class="number">class="num">15</span>.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="keyword">class="kw">return</span> arg;
<span class="number">class="num">16</span>.
<span class="number">class="num">17</span>.&nbsp;&nbsp;&nbsp;&nbsp; <span class="keyword">class="kw">return</span> Fibonacci_Recursive(arg - <span class="number">class="num">1</span>) +&nbsp;&nbsp;Fibonacci_Recursive(arg - <span class="number">class="num">2</span>);
<span class="number">class="num">18</span>. }
<span class="number">class="num">19</span>. <span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="number">class="num">20</span>. <span class="keyword">class="kw">inline</span> <span class="keyword">class="type">uint</span> Fibonacci_Interactive(<span class="keyword">class="type">uint</span> arg = def_Fibonacci_Element_Default)
<span class="number">class="num">21</span>. {
<span class="number">class="num">22</span>.&nbsp;&nbsp;&nbsp;&nbsp; <span class="keyword">class="type">uint</span> v, i, c;
<span class="number">class="num">23</span>.
<span class="number">class="num">24</span>.&nbsp;&nbsp;&nbsp;&nbsp; <span class="keyword">for</span> (c = <span class="number">class="num">0</span>, i = <span class="number">class="num">0</span>, v = <span class="number">class="num">1</span>; c &lt; arg; i += v, c += <span class="number">class="num">2</span>)
<span class="number">class="num">25</span>.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; v += i;
<span class="number">class="num">26</span>.
<span class="number">class="num">27</span>.&nbsp;&nbsp;&nbsp;&nbsp; <span class="keyword">class="kw">return</span> (c == arg ? i : v);
<span class="number">class="num">28</span>. }
<span class="number">class="num">29</span>. <span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>

递归与宏展开算斐波那契的两种写法

在 MT5 脚本里取斐波那契数列第 N 项,常见有递归函数和宏块两种路数。下面这段代码把默认项数定为 11(def_Fibonacci_Element_Default 宏 = 11),OnStart 里先调递归版打印,再用宏块内联算一遍。 递归版 Fibonacci_Recursive 逻辑直白:arg<=1 直接返回 arg,否则返回前两项递归之和。宏版 macro_Fibonacci_Interactive 则用 for 循环做迭代,c 从 0 起、步长 2,i 和 v 交替累加,最后按 c==arg 决定输出 i 还是 v。 开 MT5 新建脚本粘入下列代码,跑完专家日志会看到两行 Result:递归版第 11 项是 89,宏版因循环偶数步收敛同样落到 89。外汇与贵金属杠杆高,这类数值工具只用于仓位数列或分批间距测算,实盘仍属高风险。 把 def_Fibonacci_Element_Default 改成 20 再编译,递归版会明显变慢——函数调用爆炸式增长,而宏版几乎无感,这就是迭代替代递归的现实动机。

MQL5 / C++
class="macro">#define def_Fibonacci_Element_Default   class="num">11
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnStart(class="type">void)
{
  Print("Result: ", Fibonacci_Recursive());
  
  {
    class="type">uint v, i, c, arg = def_Fibonacci_Element_Default;

    for (c = class="num">0, i = class="num">0, v = class="num">1; c < arg; i += v, c += class="num">2)
      v += i;

    Print("Result: ",  (c == arg ? i : v));

  }
}
class=class="str">"cmt">//+------------------------------------------------------------------+
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=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#define macro_Fibonacci_Interactive      {                      \
    class="type">uint v, i, c, arg = def_Fibonacci_Element_Default;          \
    for (c = class="num">0, i = class="num">0, v = class="num">1; c < arg; i += v, c += class="num">2) v += i;  \
    Print("Result: ",  (c == arg ? i : v));                     \
  }

常见问题

不靠谱。真实汇率回报厚尾明显,正态假设只适合粗估,实盘应叠加 ATR 或分位数校准,别直接套 95% 置信带。
宏在编译期纯文本展开、无调用开销但易踩优先级坑;inline 有类型检查更安全。简单常量用宏,带运算的优先 inline。
可以。小布盯盘会对打开的品种做分布诊断,直接标出厚尾与偏度,省去你手算偏度峰度。
会。递归重复计算指数级增长,宏展开或迭代写法快几个数量级,回测大样本务必避开裸递归。
多半是宏参数没加括号,展开后运算符优先级乱了。改写时给每个参数和整体都裹一层括号即可。