优秀程序员(第 01 部分):您必须停止做这 5 件事才能成为一名成功的 MQL5 程序员·进阶篇
◍ 先交代码再迭代
很多开发者卡在“圣杯思维”上:总想一次写出完美指标或EA,结果素材囤在硬盘里从没见过光。实际上 MT5 生态里几乎一切都在滚动更新——MQL5 语言本身、Python 库、市场里的指标和智能交易系统,版本号几乎逐月跳动。 连论坛的帖子、回复甚至自己的评论都能事后改,说明“先发布、后打磨”不是妥协,而是这个环境的默认工作流。 对客户或同行,写出“够用”的代码就够了;留出后续优化空间,比憋大招更省时间。一年堆出几百个能跑但不完美的片段,往往比三年磨一个“完美”EA 更有实战价值。 外汇与贵金属波动剧烈、杠杆风险高,任何脚本上线前都该在策略测试器里先跑历史数据,别把未验证代码直接挂真实账户。
把 EA 代码写得能让自己三个月后还认得
布赖恩·克尼汉那句老话在 MT5 开发里特别应景:调试难度是写代码的两倍,若写的时候拼命炫技,后面查错基本是在坑自己。外汇和贵金属 EA 波动快、逻辑易改,高杠杆环境下一段看不懂的代码可能让实盘止损形同虚设,干净可读比聪明更重要。 MetaEditor 里有个像发梳的蓝色按钮,点一下就能做样式化。下面这段按类型统计仓位的函数,原始缩进乱、括号错位,样式化后仅调整空白与换行,逻辑一字未改,可读性立刻不同: [CODE] int CountPosByType(ENUM_POSITION_TYPE type){ int counter = 0; for (int i=PositionsTotal(); i>=0; i--) if (m_position.SelectByIndex(i)) if (m_position.Magic()==MagicNumber && m_position.Symbol()==Symbol() && m_position.PositionType()==type) { counter++; } return counter; } int CountPosByType(ENUM_POSITION_TYPE type) { int counter = 0; for(int i=PositionsTotal(); i>=0; i--) if(m_position.SelectByIndex(i)) if(m_position.Magic()==MagicNumber && m_position.Symbol()==Symbol() && m_position.PositionType()==type) { counter++; } return counter; } int CountPositionsByType(ENUM_POSITION_TYPE type) int counter = 0; //Variable to store the number of positions [/CODE] 逐行拆解:第一行定义函数 CountPosByType,入参是仓位类型枚举;counter 初始化为 0,用来累计符合条件持仓。for 从 PositionsTotal() 倒序遍历,SelectByIndex 按序号选中持仓。内层两个 if 串联过滤:魔术码与当前图表品种匹配,且持仓类型等于入参,满足则 counter++。最后返回统计值。第二段只是同一函数的混乱排版版,第三段函数名改了却没写完整体,还顺手用注释标了 counter 用途——这正是“保持一致”和“加注释”的反面教材。 保持一致能直接省时间:用全局变量替代各函数内重复声明,函数名按“动宾+类型”统一,比如 CountPositionsByType。MetaEditor 的“在文件中查找”可跨 EA 搜函数,老项目里拷过来就能用,交付速度可能快到原来的 3 倍。注释不丢人,变量一多,//Variable to store the number of positions 这种一行就能唤回记忆。 别重复造轮子。已有 CountPositionsByType 统计某类仓位,就不需要再写个 CheckIfPosExistsByType 做近似判断——OOP 把共用逻辑收进类里,改动只发生在一处,调试半径最短。
class="type">int CountPosByType(class="type">ENUM_POSITION_TYPE type){ class="type">int counter = class="num">0; for (class="type">int i=PositionsTotal(); i>=class="num">0; i--) if (m_position.SelectByIndex(i)) if (m_position.Magic()==MagicNumber && m_position.Symbol()==Symbol() && m_position.PositionType()==type) { counter++; } class="kw">return counter; } class="type">int CountPosByType(class="type">ENUM_POSITION_TYPE type) { class="type">int counter = class="num">0; for(class="type">int i=PositionsTotal(); i>=class="num">0; i--) if(m_position.SelectByIndex(i)) if(m_position.Magic()==MagicNumber && m_position.Symbol()==Symbol() && m_position.PositionType()==type) { counter++; } class="kw">return counter; } class="type">int CountPositionsByType(class="type">ENUM_POSITION_TYPE type) class="type">int counter = class="num">0; class=class="str">"cmt">//Variable to store the number of positions
「可读性优先于代码长度」
不少 MQL5 开发者有种惯性:把行数压到最少、变量名缩到最短,就觉得代码更“高级”。实际在 MT5 里,一段 EA 源码在运维期会被翻开几十甚至上百次,字符省下的那点空间,远抵不过每次读不懂带来的调试成本。 我曾也犯过懒,接外包时只求跑通交差,把异常分支全砍了。后来实盘出问题才意识到:输入参数里手滑填了 0、或点差超限导致 OrderSend 返回错误码,用户面对沉默的 EA 完全不知道卡在哪。外汇与贵金属杠杆高,这类“哑火”可能直接错过行情或错单未平,风险不小。 正确做法是在 EA 输入区留清楚注释,对零值、越界、交易返回码做显式拦截。例如检测到手数输入为 0,就用 Comment() 在图表写提示,或 Print() 配合 Alert() 告诉操作者该填什么。错误码和异常是给你和用户指路的,不是用来吓人。 这些校验写起来枯燥,但别每次重写。建一个 include 头文件,把常用参数检查、错误码翻译函数塞进去,新 EA 直接 #include 或拷过来改改。长期看,可读性高的代码反而更短命少 bug。
◍ 收束
良好的编码习惯在 MQL5 里好处明显,但真正难的是长期执行。一旦把模块化、可复用那套跑顺了,后面写 EA 和指标会省力很多。 外汇和贵金属算法交易本身高风险,任何策略在历史表现再好也只是概率倾向,实盘前务必用 MT5 策略测试器自己跑一遍。 写到这,系列里提到的 Python 风格 SQLite3、Requests 库如果你还没动手移植,现在开 MT5 建个脚本试两行,比看十篇都实在。