更好的程序员(第 06 部分):9 个导致有效编码的习惯(基础篇)
📘

更好的程序员(第 06 部分):9 个导致有效编码的习惯(基础篇)

第 1/2 篇

「九个拖慢你 EA 开发进度的编码陋习」

在 MT5 里写 EA 或指标,真正卡住进度的往往不是算法难,而是几个反复出现的编码习惯。根据 2022 年 1 月社区一篇阅读量 1427 的实操梳理,有 9 类习惯最容易导致有效编码被打断:硬编码魔法数字、忽视 OnTick 与 OnCalculate 的调用频率差异、把打印调试当常规日志、不做输入参数校验、用全局变量代替类成员、循环里重复调用 iMA 等序列函数、不处理历史数据不足返回空值、注释与逻辑脱节、以及不区分测试环境与实盘环境。 这些习惯单独看都不致命,但叠加之后会让回测和实盘行为不一致。比如 iMA 在每根 tick 里重复取句柄,不仅拖慢执行,还可能在点差跳变时返回意料外的缓冲偏移。 打开你最近一个 EA 的 OnTick,搜一下是否在循环内调用了指标取数函数,如果有,先把它提到循环外缓存句柄,这是当天就能验证的一个改法。外汇与贵金属杠杆交易高风险,改动后请在策略测试器用 2021 年 XAUUSD 数据跑一遍确认。

◍ 动键盘前先把纪律钉死

很多交易者写 EA 时都有过这种状态:手指在敲,脑子已飘,回头一看三小时只写出一段逻辑还全是坑。低效的根源通常不是不会写,而是缺计划、无节奏、被消息弹窗切碎注意力。 动手前先列颗粒化清单:把「读取均线斜率」「判断影线占比」「发通知」拆成单用途小函数,每个函数写完立刻用 Print 自测,而不是堆到终盘才编译。给半年后的自己留注释,不是写「这是循环」,而是写「这里用 iBarShift 是为了避开离线图重绘错位」。 盲打和版本控制是隐性杠杆。盲打省下的视线往返时间按一次 EA 迭代 2000 次击键算,可能省出 15% 工时;Git 提交记录让你在参数改崩时能回退到「上周还能跑」的版本。MT5 自带的 MetaEditor 配合外部版本工具即可,不挑贵贱,顺手就行。 外汇与贵金属杠杆高、滑点随机,任何自写代码未经实盘前都只是概率玩具。上面这些习惯不保证盈利,但能让你在下次重看代码时少骂两句自己。

动手写EA前先把逻辑落到纸面

随便问 100 个程序员,在敲键盘前真把系统规划清楚的,恐怕不到 10 个。做 MQL5 开发也是一样,没想明白就写代码,后期改结构的成本远高于前期列清楚。 你可以把它当成上车前得知道去哪——跳上公交乱坐不可能到目的地。动手前先记下来:要写的程序类型、要用到的工具、打算调用的函数库。自由职业平台里的「工作规范」本质就是这份规划,不特殊,但必须有。 手边没纸笔就用 Word 或 WPS 画流程图,把开仓条件、资金规则、调用的标准类库先描一遍。查尔斯·凯特林那句「问题说清楚就解决了一半」放在 EA 开发里很贴切:模糊的念头直接写代码,基本会卡在半路。 举个最小例子,只想做一个带 Labouchere 资金管理的网格 EA,初始值先这样定(均可后续优化):Lotsize=0.01、xgrid=100 点、LotIncrement=0.01。规则写明白后,代码只是翻译:无持仓时双向各开一单;买条件为无买仓或现 Bid 低于末笔买仓开仓价减 xgrid,手数=0.01+0.01×买仓数;卖条件为无卖仓或 Ask 高于末笔卖仓开仓价加 xgrid,手数同理累加;默认 SL=0、TP=100。配标准仓位/品种/交易类库。 这样一份规划摆着,写代码时不会漏分支,也方便回头查自己哪块 MQL5 类库还不熟。外汇与贵金属杠杆高、网格策略回撤风险大,规划归规划,真跑之前务必在 MT5 策略测试器用历史数据验过。

「把网格逻辑收进一个 mqh 模块」

写网格类 EA 时,按持仓类型统计笔数、取最后一笔同类型持仓的开仓价,这两个动作几乎每个网格程序都要用。与其在每一个 .mq5 里重写一遍,不如抽成一个 gridmodule.mqh 包含文件,主程序用 #include 挂进来就能复用。 这么做最直接的回报是调试面缩小:函数逻辑只在一处改,主文件代码量明显下降。以一个典型网格 EA 为例,抽出这两函数后,主文件里至少能少写 30~50 行重复代码。 下面这段是 gridmodule.mqh 的骨架,已经把持仓信息类和品种类挂进来了,并声明了 CGrid 类。注意 MagicNumber 放在 protected,外部只能通过 InitializeModule(int magic) 注入,避免乱改标识。 别把模块写成万能库 只放真正跨 EA 通用的两块逻辑,塞太多无关函数反而让编译依赖变重,MT5 里追调用栈也更费劲。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                                 gridmodule.mqh |
class=class="str">"cmt">//|                                                    Copyright class="num">2021, Omega Joctan |
class=class="str">"cmt">//|                                         [MQL5官方文档] |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#class="kw">property copyright "Copyright class="num">2021, Omega Joctan"
class="macro">#class="kw">property link      "[MQL5官方文档]
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Libraries                                                                  |
class=class="str">"cmt">//+------------------------------------------------------------------+ 
class="macro">#include <Trade\PositionInfo.mqh>
class="macro">#include <Trade\SymbolInfo.mqh>
CSymbolInfo   m_symbol;
CPositionInfo m_position;
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| DLL imports                                                                |
class=class="str">"cmt">//+------------------------------------------------------------------+
class CGrid
  {
   class="kw">protected:
      class="type">int                MagicNumber;

   class="kw">public:
                     CGrid(class="type">void);
                    ~CGrid(class="type">void);
      class="type">void           InitializeModule(class="type">int magic) { MagicNumber = magic; }
      class="type">class="kw">double         LastPositionOpenPrice(class="type">ENUM_POSITION_TYPE type);
      class="type">int            CountPositions(class="type">ENUM_POSITION_TYPE type);

   };
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|          Constructor                                                |
class=class="str">"cmt">//+------------------------------------------------------------------+
CGrid::CGrid(class="type">void)
{
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|           Destructor                                                |
class=class="str">"cmt">//+------------------------------------------------------------------+
CGrid :: ~CGrid(class="type">void)
{
}
class=class="str">"cmt">//+------------------------------------------------------------------+

常见问题

动键盘前先把交易逻辑落到纸面,画清开平仓条件和参数边界,能少写一半废代码。
把网格逻辑收进一个独立模块文件,主程序只调接口,后续加止损或改间距只动一处。
小布可对照你的策略描述检查逻辑漏洞,提示未模块化、缺边界条件等常见陋习,省去自我复查时间。
改成外部输入参数,回测时直接拉数值对比,不必重新编译。
动手前用三行写清「什么市况不做、单笔最大风险、模块分工」,贴屏幕边照此写。