更好的程序员(第 04 部分):如何成为更迅捷的开发人员·综合运用
「用间隔重复把交易认知练成本能」
艾宾浩斯早就点明:比起短时间集中灌输,拉长周期、分次回看同一内容,人脑留存率明显更高。放到交易上,你记不住的形态、参数边界、品种波动习性,靠临盘前突击看一眼基本没用,得靠间隔温习挤进长期记忆。 MT5 交易者可以这么落地:每周固定挑一个自己最弱的环节——比如黄金在美盘时段的假突破分布、或某 EA 的止损逻辑漏洞——重读笔记并开历史回放验证一次。连续四周重复同一主题,比一天看四小时更容易变成‘盘中不假思索’的反应。 导致下单犹豫的核心往往不是心态,而是对该主题认知有空洞。找出这类空洞(本文后续会拆几类),用间隔方式反复补,直到你在实盘能异常流畅地处理它。外汇与贵金属杠杆高、回撤快,认知没固化就上仓位,风险会被放大。
把手指钉在键盘上:MetaEditor 提速配置
在 MT5 里写 EA 或指标,鼠标点菜单纯属浪费生命。把下面这套键位练成肌肉记忆,编码节奏能直接翻倍:F4 在终端和编辑器间瞬切,F7 编译当前文件,CTRL+F7 一次性编译所有打开标签;ALT+G 跳到函数或变量声明,CTR++ 和 CTRL+- 做前后向导航,比肉眼扫几千行快得多。 Mac 用户把 CTRL 映射成 COMMAND 即可复用同一张表。CTRL+SHIFT+U / CTRL+U 切换选中单词大小写,TAB 与 SHIFT+TAB 批量改缩进,SHIFT+方向键框选代码——这些在重构老策略时天天用得上。 编辑器选项里四个勾必须打:自动补闭括号与方括号、高亮匹配括号、高亮当前行、剪贴板历史。前两个能拦掉「unexpected end of program」那种藏在多层嵌套里的低级错;当前行高亮让你隔半天回来也能一秒定位光标;剪贴板历史则省去反复复制粘贴的折腾。 字体大小按你坐离屏幕的距离调,别为了多显示几行把字缩到瞅瞎眼。工具>选项里还有不少隐藏设定,挨个翻一遍,外汇与贵金属杠杆高、策略迭代快,少一次编译卡顿就少踩一轮实盘回测的坑。 代码分隔习惯也顺手规整下,下面这段注释模板可直接粘去当分节标头: //--- 将代码分隔符放到下一行 //+------------------------------------------------------------------+
| // |
|---|
//+------------------------------------------------------------------+ // 在行首添加两个正斜杠
class=class="str">"cmt">//--- 将代码分隔符放到下一行 class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| | class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">// 在行首添加两个正斜杠
◍ 用 site 限定把 MQL5 搜透
写 EA 时卡在「从二进制文件读数组」这类问题很常见,但直接丢一句「how to read array in binary file」去谷歌,返回结果能冲到 9800 万条以上,里面大半是灌水站,点错了还容易中钓鱼页。
想要快准狠,第一步是把范围锁死在 MQL5 社区内。在搜索词尾巴加 site:MQL5.com,同样的需求只回 568 条,且全部出自该站。
光锁站点还不够,MQL5 自身有文献、论坛、代码库几个板块,定位差很多。按 MQL5 板块 关键词 的写法先缩一次,比如 MQL5 forum how to read an array in a binary file,再拼上 site 限定成 forum how to read an array in a binary file site:MQL5.com,结果降到 399 条,基本条条能用。外汇与贵金属自动化策略开发高风险,参数和文件读写逻辑错一处就可能爆仓,搜到的代码务必在 MT5 策略测试器里先跑一遍再上实盘。
「把这条线请下神坛」
上面那段 MQL5 片段里,账户、品种、订单、持仓、成交、终端等信息类全部在 OnInit 里挂成成员,看似把基础设施一步铺齐,实则是把回测与实盘的环境假设焊死在初始化阶段。 CAccountInfo m_account; // 账户信息类成员,负责读取余额、杠杆等 CSymbolInfo m_symbol; // 品种信息类成员,点差、 tick 值从这里取 COrderInfo m_order; // 当前订单信息成员 CHistoryOrderInfo m_orderhistory; // 历史订单信息成员 CPositionInfo m_position; // 持仓信息成员 CDealInfo m_deal; // 成交信息成员 CTrade m_trade; // 下单执行类成员 CTerminalInfo m_terminal; // 终端信息成员 int OnInit() // 初始化事件入口 { // 开括号 // 下面是我的代码 .... } // 闭括号 ouble NPB=ND(m_account.Balance()*PortionPC,2);
| if(CbT==0 | PortChange<0 | (PortChange>0&&NPB>PortionBalance))PortionBalance=NPB; |
|---|
if(Pb+Ph<0)DrawDownPC=-(Pb+Ph)/PortionBalance;
if(!FirstRun&&DrawDownPC>=MaxDDPercent/100)
{ ET(A,displayColorLoss,"Equity Stop Loss Reached");
if(PlaySounds)PlaySound(AlertSound);
return(0);
}
if(-(Pb+Ph)>MaxDD)MaxDD=-(Pb+Ph);
MaxDDPer=MathMax(MaxDDPer,DrawDownPC*100);
if(SaveStats)Stats(false,TimeCurrent()<NextStats,PortionBalance,Pb+Ph);
注意其中 ouble NPB 少了首字母 d,属原文笔误,MT5 编译会直接报错;外汇与贵金属杠杆品种波动剧烈,回测里跑通的仓位比例控制逻辑,实盘可能因点差跳变让 PortionBalance 计算偏离。每天练手改这类片段能提效,但别把某次验证过的参数当成通用解。
CAccountInfo m_account; <span class="comment">class=class="str">"cmt">// AccountInfo 类的成员</span> CSymbolInfo m_symbol; <span class="comment">class=class="str">"cmt">//符号信息类的成员</span> COrderInfo m_order; <span class="comment">class=class="str">"cmt">// OrderInfo 成员</span> CHistoryOrderInfo m_orderhistory; <span class="comment">class=class="str">"cmt">// OrderHistoryInfo 类的成员</span> CPositionInfo m_position; <span class="comment">class=class="str">"cmt">// PositionInfo 类的成员</span> CDealInfo m_deal; <span class="comment">class=class="str">"cmt">// 交易信息类的成员</span> CTrade m_trade; <span class="comment">class=class="str">"cmt">// TradeInfo 类的成员</span> CTerminalInfo m_terminal; <span class="comment">class=class="str">"cmt">//终端信息类的成员</span> <span class="keyword">class="type">int</span> <span class="functions">OnInit</span>()<span class="comment">class=class="str">"cmt">// 这是 OnInit 事件</span> {<span class="comment">class=class="str">"cmt">//Opening bracket</span> <span class="comment">class=class="str">"cmt">//下面是我的代码</span> .... }<span class="comment">class=class="str">"cmt">//Closing bracket</span> ouble NPB=ND(m_account.Balance()*PortionPC,<span class="number">class="num">2</span>); <span class="keyword">if</span>(CbT==<span class="number">class="num">0</span>||PortChange<<span class="number">class="num">0</span>||(PortChange><span class="number">class="num">0</span>&&NPB>PortionBalance))PortionBalance=NPB; <span class="keyword">if</span>(Pb+Ph<<span class="number">class="num">0</span>)DrawDownPC=-(Pb+Ph)/PortionBalance; <span class="keyword">if</span>(!FirstRun&&DrawDownPC>=MaxDDPercent/<span class="number">class="num">100</span>) { ET(A,displayColorLoss,<span class="class="type">class="kw">string">"Equity Stop Loss Reached"</span>); <span class="keyword">if</span>(PlaySounds)<span class="functions">PlaySound</span>(AlertSound); <span class="keyword">class="kw">return</span>(<span class="number">class="num">0</span>); } <span class="keyword">if</span>(-(Pb+Ph)>MaxDD)MaxDD=-(Pb+Ph); MaxDDPer=<span class="functions">MathMax</span>(MaxDDPer,DrawDownPC*<span class="number">class="num">100</span>); <span class="keyword">if</span>(SaveStats)Stats(<span class="macro">false</span>,<span class="functions">TimeCurrent</span>()<NextStats,PortionBalance,Pb+Ph);