彻底解决 Off quotes / 拒绝交易(MT5/MQL5)— 10021
目录
- 这两者(+MT4 的 136/138)有什么区别
- ① TRADE_RETCODE_PRICE_OFF = 10021(没有可处理的报价)
- ② TRADE_RETCODE_REQUOTE = 10004(拒绝交易/requote=重新报出新价格)
- ③ MT4 时代的错误代码 136 / 138
- 先用 30 秒做初步判断
- 原因与对策(5 种模式)
- ① 行情急变、经济数据引发的价格跳动(发送的价格在到达前就已过期)
- ② deviation(允许滑点)设置过窄
- ③ 执行方式的差异(requote 是 instant 执行特有的现象)
- ④ 报价稀薄或停滞的时段与品种
- ⑤ 网络延迟(VPS 距离经纪商服务器较远)
- 用 MQL5 减少此类错误的代码(面向 EA 开发者)
- 以 points 为单位合理设置 deviation
- 仅对价格类 retcode 进行重试(用最新 tick 重发)
- 利用 requote(10004)重新报出的价格
- 从根本上设置不开新仓的时段
- 优先级检查清单
- 总结
- 常见问题
- Q: 10004 和 10021 哪个更严重?
- Q: 从未出现过 requote 的经纪商是否更优秀?
- Q: MT4 的 EA 出现 error 136 / 138,可以用同样的方法处理吗?
- Q: 回测中没有出现,但实盘中却出现了。
彻底解决 Off quotes / 拒绝交易(MT5/MQL5)
运行 EA 时,如果日志或 Expert 选项卡中出现 off quotes(10021)或 requote(10004),看起来像是"被经纪商拒绝成交",让人感到不安,但这两者并非资金或手数的问题,而是"价格"的问题。你发送的价格与服务器当前持有的价格不一致——仅此而已,绝大多数原因都可以归结为行情急变、允许滑点、执行方式、网络延迟这四类之一。
本文面向使用 MT5 运行 EA 的用户,以及用 MQL5 编写 EA 的开发者,将10021(TRADE_RETCODE_PRICE_OFF)与 10004(TRADE_RETCODE_REQUOTE),以及 MT4 时代的错误代码 136/138的本质、成因、可立即执行的应对方法,直到代码层面的长期对策,整理成一篇终极指南。关于错误代码的完整列表,请参考 MQL5 / MT5 错误代码处理综合指南。
本文以 2026 年 7 月时点的 MT5(build 4xxx 系列)为前提。具体行为细节(是返回 requote 还是直接成交等)会因经纪商的执行方式而异。
这两者(+MT4 的 136/138)有什么区别
"价格不一致"类的拒单,根据服务器的应答方式可分为两种。
① TRADE_RETCODE_PRICE_OFF = 10021(没有可处理的报价)
这是 OrderSend() 返回结果 MqlTradeResult.retcode 中的值,表示**"没有可用于处理该请求的报价(There are no quotes to process the request)"**这一拒单。指服务器端没有有效价格,或发送的价格与当前报价偏离过大而无法处理。
含义: 没有可处理请求的报价
常量: TRADE_RETCODE_PRICE_OFF
值 : 10021
② TRADE_RETCODE_REQUOTE = 10004(拒绝交易/requote=重新报出新价格)
同样是 OrderSend() 的 retcode,但这不是单纯的拒单,而是**"该价格不行,但这个新价格如何"的重新报价(requote)。MqlTradeResult 的 bid / ask 中会返回服务器重新提供的价格**。
含义: Requote(拒绝交易)— 重新报出新价格
常量: TRADE_RETCODE_REQUOTE
值 : 10004
// 日志中可见的典型输出示例
2026.07.07 21:30:02.118 EA_NAME EURUSD,M5: OrderSend error 10004 (requote)
2026.07.07 21:30:02.310 EA_NAME EURUSD,M5: OrderSend error 10021
③ MT4 时代的错误代码 136 / 138
在 MQL4(MT4)中,同样的现象是通过 GetLastError() 的错误代码返回的。
| MT4 常量 | 值 | 对应的 MT5 retcode |
|---|---|---|
ERR_OFF_QUOTES | 136 | 10021 (TRADE_RETCODE_PRICE_OFF) |
ERR_REQUOTE | 138 | 10004 (TRADE_RETCODE_REQUOTE) |
如果在旧版 EA 的说明文章或 MT4 版 EA 的日志中看到"error 136""error 138",本文的内容同样适用(在 MT4 中,惯例做法是用 RefreshRates() 重新获取价格后再重发。MT5 中的写法见下文)。
实务判断:
| retcode | 服务器的意思 | EA 应采取的行动 |
|---|---|---|
| 10004 (requote) | "价格已变动,提供新价格" | 用最新价格重发(或放弃) |
| 10021 (price off) | "没有可处理的报价" | 稍等后用最新报价重发 |
两者都是临时性(可重试)的错误,无法通过修改代码或调整设置做到"绝对不出现",但可以大幅降低出现频率。
先用 30 秒做初步判断
-
确认出现的时间点(查看日志时间戳)
- 非农、FOMC 等经济数据公布的瞬间 → 属正常的行情急变,如果 EA 有数据公布过滤功能,启用它
- 服务器时间 0 点前后(隔夜展期)或周一开盘的跳空 → 报价稀薄的时段,属正常范围
- 与时段无关、随机频繁出现 → 应怀疑网络、VPS、deviation 设置
-
确认出现在哪个品种上
- 若集中于流动性较低的非主流货币对、外汇小众品种、CFD 等,原因多为该品种本身报价稀薄
-
测试手动下单是否也会出现
- 手动快捷下单正常成交,只有 EA 被拒单 → 很可能是 EA 的
deviation(允许滑点)设置过窄
- 手动快捷下单正常成交,只有 EA 被拒单 → 很可能是 EA 的
通过以上三点,先判断是"行情原因""品种原因"还是"设置/环境原因",再进入下面按原因分类的对策部分。
原因与对策(5 种模式)
① 行情急变、经济数据引发的价格跳动(发送的价格在到达前就已过期)
症状: 在经济数据公布时刻、重要人物讲话、周一早盘等时段集中出现 10004/10021。
原因: EA 接收到 tick 后计算出价格,到订单送达服务器之间的数十到数百毫秒内,价格已经变动了几个点。发送的价格已不复存在,服务器因此返回 requote(10004)或无报价(10021)。与其说是错误,不如说是快速行情下理所当然会发生的现象。
对策:
- 在数据公布前后停止开新仓(新闻过滤器)。本站发布的 EA 标配了
EconomicFilter - 将
deviation(允许滑点)放宽到合理的数值(详见②) - 加入重试逻辑(代码见下文)
② deviation(允许滑点)设置过窄
症状: 即便行情平稳也时不时出现。手动下单可以成交,只有 EA 被拒单。
原因: MqlTradeRequest.deviation 声明的是"相对于发送价格,允许偏差多少points(点)"。若将其设为 0 到几个点,即使是普通 tick 更新程度的偏差也会被拒绝。**将 points 误认为 pips(对于 5 位报价经纪商,1 pip = 10 points)**也是常见的误区。
对策:
- 先从 10~30 points(= 1~3 pips)左右开始尝试
deviation。如果不是剥头皮策略,20 points 是较稳妥的起点 - 确认是否存在"以为设的是 deviation=5,实际却是 0.5 pips"这类单位混淆
- 若策略完全不能容忍滑点,则应接受拒单为正常现象,只控制重试次数
③ 执行方式的差异(requote 是 instant 执行特有的现象)
症状: 在经纪商 A 频繁出现,在经纪商 B 却从未见过。
原因: Requote(10004)是 instant execution(即时执行) 特有的现象。即时执行是"按此价格成交"的订单,一旦价格变动,服务器就会重新报出新价格(requote)。而 market execution(市价执行) 则是"按当前市场价格成交"的订单,理论上不会出现 requote,而是直接按偏离后的价格成交(即滑点)。
也就是说,"不出现 requote=优秀"并不成立,这其实是被拒单还是滑点成交之间的权衡。品种的执行方式可在 MT5 的"品种规格(Specification)"中的 Execution 栏查看,或在代码中通过 SYMBOL_TRADE_EXEMODE 确认。
对策:
- 确认自己账户的执行方式(许多海外经纪商的标准账户为市价执行,本身就不会出现 requote)
- 如果频繁的 requote 妨碍了策略运行,可考虑市价执行的账户类型或经纪商
- 即使是市价执行,不同经纪商对
deviation的遵守程度也不同,若无法接受极端滑点,应通过成交记录核实实际滑点情况
④ 报价稀薄或停滞的时段与品种
症状: 服务器时间 0 点前后(隔夜展期)、周一开盘后不久、圣诞节等清淡时段、非主流品种上出现 10021。
原因: 隔夜展期时因处理库存费,报价推送可能暂时中断或点差极端扩大。周一开盘后不久或流动性低的品种,本身可处理的报价就很稀薄。此时下单就会出现 10021(无报价)。
对策:
- 避免在服务器时间 23:55~0:05 前后开新仓(时间过滤器)
- 避开周一开盘后的几分钟(
AvoidMondayOpen类设置) - 加入点差过滤器(
MaxSpread)。报价稀薄时段点差会扩大,因此可以实质性地自动规避该时段
⑤ 网络延迟(VPS 距离经纪商服务器较远)
症状: 与时段、品种无关,频率明显高于其他环境。ping 值偏大。
原因: 订单送达服务器所需的往返时间(延迟)越长,期间价格变动的概率就越高。若在自家电脑上运行,或使用距经纪商服务器所在地(多为伦敦、纽约等)较远地区的 VPS,10004/10021 的出现频率会在结构上升高。MT5 右下角显示的 ping 值是一个参考指标(数百毫秒明显不利,数十毫秒以下较为理想)。
对策:
- 确认 MT5 右下角的 ping 值,若持续偏大,应重新审视 EA 的运行环境
- 迁移到靠近经纪商服务器所在地区的 VPS。坦率地说,由延迟引起的 10004/10021 无法通过代码减少,物理上靠近服务器是唯一的对策。选择方法请参考 EA 专用 VPS 选购指南
- 剥头皮类 EA 受延迟影响更大。日线、H4 级别的 EA,此原因的优先级较低
用 MQL5 减少此类错误的代码(面向 EA 开发者)
思路分三点:(1) 将 deviation 设为合理数值,(2) 被拒单后重新获取最新 tick 再重发,(3) 仅对价格类 retcode 进行重试。
以 points 为单位合理设置 deviation
MqlTradeRequest req; MqlTradeResult res;
ZeroMemory(req); ZeroMemory(res);
req.action = TRADE_ACTION_DEAL;
req.symbol = _Symbol;
req.type = ORDER_TYPE_BUY;
req.volume = lots;
req.deviation = 20; // 允许滑点 20 points(5位报价即2.0 pips)
// req.price / req.sl / req.tp / req.magic 等也需设置
deviation 的单位是 points。对于 5 位(3 位)报价的经纪商,10 points = 1 pip。设为 0 或极小值会不必要地提高拒单率。
仅对价格类 retcode 进行重试(用最新 tick 重发)
关键在于每次重试都用 SymbolInfoTick() 重新获取价格(用旧价格重发只会得到同样的拒单结果),以及将重试对象限定为 10004/10021。对资金不足(10019)或无效请求(10013)机械地重发没有意义,只会让日志变得混乱。
bool IsRetryableRetcode(uint rc)
{
return (rc == TRADE_RETCODE_REQUOTE // 10004
|| rc == TRADE_RETCODE_PRICE_OFF); // 10021
}
// 用最新 tick 更新价格,最多重发3次
bool SendWithRetry(MqlTradeRequest &req, MqlTradeResult &res, int maxTries = 3)
{
for(int attempt = 0; attempt < maxTries; attempt++)
{
MqlTick tick;
if(!SymbolInfoTick(req.symbol, tick))
{
Print("SymbolInfoTick failed: ", GetLastError());
return false;
}
req.price = (req.type == ORDER_TYPE_BUY) ? tick.ask : tick.bid;
if(OrderSend(req, res) && res.retcode == TRADE_RETCODE_DONE)
return true; // 成交成功
if(!IsRetryableRetcode(res.retcode))
{
PrintFormat("OrderSend failed (no retry): retcode=%d", res.retcode);
return false; // 非价格类错误不重试
}
PrintFormat("Retry %d/%d after retcode=%d", attempt + 1, maxTries, res.retcode);
Sleep(200 + 150 * attempt); // 200ms→350ms→500ms 逐步延长等待
}
Print("Order abandoned after retries (price kept moving).");
return false;
}
之所以让 Sleep() 的等待时间逐步延长,是因为在急变的瞬间以 0ms 间隔连续重试只会不断被拒单。反之等待过久,成交价格又会偏离策略预期,因此重试以 2~3 次为限、及时放弃才是健康的做法。需要注意的是,对于挂单(TRADE_ACTION_PENDING),成功时的 retcode 是 TRADE_RETCODE_PLACED(10008)。
利用 requote(10004)重新报出的价格
出现 10004 时,MqlTradeResult 的 bid / ask 中会包含服务器重新报出的价格。按上述方式用最新 tick 重发在实际应用中已经足够,但若想在 instant 执行下实现"重新报出的价格若在允许范围内就直接接受"的逻辑,则需要将 res.ask / res.bid 与原预期价格之差按 points 比较后再决定是否重发。
从根本上设置不开新仓的时段
代码层面的重试只是对症处理。在隔夜展期前后、经济数据公布前后、点差扩大时停止开新仓的过滤器,才是更根本有效的做法。
// 点差过滤器示例:点差扩大的时段暂停开新仓
long spreadPts = SymbolInfoInteger(_Symbol, SYMBOL_SPREAD);
if(spreadPts > MaxSpreadPoints)
{
// 暂停开新仓(可自动规避容易出现10021的稀薄时段)
return;
}
FXEA365 发布的 EA 标准配备了这套点差过滤器、经济数据过滤器、价格类 retcode 自动重试机制。
优先级检查清单
| 优先级 | 检查项 | 对策 |
|---|---|---|
| 🚨 首先 | 是否集中在经济数据公布、行情急变的瞬间 | 启用新闻过滤器,该时段视为正常现象 |
| 🚨 首先 | deviation 是否过小(单位为 points) | 调整到 10~30 points,确认是否混淆了 pips/points |
| ⚠️ 其次 | 是否偏向隔夜展期、周一开盘、清淡品种 | 时间过滤器+点差过滤器 |
| ⚠️ 其次 | 执行方式是否为 instant | 考虑市价执行账户(需权衡滑点) |
| ⚠️ 其次 | ping 是否偏大(数百毫秒) | 迁移到靠近服务器的 VPS |
| 🛠 开发 | 重试是否仅限于价格类 retcode | 用 SymbolInfoTick 重新获取价格,2~3次后终止 |
总结
- 10021(TRADE_RETCODE_PRICE_OFF)意为"没有可处理的报价",10004(TRADE_RETCODE_REQUOTE)意为"重新报出新价格"。与 MT4 时代的 136/138 属于同一类价格类拒单,并非资金或手数的问题。
- 原因可归纳为 行情急变、deviation 过小、instant 执行、报价稀薄的时段/品种、网络延迟 这 5 类。
- Requote 是 instant 执行特有的现象,在 market 执行下则会表现为滑点。"不出现=好"并不成立,本质是权衡取舍。
- EA 开发者应通过 用最新 tick 重新获取价格并进行 2~3 次重试(仅限 10004/10021)+ 合理设置 deviation + 时间/点差过滤器 来实现长期对策。由延迟引起的部分,只能靠物理手段(靠近经纪商的 VPS)来减少。
关于错误代码的完整内容,请参考 MQL5 / MT5 错误代码处理综合指南;标准配备这些对策的免费 EA,请参考 EA 列表。
常见问题
Q: 10004 和 10021 哪个更严重?
两者都是临时性的价格类错误,严重程度并无太大差异。10004 是"重新报出了新价格",10021 是"没有可处理的报价",二者只是应答方式不同。偶尔出现无需在意,只有在特定时段或品种上频繁出现时,才需要针对原因(经济数据、隔夜展期、deviation、网络)加以解决。
Q: 从未出现过 requote 的经纪商是否更优秀?
很可能只是执行方式不同。市价执行账户理论上不会出现 requote,价格变动时会直接按偏离后的价格成交(即滑点)。这是拒单与滑点之间的权衡,请通过成交记录确认实际滑点情况后再做判断。
Q: MT4 的 EA 出现 error 136 / 138,可以用同样的方法处理吗?
可以。136(ERR_OFF_QUOTES)相当于 10021,138(ERR_REQUOTE)相当于 10004,原因和对策都是相通的。在 MQL4 中,惯例是在重发前调用 RefreshRates() 更新 Bid / Ask,这与 MQL5 中用 SymbolInfoTick() 重新获取价格的思路一致。
Q: 回测中没有出现,但实盘中却出现了。
这是正常现象。Strategy Tester 中不存在(或被简化处理了)"订单到达服务器所需的延迟"以及"这段时间内的价格变动",因此 10004/10021 只会在实盘(forward)中显现。即使回测结果良好,实盘中仍需另外完善 deviation 设置、重试机制以及运行环境(VPS)。
相关文章
📧 涨价预告 + 免费5天邮件课程
所有EA目前均为首发价,并将随销量阶梯式上调。在每次涨价前收到通知,另有每日一封邮件:自动交易本质、正确解读回测、券商选择技巧。
※ 严格保护隐私。可随时取消订阅。