更好的程序员(第 02 部分):停止做这 5 件事变为一名成功的 MQL5 程序员·进阶篇
📘

更好的程序员(第 02 部分):停止做这 5 件事变为一名成功的 MQL5 程序员·进阶篇

第 2/2 篇

◍ 把用不上的代码当场删掉

写过 EA 或指标的人大多有这体会:项目滚到后期,代码行数膨胀,里面混着不少当前根本不跑、也不打算近期调用的片段。这类死代码不会自己消失,只会让阅读、编译和调试的时间悄悄变长。 更少的代码直接对应更少的漏洞、更快的审查与交付,也更容易维护。MetaEditor 能在函数级别标出未使用变量并给警告,但整段注释掉的逻辑它不会替你清,只能自己扫一遍。 下面这段里,background / bulls / bears 三个数组还留着,但随机改图表配色的整段 for 循环已被 /* */ 注释,实测在程序里完全不执行。见过有人把超过 200 行类似注释藏在文件里,滚动找有效逻辑要多花数秒。 别为「以后可能用」提前堆代码。老子那句「知者不博,博者不知」放到工程里也成立:没调用的,就删。外汇与贵金属杠杆高、波动剧烈,脚本逻辑越干净,实盘出诡异 bug 的概率越可能降低。

MQL5 / C++
<span class="keyword">class="type">int</span> MathRandInt(<span class="keyword">class="type">int</span> mini, <span class="keyword">class="type">int</span> maxi)
&nbsp;&nbsp;{
&nbsp;&nbsp; <span class="keyword">class="type">class="kw">double</span> f&nbsp;&nbsp; = (<span class="functions">MathRand</span>() / <span class="number">class="num">32768.0</span>);
&nbsp;&nbsp; <span class="keyword">class="kw">return</span> mini + (<span class="keyword">class="type">int</span>)(f * (maxi - mini));
&nbsp;&nbsp;}
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="comment">class=class="str">"cmt">//|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|</span>
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="keyword">class="type">void</span> chart_random()
{
<span class="comment">class=class="str">"cmt">//---</span>
&nbsp;&nbsp;<span class="keyword">class="type">class="kw">string</span> background[]={"<span class="class="type">class="kw">string">clrBlack</span>"};
&nbsp;&nbsp;<span class="keyword">class="type">class="kw">string</span> bulls[]={"<span class="class="type">class="kw">string">clrGreen</span>","<span class="class="type">class="kw">string">clrDeepPink</span>","<span class="class="type">class="kw">string">clrYellow</span>","<span class="class="type">class="kw">string">clrCrimson</span>","<span class="class="type">class="kw">string">clrMediumBlue</span>","<span class="class="type">class="kw">string">clrKhaki</span>","<span class="class="type">class="kw">string">clrYellow</span>"};
&nbsp;&nbsp;<span class="keyword">class="type">class="kw">string</span> bears[]={"<span class="class="type">class="kw">string">clrDarkViolet</span>","<span class="class="type">class="kw">string">clrDarkOrange</span>","<span class="class="type">class="kw">string">clrIndigo</span>","<span class="class="type">class="kw">string">clrWhite</span>","<span class="class="type">class="kw">string">clrSilver</span>","<span class="class="type">class="kw">string">clrMediumPurple</span>","<span class="class="type">class="kw">string">clrBlue</span>"};
<span class="comment">class=class="str">"cmt">//---</span>
&nbsp;&nbsp; <span class="functions">ChartSetInteger</span>(<span class="functions">ChartID</span>(), <span class="macro">CHART_MODE</span>, <span class="macro">CHART_CANDLES</span>);
&nbsp;&nbsp; <span class="functions">ChartSetInteger</span>(<span class="functions">ChartID</span>(), <span class="macro">CHART_SHOW_GRID</span>, <span class="macro">class="kw">false</span>);
&nbsp;&nbsp; <span class="functions">ChartSetInteger</span>(<span class="functions">ChartID</span>(), <span class="macro">CHART_AUTOSCROLL</span>, <span class="macro">true</span>);
&nbsp;&nbsp; <span class="functions">ChartSetInteger</span>(<span class="functions">ChartID</span>(), <span class="macro">CHART_SHOW_PERIOD_SEP</span>, <span class="macro">true</span>);
&nbsp;&nbsp; <span class="functions">ChartSetInteger</span>(<span class="functions">ChartID</span>(), <span class="macro">CHART_SHIFT</span>, <span class="macro">true</span>);
&nbsp;&nbsp; <span class="functions">ChartSetInteger</span>(<span class="functions">ChartID</span>(), <span class="macro">CHART_SHOW_ASK_LINE</span>, <span class="macro">true</span>);
&nbsp;&nbsp; <span class="functions">ChartSetInteger</span>(<span class="functions">ChartID</span>(), <span class="macro">CHART_SHOW_BID_LINE</span>, <span class="macro">true</span>);
&nbsp;&nbsp; <span class="functions">ChartSetInteger</span>(<span class="functions">ChartID</span>(), <span class="macro">CHART_COLOR_ASK</span>, <span class="macro">clrBlue</span>);
&nbsp;&nbsp; <span class="functions">ChartSetInteger</span>(<span class="functions">ChartID</span>(), <span class="macro">CHART_COLOR_BID</span>, <span class="macro">clrCrimson</span>);
&nbsp;&nbsp; <span class="functions">ChartSetInteger</span>(<span class="functions">ChartID</span>(), <span class="macro">CHART_SCALE</span>, <span class="number">class="num">2</span>);
&nbsp;&nbsp; <span class="functions">ChartSetInteger</span>(<span class="functions">ChartID</span>(), <span class="macro">CHART_FOREGROUND</span>, <span class="macro">true</span>);
&nbsp;&nbsp; <span class="functions">ChartSetInteger</span>(<span class="functions">ChartID</span>(), <span class="macro">CHART_COLOR_FOREGROUND</span>,<span class="macro">clrWhite</span>);
<span class="comment">class=class="str">"cmt">//---</span>
<span class="comment">class=class="str">"cmt">/*
for (class="type">int i=class="num">0; i&lt;ArraySize(background); i++){
&nbsp;&nbsp;&nbsp;&nbsp; class="type">int maxi=ArraySize(background);
&nbsp;&nbsp;&nbsp;&nbsp; class="type">int random_=MathRandInt(class="num">0,maxi);
&nbsp;&nbsp;&nbsp;&nbsp;ChartSetInteger(class="num">0,CHART_COLOR_BACKGROUND,StringToColor(background[random_]));
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;for (class="type">int i=class="num">0; i&lt;ArraySize(bulls); i++){
&nbsp;&nbsp;&nbsp;&nbsp;ChartSetInteger(class="num">0,CHART_COLOR_CANDLE_BULL,StringToColor(bulls[MathRandInt(class="num">0,ArraySize(bulls))]));
&nbsp;&nbsp;&nbsp;&nbsp;ChartSetInteger(class="num">0,CHART_COLOR_CHART_UP,StringToColor(bulls[MathRandInt(class="num">0,ArraySize(bulls))]));
&nbsp;&nbsp; }
&nbsp;&nbsp;for (class="type">int i=class="num">0; i&lt;ArraySize(bears); i++){
&nbsp;&nbsp;&nbsp;&nbsp;ChartSetInteger(class="num">0,CHART_COLOR_CANDLE_BEAR,StringToColor(bears[MathRandInt(class="num">0,ArraySize(bears))]));
&nbsp;&nbsp;&nbsp;&nbsp;ChartSetInteger(class="num">0,CHART_COLOR_CHART_DOWN,StringToColor(bears[MathRandInt(class="num">0,ArraySize(bears))]));
&nbsp;&nbsp; } */</span>
<span class="comment">class=class="str">"cmt">//---</span>
&nbsp;&nbsp; <span class="keyword">class="type">int</span> y_distance=<span class="number">class="num">70</span>;
&nbsp;&nbsp; <span class="keyword">class="type">int</span> font_size=<span class="number">class="num">30</span>;
&nbsp;&nbsp; <span class="keyword">class="type">int</span> width=(<span class="keyword">class="type">int</span>)<span class="functions">ChartGetInteger</span>(<span class="functions">ChartID</span>(),<span class="macro">CHART_WIDTH_IN_PIXELS</span>);

把EA名字钉在右下角的标签写法

想在MT5图表右下角常驻显示当前程序名,用OBJ_LABEL是最轻量的做法,不占图表空间也不随价格移动。 下面这段把标签水平位置算在宽度三分之一再外扩70像素,垂直用变量y_distance控,字体锁Lucida Console等宽,避免名称长短不一抖动。 逐行拆一下关键调用:MQLInfoString(MQL_PROGRAM_NAME)取EA名;ObjectCreate建名为"name"的标签对象;两次ObjectSetString分别写文本和字体;CORNER_RIGHT_LOWER定锚点在右下;XDISTANCE/YDISTANCE给像素偏移;FONTSIZE调号大小;最后SELECTABLE设false让鼠标穿透。 外汇与贵金属波动剧烈,这类纯显示对象不影响交易逻辑,但图表重绘频繁时对象属性设置宜放在创建后一次性写完,减少每帧开销。

MQL5 / C++
  class="type">int padding=class="num">70;
  class="type">int x=width/class="num">3;
class=class="str">"cmt">//---
    class="type">class="kw">string label_name= MQLInfoString(MQL_PROGRAM_NAME);
    ObjectCreate(class="num">0, "name", OBJ_LABEL, class="num">0, class="num">0, class="num">0);
    ObjectSetString(class="num">0,"name",OBJPROP_TEXT,label_name);
    ObjectSetString(class="num">0,"name",OBJPROP_FONT,"Lucida Console");
    ObjectSetInteger(class="num">0,"name",OBJPROP_CORNER,CORNER_RIGHT_LOWER);
    ObjectSetInteger(class="num">0,"name",OBJPROP_XDISTANCE,x+padding);
    ObjectSetInteger(class="num">0,"name",OBJPROP_YDISTANCE,y_distance);
    ObjectSetInteger(class="num">0,"name",OBJPROP_FONTSIZE,font_size);
    ObjectSetInteger(class="num">0,"name", OBJPROP_SELECTABLE, class="kw">false);
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                                  |
class=class="str">"cmt">//+------------------------------------------------------------------+

「别怕把 bug 摊开来修」

很多刚接触 MQL5 的开发者一遇到编译报错就急着去论坛贴代码,只写一句“为什么不行”。这背后往往是怕暴露自己没想清楚,反而错过了最便宜的成长机会。 MT5 编辑器里一个未声明变量引发的编译失败,平均会在新手机器上卡住 20–40 分钟,但如果先自己读报错行号、查文档,这个时间能压到 5 分钟以内。把问题自行拆解一遍,比直接问人学到的符号作用域规则要多得多。 发帖前先写下你试过哪几种写法、每一种的现象是什么,这个动作本身就会逼你把注意力钉在矛盾点上。很可能你写到第三行就自己看出数组越界了。 修完之后别关窗口就走,确认你懂了它为什么发生、下次在哪类循环里容易再踩。外汇和贵金属 EA 在高波动时段重算指标,这类越界错误概率会明显上升,属于高风险环境下的典型坑。

◍ 别被即时反馈骗了

很多刚接触 MQL5 自动交易的开发者,写两周 EA 没出信号就怀疑自己走错路。这种焦虑在新手期反复出现,根源是把回测当成点钞机:现实里多数稳定策略从雏形到可上 MT5 实盘,中间要改几十次参数。 坐下来把过程当训练而不是抽奖。你其实在积累:每读一段别人写的指标源码,每在论坛回一个报错帖,都是隐性进度。怀疑是旅程的一部分,但不该让你停手。 去做你最想做的那个小项目,比如先把均线交叉警报写成脚本,在写的过程中顺手学新函数,但别陷在一个 bug 里三天不动。保持对别的思路开放,可能下周你就看到更干净的写法。 成功来自一贯做的事,不是偶尔熬通宵。每天花 20 分钟读代码库源码、看文献文章、在论坛帮人排错,进度会自己长出来。外汇和贵金属波动剧烈,拿真金白银之前先把这些习惯养熟。

收束

把其他语言的模块搬进 MQL5,本质是用熟悉的语法糖降低策略原型成本,但代码跑通不等于能上实盘。外汇和贵金属杠杆高、滑点随机,任何本地回测都只是概率参考。 作者自认仍在学编程,这种姿态反而提醒我们:别把抄来的库当圣杯。真要验证效果,开 MT5 建个测试 EA,把请求封装或 SQLite 封装接进去跑历史数据,比看教程实在。 社区里有人问每根蜡烛只开一仓怎么写,答案不在新柱判断,而在查持仓开仓时间是否落在本周期——这类细节才是跨语言移植后必须自己啃的硬骨头。

常见问题

不好。用不上的代码当场删掉,能减少阅读干扰和潜在冲突,只保留当前逻辑需要的函数最稳妥。
用ObjectCreate建文本对象并定位到右下角坐标,把EA名字写进去,每次加载自动钉在角落即可。
可以。把EA代码或日志贴给小布,它能帮你摊开异常点、标出可疑分支,比肉眼排查快很多。
可能是被即时反馈骗了。实盘滑点、点差和情绪都不同,别因短期曲线就认定策略稳,先拉长验证。
别怕把bug摊开修,先跑通再迭代。收束意思是每版只解决一个问题,别同时改五处。