MT5 EA发送交易命令之后,交易服务器返回的结果会异步触发 OnTradeTransaction(),它传回来的 MqlTradeResult.retcode 就是交易命令的最终结果。本文将把最常见的 8 个交易错误码逐一解析,帮助EA编程新手们少踩坑。
一、OrderSend之后能直接拿到交易结果吗?
很多人是这样判断交易是否失败的:
if(!OrderSend(req, result))
Print("发送失败:", GetLastError()); // 打印出 4756
这里如果拿到 4756 之类的错误码,很多人就以为找到了错误原因。但其实4756除了说明交易命令发送失败以外,并没有额外的什么信息。
因为真正的交易结果是无法在 OrderSend() 执行后同步得到的。
交易服务器的返回码(10004–10046)要在 OnTradeTransaction() 里进行确认。
void OnTradeTransaction(const MqlTradeTransaction &trans,
const MqlTradeRequest &request,
const MqlTradeResult &result)
{
if(trans.type == TRADE_TRANSACTION_REQUEST
&& result.retcode != TRADE_RETCODE_DONE)
Print("服务器拒绝:", result.retcode);
}
总结一下:OrderSend() 返回 true 并不代表交易命令一定执行成功了,具体情况要到异步事件 OnTradeTransaction() 里确认。
二、常见的8个高频错误码
下面这 8 个错误码是在交易命令执行失败时最常见的,现在我将逐一解析并给出解决方案。
10004 · REQUOTE|重新报价
为什么会发生:你发送命令时的价格,到达交易服务器的时候已经无法成交了。
解决方案:对于市价执行的品种,把填充模式从 ORDER_FILLING_RETURN 换成 ORDER_FILLING_IOC 或 ORDER_FILLING_FOK,按当前最优价成交。
req.type_filling = ORDER_FILLING_IOC; // 立刻成交,剩余量撤销(也可以换成 FOK)
可不可以重试:可以的,先刷新报价再重试,但是一定要记得加重试次数上限。
10016 · INVALID_STOPS|无效止损止盈
为什么会发生:SL/TP 超过了该品种的硬性限制。最常见的是离平台允许的最小距离(SYMBOL_TRADE_STOPS_LEVEL)太近了,其次是把止损设成了止盈,或者把止盈设成了止损。
解决方案:下单前先读一下SYMBOL_TRADE_STOPS_LEVEL,把 SL/TP 放到最小距离之外,最好还稍微加一点缓冲距离。
// 平台允许的最小止损距离(点)
double minD = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL) * _Point;
// 止损放到 minD 之外,再留一点缓冲
更好的做法是 OrderSend() 之前先执行 OrderCheck(),如果校验不过就不需要把命令发到交易服务器了。
可不可以重试:这种情况不能重试,必须得先改TP/SL。
10019 · NO_MONEY|资金不足
为什么会发生:账户可用净值不足以执行这一笔开仓。
解决方案:这种情况只能用日志记录起来,告诉用户错误原因,提示其调整仓位或增加资金。
// 可用净值不够这一笔开仓
if(!OrderSend(req, res) && res.retcode == TRADE_RETCODE_NO_MONEY)
{
Print("资金不足,别重试");
lot = NormalizeLot(lot * 0.5); // 调小仓位
}
可不可以重试:这种情况不能重试,重试也没用。
10030 · INVALID_FILL|不支持的填充模式
为什么会发生:发送的 type_filling 在这个品种、这种执行方式下不被服务器接受。最常见的错误是把 ORDER_FILLING_RETURN 用在市价执行的品种上;其次就是硬编码 FOK,如果换了平台就有可能失效了。
解决方案:不把 type_filling 写死。先读 SYMBOL_FILLING_MODE的值,按品种标志位选择填充模式。
uint modes = (uint)SymbolInfoInteger(_Symbol, SYMBOL_FILLING_MODE);
bool okIOC = (modes & SYMBOL_FILLING_IOC) != 0;
bool okFOK = (modes & SYMBOL_FILLING_FOK) != 0;
可不可以重试:不能重试,不改填充模式的情况下重试也没用。
10018 · MARKET_CLOSED|市场关闭
为什么会发生:当前时间不在交易时段内,比如周末和节假日。
解决方案:下单前先判断当前品种是否允许交易,检查 SymbolInfoInteger(_Symbol, SYMBOL_TRADE_MODE) 是否为 SYMBOL_TRADE_MODE_FULL。这种情况只能等待允许交易的时段。
if(SymbolInfoInteger(_Symbol, SYMBOL_TRADE_MODE) != SYMBOL_TRADE_MODE_FULL)
return; // 不在可交易状态
可不可以重试:不能重试,重试也没用。
10015 · INVALID_PRICE|无效价格
为什么会发生:无效价格有三种常见情况,用了过期的报价(Ask/Bid),小数位和品种精度对不上,还有就是挂单价格放错了方向,比如 Buy Limit 放到了当前市价上方。
解决方案:先获取最新报价,然后按品种报价精度收敛,最后再发送交易命令。
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double px = NormalizeDouble(ask, _Digits);
可不可以重试:可以重试,但是要刷新报价,并且也要设置重试次数上限。
10026 · SERVER_DISABLES_AT|服务器禁止自动交易
这种情况是交易服务器直接告诉EA,当前服务器禁止自动交易。
以下是最常见的触发原因:
- 用只读密码(投资人密码)登录了账户。只读密码登录的本来就不允许交易。
- 账户类型本身禁止 EA 交易。有些账户不允许算法交易,手动下单可能没问题,但是用 EA 就无法交易。
- 平台在服务器端主动关闭了自动交易权限,这可能是因为风控、合规等各种原因。
解决方案:直接读取账户的交易权限,判断是否允许 EA 交易。
bool canTrade = AccountInfoInteger(ACCOUNT_TRADE_ALLOWED); // 账户能不能交易
bool canExpert = AccountInfoInteger(ACCOUNT_TRADE_EXPERT); // 能不能跑 EA
if(!canTrade || !canExpert) return; // 无法执行交易命令
如果这两项都是true但还是无法执行交易,那就直接联系平台吧。
可不可以重试:不能重试,重试也没用。
10027 · CLIENT_DISABLES_AT|客户端禁止自动交易
为什么会发生:MT5 工具栏上的自动交易按钮被关掉了,或者 EA 属性里没有勾选「允许算法交易」。这是最好解决的一个错误码。
解决方案:打开工具栏的算法交易按钮,检查 EA 属性里的自动交易是否勾上。也可以在 EA 里提前判断:
bool termOk = TerminalInfoInteger(TERMINAL_TRADE_ALLOWED); // 工具栏的算法交易按钮
bool eaOk = MQLInfoInteger(MQL_TRADE_ALLOWED); // EA 属性里的「允许算法交易」
可不可以重试:不能重试,重试也没用。
三、总结
只记录交易报错还不够,还要做好这三件事:
- 事前拦截:发单前先
OrderCheck(),先在本地把一道关。 - 定位原因:判断失败原因要看 OnTradeTransaction 异步回调里的
result.retcode(10004–10046),GetLastError()只表示MT5层面的错误。 - 分类处理:要根据不同的错误码决定是重试、等待、还是只记录下来。
写 EA 难的从来不是去记那些错误码,而是在拿到错误码之后能知道根本原因和对应的解决方案,怎么样,天才交易员们,今天又学到了吗? ^_^
@我是迈达斯之手 · 每天五分钟,带你轻松看懂交易背后的门道