更好的程序员(第 04 部分):如何成为更迅捷的开发人员·进阶篇
(2/3)· 为什么有人几分钟写完复杂算法你却耗数天?本篇剥离设备与手速迷思,直击开发提速内核
很多人把写码慢归咎于键盘差、内存小或手速不行,实际这些都不是瓶颈。真正拖慢交付的,是缺乏函数库沉淀、代码绕弯和频繁走神。认清开发与编码的差异,才谈得上迅捷。
「别把标准库用成野路子」
MQL5 标准库是专业程序员按一致规范写的,强行用自己懒散的引用方式,代码迟早出灾难。最典型的坏习惯是把 CTrade 之类类随便起个 my_trade、ac 这种模糊名,下次读代码根本想不起它管什么。 正确姿势是用 m_ 前缀标明“某类成员”,比如 m_trade、m_symbol,并在每行 include 后补一句注释说明这个类负责哪块。下面代码上半段是反面教材,下半段是推荐写法,打开 MT5 新建 EA 把两段都贴进去对比编译后的可读性就知道了。 统计上,用标准库的编码者比从头手搓的人效率高 3 倍以上,且生成的程序代码量更少,意味着潜在漏洞也少。外汇和贵金属杠杆高、波动猛,少写错一行比多写一套自创逻辑更保命。 顺手把 SymbolAsk / SymbolBid 这种小函数也用标准方式包一层,比每次直接调 SymbolInfoDouble 更不容易拼错参数。
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 把这两段分别挂不同品种跑一遍最踏实。
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>PbMax | PbMax==0)PbMax=Pb; // 刷新多头价上限 |
|---|---|
| if(Pb<PbMin | PbMin==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 逻辑乱了容易在极端点差下误写信号,拆函数至少让回测和实盘排查少花几小时。
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 数据,也知道从网络服务器拉宏观日历——选择越多,绕路越少。外汇和贵金属杠杆高、滑点凶,自己写的逻辑起码你知道哪里会爆。