MQL5 里运算符谁先算
MQL5 里运算符谁先算
写 EA 或指标时,表达式里混着加减乘除和位运算,结果常跟直觉拧着走。MQL5 沿用了类 C 的优先级规则,但很多从脚本语言转过来的交易者会默认「从左到右」,导致风控逻辑算错手数。
拿最常见的一行来说:double lot = balance * 0.01 + equity * 0.02; 这里乘法优先级高于加法,先各乘完再加总,不是先加 balance 和 equity。若你本意是「先加后按比例」,必须显式加括号,否则回测里仓位会偏薄或偏厚。
优先级表里还有个坑:位移 << >> 比加减低、比比较运算高。下面这段是终端里能直接编译验证的最小例子,把优先级差异打印出来。
class="type">int a = class="num">1 + class="num">2 << class="num">3; class=class="str">"cmt">// 先算 class="num">1+class="num">2=class="num">3,再左移3位 => class="num">24 class="type">int b = (class="num">1 + class="num">2) << class="num">3; class=class="str">"cmt">// 同上,括号冗余但清晰 => class="num">24 class="type">int c = class="num">1 + (class="num">2 << class="num">3); class=class="str">"cmt">// 先 class="num">2<<class="num">3=class="num">16,再加1 => class="num">17 Print(a, b, c);
◍ 从 FOR 到运算符的衔接点
前一节讲完了 FOR 语句的基础用法,能用它拼出不少简单脚本。对刚上手的人来说,哪怕只是写出不互动的脚本,只要自己跑通了,就是踏进 MQL5 实操的第一步。 这一节要往前推一层:运算符。之前提过的运算符偏基础,现在专门啃两块硬骨头——优先级规则,以及让很多人犯晕但真能省事的三元运算符。 进本节前有个硬前提:你得会在 MQL5 里声明和使用变量,前面文章铺过这部分。没把握的先回去补,否则下面代码拆着你容易断片。 外汇和贵金属代码跑在 MT5 上,杠杆高、波动猛,拿运算符练手先用模拟盘,别直接挂实盘。
「表达式里藏着优先级陷阱」
MT5 里运算符优先级表从上往下读,越靠上越先执行。但真正坑人的是分组:引用类运算符最高,类型/一元运算符从右往左算,到第三组基本算术才是从左到右。很多人第一次看会觉得反直觉,其实只要写过几轮就习惯,但写给别人看的代码若自己都讲不清,别人更懵。 看一段最直白的:char value=9,执行 ++value * 5。按优先级,++ 作为一元运算符从右往左,但这里乘法优先级低于 ++ 的副作用应用时机,实际先取 value=9 自增成 10 再乘 5?不对——代码 01 跑出来是 50,说明是 value 先以 9 参与乘 5 得 45,随后 ++ 生效。把 ++value 换成 value++(代码 02),打印顺序变,但乘完那行仍是 45 逻辑外的 50?其实 value++ 先取 9 乘 5 打印 45,再自增。验证方法只有开 MT5 贴进去跑。 复杂表达式里有个程序员潜规则:能用括号分层就别裸写优先级。不是强制规范,但能让接手的人秒懂。更狠的是代码 04 这类——编译器直接抛警告(图 05),却仍生成 exe。我们跑出结果是 8:先 1*v2=5,左移一位成 10,v1(9) 按位与 10 得 8,最后 v1 自增为 10。换了 ++ 位置(代码 05)结果变 10 而非 8,因为副作用应用点变了。 别把编译器警告当耳旁风 代码 04 编译时有警告却照跑,输出 8 看似合理,但外汇/贵金属 EA 里这种不可预测表达式可能引发错单。高杠杆环境下,一次位运算歧义足以让风控偏移。手写因子分解前,先加括号隔离执行层。 让小布替你跑这套 把代码 01、02、04、05 依次丢进 MT5 脚本,对比 Print 输出。你会发现同样 v1=9、v2=5,仅 ++ 位置不同,结果在 8 与 10 间跳。这比背优先级表管用。
class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#class="kw">property copyright "Daniel Jose" class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#define PrintX(X) Print("Factoring: { ", class="macro">#X, " } is: ", X) class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnStart(class="type">void) { class="type">char value = class="num">9; PrintX(value); PrintX(++value * class="num">5); PrintX(value); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#class="kw">property copyright "Daniel Jose" class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#define PrintX(X) Print("Factoring: { ", class="macro">#X, " } is: ", X) class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnStart(class="type">void) { class="type">char value = class="num">9; PrintX(value); PrintX(value++ * class="num">5); PrintX(value); }
运算符优先级与自增位置的坑
MT5 脚本里运算符优先级常被想当然,尤其位运算、乘法和自增混用时。下面三段代码都只改了一两个符号,输出却完全不同,直接开 MT5 跑一遍比看说明更快。
第一段里 v1 & 1 * v2 因乘法优先级高于位与,等价于 v1 & (1*v2),即 9 & 5 = 1;而 (v1 & 1) * v2 先取 9 的末位 1 再乘 5,得 5。PrintX 宏会把表达式原文和结果一起打印,方便对照。
第二段用了后置自增 v1++ & 1 * v2 << 1:v1 先以 9 参与运算(9 & 5 << 1 = 9 & 10 = 8),之后 v1 才变成 10。第三段换成前置 ++v1,v1 先变 10 再算,10 & 10 = 10,随后 v1 就是 10。
别把正态当圣经:以为自己记得优先级,不如把可疑式子加括号。外汇和贵金属杠杆高、波动快,EA 里一个运算符顺序写错,可能让仓位判断偏掉几个点。
class="macro">#class="kw">property copyright "Daniel Jose" class="macro">#define PrintX(X) Print("Factoring: { ", class="macro">#X, " } is: ", X) class="type">void OnStart(class="type">void) { class="type">char v1 = class="num">9, v2 = class="num">5; PrintX(v1); PrintX(v2); PrintX(v1 & class="num">1 * v2); PrintX((v1 & class="num">1) * v2); } class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#class="kw">property copyright "Daniel Jose" class="macro">#define PrintX(X) Print("Factoring: { ", class="macro">#X, " } is: ", X) class="type">void OnStart(class="type">void) { class="type">char v1 = class="num">9, v2 = class="num">5; PrintX(v1); PrintX(v2); PrintX(v1++ & class="num">1 * v2 << class="num">1); PrintX(v1); PrintX(v2); } class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#class="kw">property copyright "Daniel Jose" class="macro">#define PrintX(X) Print("Factoring: { ", class="macro">#X, " } is: ", X) class="type">void OnStart(class="type">void) { class="type">char v1 = class="num">9, v2 = class="num">5; PrintX(v1); PrintX(v2); PrintX(++v1 & class="num">1 * v2 << class="num">1); PrintX(v1); PrintX(v2); }
◍ 先把答案算出来再写代码
很多新手把编程当成「让计算机告诉我答案」,但在 MQL5 里更稳妥的思路是:动手写代码前,你心里先得有预期结果。这里对比代码 04 与代码 05 就能看出来——两段表达式语义相近,却可能输出图 06 与图 07 两种不同结果,而两者在各自优先级规则下都「没错」。你更可能想要的是图 07 那种,图 06 往往是后缀自增(++ 在变量后)被误用带来的副作用。 具体差异出在自增运算符的位置:代码 05 里 ++ 在前,自增优先于乘法;代码 04 里 ++ 在后,要等整个表达式算完才自增。只要给代码 04 的第 13 行加上括号明确定义顺序,编译器不再报「结果可能不可靠」的警告,输出就与图 07 一致。这一个括号的改动,正是很多人觉得运算符优先级「玄学」的根源。 回测或前测时,别急着信终端打印值。拿手算可验证的输入(比如 v1=3、v2=5)跑一遍,确认程序和你的预期吻合,才能说「这段逻辑算对了」。外汇与贵金属行情受杠杆与跳空影响,高风险,任何代码先在策略测试器用历史数据过小样本再上实盘。 别把编译器静默当正确 警告消失不代表逻辑如你所想,只是优先级被你显式锁死。前测时改一个输入值,看输出是否按手算走,比看一百次日志都管用。
PrintX((v1++ & class="num">1) * (v2 << class="num">1));
「把 IF 压成一行:三元运算符的读法」
三元运算符本质是一个压缩版的 IF,但它作为表达式存在,能直接塞进赋值或打印语句里,而不像 IF 那样只能控制代码块流向。原文示例里第 13 行 v1 * ((v2 & 1) == true ? v2 - 1 : v2 + 1) 就是典型用法:当 v2 为奇数时乘 v2-1,否则乘 v2+1。
代码里用了一个调试宏 PrintX,定义在第 4 行:#define PrintX(X) Print("Factoring: { ", #X, " } is: ", X)。带 # 的参数是预处理指令,编译器会把传入的代码片段原样转成字符串打印,方便你看清正在算什么。
把第 13 行展开成 IF 形式就很好懂:若 (v2 & 1) == true 则 value = v1 * (v2 - 1),否则 value = v1 * (v2 + 1)。这里的 value 只是编译器视角的临时量,你无法直接引用。三元结构不能容纳完整代码块,这是它和 IF 的根本区别。
跑这段代码会得到可验证现象:v1=9、v2=5 时,第 13 行算出 9*(5+1)=54;第 14 行 v2++ 后 v2 变 6,第 15 行因 6 为偶数仍走 v2+1 分支,算出 9*7=63。外汇与贵金属 EA 开发中用这类写法压缩逻辑时,需注意 MT5 运算符优先级,高风险品种下任何逻辑误差都可能放大滑点损失。
class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#class="kw">property copyright "Daniel Jose" class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#define PrintX(X) Print("Factoring: { ", class="macro">#X, " } is: ", X) class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnStart(class="type">void) { class="type">char v1 = class="num">9, v2 = class="num">5; PrintX(v1); PrintX(v2); PrintX(v1 * ((v2 & class="num">1) == true ? v2 - class="num">1 : v2 + class="num">1)); PrintX(v2++); PrintX(v1 * ((v2 & class="num">1) == true ? v2 - class="num">1 : v2 + class="num">1)); PrintX(v1); PrintX(v2); } class=class="str">"cmt">//+------------------------------------------------------------------+ if ((v2 & class="num">1) == true) value = v1 * (v2 - class="num">1); else value = v1 * (v2 + class="num">1); Print("Factoring: { v1 * ((v2 & class="num">1) == true ? v2 - class="num">1 : v2 + class="num">1) } is: ", value);
画得少,看得清
运算符这套东西,纸面上看理论绕,真到 MT5 里跑一遍就透亮了。每个运算结果摆出来,你会发现程序无非是把已知答案的问题用更快的方式重答一遍。 别把文档当圣经,开 MT5 把本文里的结构、继承例子逐条改参数跑一下,比看十遍说明都管用。外汇和贵金属杠杆高,验证时先用模拟盘,经验比理论值钱。 练到能预判某行代码在 EURUSD 的 M1 上会出什么数,这节才算过关。