说个实话,用MT4写EA这几年,最让人崩溃的从来不是自己代码写错了,而是那些官方文档压根没写、你只能在论坛犄角旮旯里翻到的"特性"。今天说两个我花了好几天才搞明白的坑,都跟语法和平台机制有关,跟DLL那些常规问题完全不搭边。
坑一:Market Watch里的品种会自己"繁殖"
这个事儿第一次遇到的时候我差点把电脑砸了。你的EA本来只扫描Market Watch里5个品种,正常开仓平仓。过了一个小时回来看,好家伙,品种列表变成了8个。再过一会儿,29个。最后整个经纪商提供的所有品种全在里面了。
这玩意儿不是bug,是"特性"。没写在文档里的那种。
MQL5官方论坛里MetaQuotes的开发人员stringo亲口承认过:当你的EA在Market Watch里扫描品种并开仓时,MT4会把任何涉及利润或保证金计算的品种自动加回到Market Watch列表里。你的EA如果引用EURUSD来计算某个东西,这个品种就会被"隐含包含"。下一个扫描周期,EA看到更多品种,开更多仓位,再加更多品种,循环往复,根本停不下来。
开发人员的原话是:"这个行为在更新MQL4之前很久就存在了。即,隐含地包含参与利润和保证金计算的品种(如果它们之前没有被包含)"。他们承认这是未文档化的行为,也承认用户想获得一个"品种是否被隐藏"的信息来规避这个问题,但最终没改。
我的解决办法:运行任何会扫描Market Watch的EA之前,手动把所有不想交易的品种从列表里删掉。然后EA初始化的时候,用
SymbolSelect()明确把不需要的品种设为不可见。这个操作放在OnInit()里,只跑一次,别放OnTick()。如果你在同一台终端跑多个EA,每个EA单独挂一张图,用魔术数字严格区分仓位归属。坑二:if判断里只有0才是假的
这个问题更底层,但造成的后果更隐蔽。很多人以为if条件里非0就是真、0就是假,这个认知本身没错。但坑在于什么情况下函数返回0,什么情况下返回别的数。
简单测试:
if(1)是真,if(2)是真,if(-1)是真,if(0.1)也是真。只有0是假。正数负数整数小数,统统算真。然后问题来了。
OrderSend()如果失败,返回的是-1,不是0。新人常犯的错误是写if(OrderSend(...)),以为订单失败会返回0。不会,-1在if里被当作真,所以EA以为订单成功了,继续执行后续只应该在开仓成功后运行的代码。这个坑导致的无意义开仓我见过太多次了。正确写法:
``
cpp
int ticket = OrderSend(...);
if(ticket < 0) {
// 失败了,处理错误
Print("订单失败,错误码:", GetLastError());
} else {
// 成功了,继续操作
}
`
对于返回-1表示失败的函数,一定要用< 0来判断。返回0表示失败的,才用== 0或者!取反。别图省事把函数调用直接塞进if条件里。
附加坑:OrderSend和OrderModify之间的时间差
这个坑在EA编程里挺常见的,但很多人没意识到严重性。先发一个不带止损止盈的市价单,然后立刻用OrderModify()去加止损止盈。
问题出在:OrderSend()返回成功到OrderModify()执行之间,就算只有几毫秒,市场价格也可能已经变动了。你算好的止损价可能已经变成了无效价格(比如买单止损价低于当前市价),OrderModify()悄悄失败。你的EA以为仓位有保护,实际上它是裸奔的。
解决方案:止损止盈直接在OrderSend()`里传进去。一个原子操作,没有竞态条件。订单成功就自带保护,失败就直接捕获错误。---
参考来源:
本文首发于FXEAR.com,原创内容,未经授权禁止转载