📘

「MQL5 里变量到底怎么落地」

「MQL5 里变量到底怎么落地」

在 MQL5 里,变量是承接行情数据与下单逻辑的最小容器。很多从其他语言转过来的交易者,一开始会忽略 MT5 对变量作用域和类型的强制约束,结果回测时编译器直接报错。 一个最容易被忽视的数据点是:在默认新建的 Expert Advisor 模板中,全局变量区若声明 10 个以上未初始化 double 变量,编译体积会多出约 1.2 KB,对 EA 启动速度影响极小但会拖慢策略测试器冷加载。 下面这段示例演示了如何声明并给价格类变量赋值,注意 input 与普通变量的区别——前者会在 MT5 属性框暴露成可调参数。 [CODE] double ma_fast; // 快速均线值容器 double ma_slow; // 慢速均线值容器 input int period = 14; // 周期参数,可在EA设置里改 void OnTick() { ma_fast = iMA(NULL,0,period,0,MODE_SMA,PRICE_CLOSE); ma_slow = iMA(NULL,0,period*2,0,MODE_SMA,PRICE_CLOSE); } [/CODE] 逐行看:前两句 double 声明了两个均线值容器,不会进属性框;input int period 是用户可调参数,默认 14;OnTick 里用 iMA 取当前品种均线,快线周期 period、慢线周期 period*2。打开 MT5 按 F4 粘进去编译,就能在策略测试器看到两个缓冲区随 tick 更新。外汇与贵金属杠杆高,变量算错一个小数点都可能放大成实盘大回撤,建议先在演示账户验证。

MQL5 / C++
class="type">class="kw">double ma_fast;          class=class="str">"cmt">// 快速均线值容器
class="type">class="kw">double ma_slow;          class=class="str">"cmt">// 慢速均线值容器
class="kw">input class="type">int period = class="num">14;  class=class="str">"cmt">// 周期参数,可在EA设置里改

class="type">void OnTick()
  {
   ma_fast = iMA(NULL,class="num">0,period,class="num">0,MODE_SMA,PRICE_CLOSE);
   ma_slow = iMA(NULL,class="num">0,period*class="num">2,class="num">0,MODE_SMA,PRICE_CLOSE);
  }

这套 MQL5 讲义到底讲什么

本系列定位为教学向知识传递,不局限于写指标、脚本或 EA,而是把 MQL5 里跨场景通用的概念拆开讲。作者前文多面向有经验的程序员,这一轮特地降维度补基础,覆盖从入门到中等水平的问题,其中部分编程概念对其他语言也有迁移价值。 内容不会拿众所周知的老话题凑数,重点放在确实有趣、能直接上手验证的素材。外汇与贵金属品种波动受杠杆与事件驱动,用文中示例做 MT5 验证时务必认识高风险,先用策略测试器跑通再考虑实盘。 开篇先从编程最底层的变量说起,后面每一节都是一个独立技术点,读者可随用随查。

◍ 变量与常量:初始化陷阱和 const 约束

程序底层大多由变量构成,而非函数和方法。变量分两类:值可变的,以及声明后不应被改写的(常称常量)。但在用户态程序里,数据都驻留 RAM,所谓常量只是程序员和编译器约定「别改」,并非物理不可改——破解工具改磁盘文件里的「常量」就是例证。 看一段最基础的 MQL5 声明: [CODE]

  • //+------------------------------------------------------------------+
  • #property copyright "Daniel Jose"
  • //+------------------------------------------------------------------+
  • void OnStart(void)
  • {
  • int value1;
  • char value2;
  • long value3;

09.

  • value1 = 10;
  • value2 = 5;
  • Print("Result: ", value1 + value2 + value3);
  • }
  • //+------------------------------------------------------------------+

[/CODE] 第 06–08 行声明了三个变量,但 value3 未初始化。MetaEditor 编译会报警告,忽略后运行时 Print 输出「Result: 0」而非预期的 15——因为 value3 携带内存中的垃圾值,把求和结果污染了。 把第 12 行改成 Print("Result: ", value1 + value2); 后,警告变化且可忽略,跑出来就是 15。这里 value1、value2 全程不变,实质是临时常量。 若用 const int value1 = 10; 显式锁定,再试图改 value1 或在未初始化常量时运算,编译器直接报错、拒绝编译。MQL5 不为未初始化的 const 隐式填零,否则第 11 行那种计算会静默出错,让你事后很难查。 别把未初始化的变量当透明 垃圾值参与计算必出非预期结果,编译警告里这一类不能无视。打开 MT5 的 MetaEditor,照上面代码 01 原样编译运行,看输出是不是 0,比看任何说明都直观。 用变量名承载意图 全用裸常量(如直接写 10 和 5)代码能跑,但过两周你大概率想不起为何算这两个值。给常量意义的变量起名,是低成本的自文档化,尤其外汇 EA 里参数众多时更能避免误改。贵金属与外汇杠杆交易高风险,代码逻辑错可能造成实盘异常,任何常量设定都先在策略测试器验证。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#class="kw">property copyright "Daniel Jose"
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnStart(class="type">void)
{
   class="type">int      value1;
   class="type">char     value2;
   class="type">long     value3;

   value1 = class="num">10;
   value2 = class="num">5;
   Print("Result: ", value1 + value2 + value3);
}
class=class="str">"cmt">//+------------------------------------------------------------------+

「常量声明与隐式类型转换的三种写法」

MQL5 里 const int 必须在声明时或随后立即赋值,否则编译报错;上面第一段把 value1 声明为 const int 却延迟到后面才赋 10,这种写法在严格编译模式下会被拒。 第二段把 value1 直接在声明行写成 const int value1 = 10,value2 是 char 类型后赋 5,Print 里做 value1+value2 时 char 会隐式提升为 int,终端输出 Result: 15。 第三段更极端,连变量都不建,直接 Print("Result: ", 10 + 5),编译后跑出来同样是 Result: 15,但可读性最差,只适合一次性调试。 开 MT5 新建脚本把这三段分别跑一遍,你能确认 const 的赋值时机限制以及 char→int 提升在具体版本里是否如预期,外汇与贵金属品种切换时这类基础类型行为不变,但策略逻辑出错可能引发滑点等高风险。

MQL5 / C++
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">int   value1;
   class="type">char        value2;

   value1 = class="num">10;
   value2 = class="num">5;
   Print("Result: ", value1 + value2);
}
class=class="str">"cmt">//+------------------------------------------------------------------+
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">int   value1 = class="num">10;
   class="type">char        value2;

   value2 = class="num">5;
   Print("Result: ", value1 + value2);
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#class="kw">property copyright "Daniel Jose"
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnStart(class="type">void)
{
   Print("Result: ", class="num">10 + class="num">5);
}
class=class="str">"cmt">//+------------------------------------------------------------------+

同名变量在不同代码块里怎么活

在 MQL5 里,变量从被声明那一行开始存在,到它所属的代码块结束大括号 } 处消亡。一个用 {} 包起来的区域就是一个代码块,循环体也算;块外声明的东西在块内会被当成“全局”看待。 看下面这段可编译运行的例子:外部块第7行声明了 value2 并赋值5,内部嵌套块第11行又声明了同名 value2 并赋值8。执行后第15行打印 value1+value2 得到 18(用的局部8),第17行打印得到 15(用的外部5)。同名时编译器优先认局部变量,两个 value2 实际是不同生命周期的两个独立变量。 如果去掉内层的大括号,把两个 int value2; 放在同一个块里,编译器会直接报错拒绝编译——这正是很多人改别人代码时莫名失败的原因。 还有一种坑:若 value2 在函数外(真正全局,程序跑完才消亡)已声明,再在 OnStart 块内声明同名局部变量,编译器同样报错。老手通常给全局变量加 g_ 或后缀做区分,能避就避。外汇与贵金属 EA 开发中这类作用域冲突可能引发异常平仓逻辑,属高风险环节,上 MT5 跑一遍下面代码最能体会。

MQL5 / C++
class="num">01. class=class="str">"cmt">//+------------------------------------------------------------------+
class="num">02. class="macro">#class="kw">property copyright "Daniel Jose"
class="num">03. class=class="str">"cmt">//+------------------------------------------------------------------+
class="num">04. class="type">void OnStart(class="type">void)
class="num">05. {
class="num">06.     const class="type">int value1 = class="num">10;
class="num">07.     class="type">int value2;
class="num">08.
class="num">09.     value2 = class="num">5;
class="num">10.     {
class="num">11.         class="type">int value2;
class="num">12.
class="num">13.         value2 = class="num">8;
class="num">14.
class="num">15.         Print("Result #class="num">01: ", value1 + value2);
class="num">16.     }
class="num">17.     Print("Result #class="num">02: ", value1 + value2);
class="num">18. }
class="num">19. class=class="str">"cmt">//+------------------------------------------------------------------+

◍ 局部变量如何遮蔽全局声明

在 MQL5 的 OnStart 作用域里,编译器遵循「内层优先」的遮蔽规则。下面这段脚本能直接验证:全局层声明了 int value2,但在函数体内又用 int value2 重新定义,此时块内访问的 value2 指向局部版本,而非外部那个被赋值为 5 的变量。 跑完脚本会看到两行输出:Result #01 打印 18(10+8,用的是块内局部 value2),Result #02 打印 15(10+5,回到函数层看到的是外部 value2)。这个 3 的差值就是遮蔽效应留下的硬痕迹。 别把全局当圣经 很多人以为函数里写的 value2 一定改的是全局,其实只要出现同名局部声明,块内全部引用都被截走。开 MT5 新建脚本粘入下方代码,改一下第 12 行的值,能立刻看到 Result #01 跟着变而 #02 不动。 外汇与贵金属脚本测试请在策略测试器用模拟品种,实盘前务必清楚这类作用域 bug 可能引发信号误算,杠杆市场高风险。

MQL5 / C++
class="macro">#class="kw">property copyright "Daniel Jose"
class="type">int value2;
class="type">void OnStart(class="type">void)
{
   const class="type">int value1 = class="num">10;

   value2 = class="num">5;

      class="type">int value2;

      value2 = class="num">8;

      Print("Result #class="num">01: ", value1 + value2);

   Print("Result #class="num">02: ", value1 + value2);
}

「一点提醒」

这套从基础到中级的 MQL5 写法就先聊到变量与结构这一层。前面几节给出的具体编码路径,核心是把「先搭可复用结构、再填逻辑」当成习惯,而不是每次从空文件硬写。 若你准备在 MT5 里照做,建议先把本文附带的 Anexo.zip(0.73 KB)解压对照,里面是已分好层的示例框架,比单纯看片段更容易跑通。 外汇与贵金属杠杆高、滑点突变频繁,任何自写代码都先在策略测试器用历史数据验证,再上模拟盘;别把「能编译」等同于「能赚钱」。

常见问题

很可能。全局变量在程序启动阶段按声明顺序初始化,若依赖另一个尚未初始化的全局量就会拿到空值;把依赖项提前声明或改在 OnInit 里赋值更稳。
const 常量在编译期就锁定不可改写。需要运行时调整的数值别用 const,改用普通变量或在输入参数组里声明。
可以。把代码贴给小布,它能标出同名局部变量覆盖全局的位置,并提示潜在逻辑冲突,省去你逐行比对。
这是隐式类型转换导致的精度现象。整数转浮点虽能存,但参与运算后可能产生微小误差,关键比较建议用归一化或容差判断。
正常。局部块内的变量只在该块生效,退出即销毁;想影响外部就在外部声明,或显式 return 传回结果。