更好的程序员(第 04 部分):如何成为更迅捷的开发人员·进阶篇

更好的程序员(第 04 部分):如何成为更迅捷的开发人员·进阶篇

(2/3)· 为什么有人几分钟写完复杂算法你却耗数天?本篇剥离设备与手速迷思,直击开发提速内核

偏理论 第 2/3 篇

很多人把写码慢归咎于键盘差、内存小或手速不行,实际这些都不是瓶颈。真正拖慢交付的,是缺乏函数库沉淀、代码绕弯和频繁走神。认清开发与编码的差异,才谈得上迅捷。

「别把标准库用成野路子」

MQL5 标准库是专业程序员按一致规范写的,强行用自己懒散的引用方式,代码迟早出灾难。最典型的坏习惯是把 CTrade 之类类随便起个 my_trade、ac 这种模糊名,下次读代码根本想不起它管什么。 正确姿势是用 m_ 前缀标明“某类成员”,比如 m_trade、m_symbol,并在每行 include 后补一句注释说明这个类负责哪块。下面代码上半段是反面教材,下半段是推荐写法,打开 MT5 新建 EA 把两段都贴进去对比编译后的可读性就知道了。 统计上,用标准库的编码者比从头手搓的人效率高 3 倍以上,且生成的程序代码量更少,意味着潜在漏洞也少。外汇和贵金属杠杆高、波动猛,少写错一行比多写一套自创逻辑更保命。 顺手把 SymbolAsk / SymbolBid 这种小函数也用标准方式包一层,比每次直接调 SymbolInfoDouble 更不容易拼错参数。

MQL5 / C++
class="macro">#include <Trade\AccountInfo.mqh> class=class="str">"cmt">//Class for working with trade account properties
class="macro">#include <Trade\SymbolInfo.mqh>  class=class="str">"cmt">//Class for working with trade instrument properties
class="macro">#include <Trade\OrderInfo.mqh>   class=class="str">"cmt">//Class for working with pending order properties
class="macro">#include <Trade\OrderHistoryInfo.mqh>   class=class="str">"cmt">//Class for working with history order properties
class="macro">#include <Trade\PositionInfo.mqh>   class=class="str">"cmt">//Class for working with open position properties
class="macro">#include <Trade\DealInfo.mqh>        class=class="str">"cmt">//Class for working with history deal properties
class="macro">#include <Trade\Trade.mqh>           class=class="str">"cmt">//Class for trade operations execution
class="macro">#include <Trade\TerminalInfo.mqh>   class=class="str">"cmt">//Class for getting the properties of the terminal environment
class=class="str">"cmt">//--- 
CAccountInfo         ac;
CSymbolInfo          symb;
COrderInfo           orders;
CHistoryOrderInfo    hist_orders;
CPositionInfo        pos;
CDealInfo            _clos;
CTrade               my_trade;
CTerminalInfo        info;
class="macro">#include <Trade\AccountInfo.mqh> class=class="str">"cmt">//Class for working with trade account properties
class="macro">#include <Trade\SymbolInfo.mqh>  class=class="str">"cmt">//Class for working with trade instrument properties
class="macro">#include <Trade\OrderInfo.mqh>   class=class="str">"cmt">//Class for working with pending order properties
class="macro">#include <Trade\OrderHistoryInfo.mqh>  class=class="str">"cmt">//Class for working with history order properties
class="macro">#include <Trade\PositionInfo.mqh>   class=class="str">"cmt">//Class for working with open position properties
class="macro">#include <Trade\DealInfo.mqh>        class=class="str">"cmt">//Class for working with history deal properties
class="macro">#include <Trade\Trade.mqh>           class=class="str">"cmt">//Class for trade operations execution
class="macro">#include <Trade\TerminalInfo.mqh>   class=class="str">"cmt">//Class for getting the properties of the terminal environment
class=class="str">"cmt">//--- 
CAccountInfo         m_account;  class=class="str">"cmt">//members of account info
CSymbolInfo          m_symbol;   class=class="str">"cmt">//members of symbolInfo
COrderInfo           m_order;    class=class="str">"cmt">//mambers of OrderInfo
CHistoryOrderInfo    m_orderhistory; class=class="str">"cmt">//members of Orderhistory info
CPositionInfo        m_position; class=class="str">"cmt">//members of position info
CDealInfo            m_deal;     class=class="str">"cmt">//members of deal info
CTrade               m_trade;    class=class="str">"cmt">//members of trade info
CTerminalInfo        m_terminal; class=class="str">"cmt">//members of terminal info
class="type">class="kw">double SymbolAsk(class="type">class="kw">string symbol)
{
        if (symbol == "") symbol = Symbol();
        class="kw">return SymbolInfoDouble(symbol, SYMBOL_ASK);
}
class="type">class="kw">double SymbolBid(class="type">class="kw">string symbol)
{
        if (symbol == "") symbol = Symbol();
        class="kw">return SymbolInfoDouble(symbol, SYMBOL_BID);
}
class="type">int SymbolDigits(class="type">class="kw">string symbol)
{

◍ 抓取品种精度与手数约束的两种写法

在 MT5 里做自动下单前,必须先拿到当前品种的报价位数和手数规则,否则可能出现价格四舍五入错位或报单被拒。下面这组独立函数用 SymbolInfoInteger / SymbolInfoDouble 直接查终端属性,入参留空时默认取当前图表品种。 if (symbol == "") symbol = Symbol(); return (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS); 上面一行先判断传参是否为空,空则回退到 Symbol() 当前品种;随后把 SYMBOL_DIGITS 强转 int 返回,EURUSD 通常返回 5,XAUUSD 常返回 2 或 3。 SYMBOL_VOLUME_STEP 与 SYMBOL_VOLUME_MIN / MAX 决定你每次能加多少手、边界在哪。比如多数券商 EURUSD 的 VOLUME_STEP 是 0.01,MIN 0.01、MAX 可能到 100,黄金的 STEP 也可能是 0.01 但 MIN 常为 0.01 且 MAX 被压到 20 附近,具体以你账户返回的 double 为准。 另一路是用标准库 CSymbolInfo 封装对象,在 OnInit 里 Name() 绑定品种,OnTick 里 RefreshRates() 后再取 Ask/Bid/Digits/ContractSize/LotsStep 等。这样做的好处是避免每帧重复拼函数名,但注意 RefreshRates 没跑就取价,可能拿到上一 tick 的陈旧值。外汇与贵金属杠杆高,手数算错会放大穿仓风险,开 MT5 把这两段分别挂不同品种跑一遍最踏实。

MQL5 / C++
if (symbol == "") symbol = Symbol();
class="kw">return (class="type">int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
}
class="type">class="kw">double SymbolLotSize(class="type">class="kw">string symbol)
{
if (symbol == "") symbol = Symbol();
class="kw">return SymbolInfoDouble(symbol, SYMBOL_TRADE_CONTRACT_SIZE);
}
class="type">class="kw">double SymbolLotStep(class="type">class="kw">string symbol)
{
if (symbol == "") symbol = Symbol();
class="kw">return SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
}
class="type">class="kw">double SymbolMaxLot(class="type">class="kw">string symbol)
{
if (symbol == "") symbol = Symbol();
class="kw">return SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);
}
class="type">class="kw">double SymbolMinLot(class="type">class="kw">string symbol)
{
if (symbol == "") symbol = Symbol();
class="kw">return SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
}
class="macro">#include <Trade\SymbolInfo.mqh>
CSymbolInfo   m_symbol;
class="kw">input class="type">class="kw">string Input_symbol = "EURUSD";
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Expert initialization function                                   |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int OnInit()
  {
class=class="str">"cmt">//---
    class="type">class="kw">string symbol = Input_symbol=="" ? Symbol() : Input_symbol;
    m_symbol.Name(symbol); class=class="str">"cmt">//Sets symbol name for our CSymbolInfo class object
class=class="str">"cmt">//---
   class="kw">return(INIT_SUCCEEDED);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Expert tick function                                             |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnTick()
  {
class=class="str">"cmt">//---
    m_symbol.RefreshRates(); class=class="str">"cmt">//Refresh to Get the Last quote 
    class="type">class="kw">double    SymbolAsk = m_symbol.Ask();
    class="type">class="kw">double    SymbolBid = m_symbol.Bid();
    class="type">class="kw">double    SymbolDigits = m_symbol.Digits();
    class="type">class="kw">double    SymbolLotSize = m_symbol.ContractSize();
    class="type">class="kw">double    SymbolLotStep = m_symbol.LotsStep();
    class="type">class="kw">double    SymbolMaxLot = m_symbol.LotsMax();
    class="type">class="kw">double    SymbolMinLot = m_symbol.LotsMin();
  }

把嵌套逻辑拆出去,别跟自己过不去

写过 EA 的人大多见过一段全挤在 if 里的代码:变量刚写完还认得,过两周回来调试,连 LbT、BEb 是干嘛的都得重新翻。这种写法不单折磨接手的人,最先坑的是自己。 上面这段就是典型——单层 if 里塞了价格归一化、极值更新、文件写入和调试打印,还嵌套了 FileOpen 的成功判断。真要改个写入逻辑,得在这堆括号里反复找落点,开发节奏直接拖慢。 别把全部操作压在一个函数。需要某类动作就单独建函数,只干一件事:比如把文件写入抽成 WriteTickBin(int tb),把极值更新抽成 UpdatePbRange()。每完成一块再进下一块,MT5 里单步跟起来也清爽。 下面这段代码原样留着,逐行拆一遍你能看清坑在哪: if(LbT>0) // 多头tick数大于0才进逻辑 { BEb=ND(BEb/LbT,Digits()); // 用Digits精度把BEb按LbT平均归一 if(BCa<0)BEb-=ND(BCa/PipVal2/(LbB-LbS),Digits()); // 空仓补偿为负时再减一项归一值

if(Pb>PbMaxPbMax==0)PbMax=Pb; // 刷新多头价上限
if(Pb<PbMinPbMin==0)PbMin=Pb; // 刷新多头价下限

if(!TradesOpen) // 交易文件未标记打开才写盘 { FileHandle=FileOpen(FileName,FILE_BIN|FILE_WRITE); // 二进制写方式开文件 if(FileHandle>-1) // 句柄合法才继续 { FileWriteInteger(FileHandle,TbF); // 写整型tick标识 FileClose(FileHandle); // 立即关句柄 TradesOpen=true; // 标记已写 if(Debug)Print(FileName+" File Written: "+TbF); // 调试开关下打印 } } } 外汇与贵金属杠杆高,EA 逻辑乱了容易在极端点差下误写信号,拆函数至少让回测和实盘排查少花几小时。

MQL5 / C++
                if(LbT>class="num">0)
                {       BEb=ND(BEb/LbT,Digits());
                        if(BCa<class="num">0)BEb-=ND(BCa/PipVal2/(LbB-LbS),Digits());
                        if(Pb>PbMax||PbMax==class="num">0)PbMax=Pb;
                        if(Pb<PbMin||PbMin==class="num">0)PbMin=Pb;
                        if(!TradesOpen)
                        {       FileHandle=FileOpen(FileName,FILE_BIN|FILE_WRITE);
                                if(FileHandle>-class="num">1)
                                {       FileWriteInteger(FileHandle,TbF);
                                        FileClose(FileHandle);
                                        TradesOpen=true;
                                        if(Debug)Print(FileName+" File Written: "+TbF);
                                }
                        }
                }

「别让手机和猫片吃掉你的45分钟」

交易策略开发最隐蔽的时间黑洞,不是行情震荡,而是你手贱点开社交软件的那一下。原文里有个扎心对比:走神的程序员可能耗5小时甚至更久写不出一段靠谱的MQL5,而专注的人45分钟就能干完。这差距不是智力,是干扰管理。 外汇和贵金属EA开发属于高风险脑力活,参数错一位点可能回测漂亮实盘爆仓。若在嘈杂环境,直接换场所、关手机、闭社交软件,把心神钉在订单逻辑上。项目卡壳时想刷Snapchat看猫片太正常,但限定自己:编码时段锁死,到点才放风。 小布盯盘建议的做法是,用MT5写代码前先设个45分钟番茄钟,手机扔远。能连续三天做到,你就能肉眼看到策略迭代速度翻倍。市场不等人,专注力才是散户最稀缺的阿尔法。

◍ 读文档比抄代码更能救急

在论坛里搜到能用的片段,先别急着 Ctrl+C 贴进 MT5。复制来的东西一旦在实盘环境报错,你连断点该打在哪都摸不着——因为成因不在你脑子里。我见过太多人卡在一段别人写的数组越界上,耗掉的时间比自己写还长。 MQL5 的官方文档里,数组、文件 I/O、HTTP 请求这些模块都有可直接跑的样例。你每多理解一个语言特性,后面把交易想法落成 EA 或指标的速度就上一个台阶。多数项目里最废时间的不是写,而是「找那段能用的正确代码」。 经验怎么来?就两句:学,然后试不同的事。熟的人知道在哪儿用 For/While 循环批量算均线,知道用文件暂存 tick 数据,也知道从网络服务器拉宏观日历——选择越多,绕路越少。外汇和贵金属杠杆高、滑点凶,自己写的逻辑起码你知道哪里会爆。

把重复诊断交给小布
这些关于代码结构和文档查阅的效率诊断,小布盯盘的 AIGC 已内置,打开对应品种页即可看到,你只需专注逻辑本身。

常见问题

开发人员通常负责设计、工时计划与编码全链路,编码人员多指经验较少、算法功底偏弱的执行者,二者在复杂 EA 交付中的负荷差异明显。
复用经过验证的模块减少重复造轮子,倾向让同类型订单逻辑、指标计算直接调用,降低出错与调试概率。
可以,小布内置的 AIGC 会对照常见结构冗余与文档缺失点给出提示,省去你人工复盘代码走神和绕弯的精力。
逻辑扁平化后,客户报漏洞时你能更快定位,可能在大批竞标者中抢先交付修订,外汇贵金属类 EA 高风险更需清晰可维护。
带着当前报错或模块目标去查,而非通篇浏览,配合定时锁屏强制专注,倾向把文档学习拆成 20 分钟块。