首页 > 博客 > 彻底解决 Off quotes / 拒绝交易(MT5/MQL5)— 10021

MT5MQL5错误故障排除EA

彻底解决 Off quotes / 拒绝交易(MT5/MQL5)— 10021

发布日: 2026-07-07阅读时间:约 2 分钟
本文为发布之日的信息。EA的业绩数值(PF、DD、年化)会随实盘运行与重新验证而变动,最新数值请在各EA页面确认。 查看最新EA业绩

彻底解决 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)MqlTradeResultbid / 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_QUOTES13610021 (TRADE_RETCODE_PRICE_OFF)
ERR_REQUOTE13810004 (TRADE_RETCODE_REQUOTE)

如果在旧版 EA 的说明文章或 MT4 版 EA 的日志中看到"error 136""error 138",本文的内容同样适用(在 MT4 中,惯例做法是用 RefreshRates() 重新获取价格后再重发。MT5 中的写法见下文)。

实务判断:

retcode服务器的意思EA 应采取的行动
10004 (requote)"价格已变动,提供新价格"用最新价格重发(或放弃)
10021 (price off)"没有可处理的报价"稍等后用最新报价重发

两者都是临时性(可重试)的错误,无法通过修改代码或调整设置做到"绝对不出现",但可以大幅降低出现频率。


先用 30 秒做初步判断

  1. 确认出现的时间点(查看日志时间戳)

    • 非农、FOMC 等经济数据公布的瞬间 → 属正常的行情急变,如果 EA 有数据公布过滤功能,启用它
    • 服务器时间 0 点前后(隔夜展期)或周一开盘的跳空 → 报价稀薄的时段,属正常范围
    • 与时段无关、随机频繁出现 → 应怀疑网络、VPS、deviation 设置
  2. 确认出现在哪个品种上

    • 若集中于流动性较低的非主流货币对、外汇小众品种、CFD 等,原因多为该品种本身报价稀薄
  3. 测试手动下单是否也会出现

    • 手动快捷下单正常成交,只有 EA 被拒单 → 很可能是 EA 的 deviation(允许滑点)设置过窄

通过以上三点,先判断是"行情原因""品种原因"还是"设置/环境原因",再进入下面按原因分类的对策部分。


原因与对策(5 种模式)

① 行情急变、经济数据引发的价格跳动(发送的价格在到达前就已过期)

症状: 在经济数据公布时刻、重要人物讲话、周一早盘等时段集中出现 10004/10021。

原因: EA 接收到 tick 后计算出价格,到订单送达服务器之间的数十到数百毫秒内,价格已经变动了几个点。发送的价格已不复存在,服务器因此返回 requote(10004)或无报价(10021)。与其说是错误,不如说是快速行情下理所当然会发生的现象

对策:

  1. 在数据公布前后停止开新仓(新闻过滤器)。本站发布的 EA 标配了 EconomicFilter
  2. deviation(允许滑点)放宽到合理的数值(详见②)
  3. 加入重试逻辑(代码见下文)

② deviation(允许滑点)设置过窄

症状: 即便行情平稳也时不时出现。手动下单可以成交,只有 EA 被拒单。

原因: MqlTradeRequest.deviation 声明的是"相对于发送价格,允许偏差多少points(点)"。若将其设为 0 到几个点,即使是普通 tick 更新程度的偏差也会被拒绝。**将 points 误认为 pips(对于 5 位报价经纪商,1 pip = 10 points)**也是常见的误区。

对策:

  1. 先从 10~30 points(= 1~3 pips)左右开始尝试 deviation。如果不是剥头皮策略,20 points 是较稳妥的起点
  2. 确认是否存在"以为设的是 deviation=5,实际却是 0.5 pips"这类单位混淆
  3. 若策略完全不能容忍滑点,则应接受拒单为正常现象,只控制重试次数

③ 执行方式的差异(requote 是 instant 执行特有的现象)

症状: 在经纪商 A 频繁出现,在经纪商 B 却从未见过。

原因: Requote(10004)是 instant execution(即时执行) 特有的现象。即时执行是"按此价格成交"的订单,一旦价格变动,服务器就会重新报出新价格(requote)。而 market execution(市价执行) 则是"按当前市场价格成交"的订单,理论上不会出现 requote,而是直接按偏离后的价格成交(即滑点)

也就是说,"不出现 requote=优秀"并不成立,这其实是被拒单还是滑点成交之间的权衡。品种的执行方式可在 MT5 的"品种规格(Specification)"中的 Execution 栏查看,或在代码中通过 SYMBOL_TRADE_EXEMODE 确认。

对策:

  1. 确认自己账户的执行方式(许多海外经纪商的标准账户为市价执行,本身就不会出现 requote)
  2. 如果频繁的 requote 妨碍了策略运行,可考虑市价执行的账户类型或经纪商
  3. 即使是市价执行,不同经纪商对 deviation 的遵守程度也不同,若无法接受极端滑点,应通过成交记录核实实际滑点情况

④ 报价稀薄或停滞的时段与品种

症状: 服务器时间 0 点前后(隔夜展期)、周一开盘后不久、圣诞节等清淡时段、非主流品种上出现 10021。

原因: 隔夜展期时因处理库存费,报价推送可能暂时中断或点差极端扩大。周一开盘后不久或流动性低的品种,本身可处理的报价就很稀薄。此时下单就会出现 10021(无报价)。

对策:

  1. 避免在服务器时间 23:55~0:05 前后开新仓(时间过滤器)
  2. 避开周一开盘后的几分钟(AvoidMondayOpen 类设置)
  3. 加入点差过滤器(MaxSpread)。报价稀薄时段点差会扩大,因此可以实质性地自动规避该时段

⑤ 网络延迟(VPS 距离经纪商服务器较远)

症状: 与时段、品种无关,频率明显高于其他环境。ping 值偏大。

原因: 订单送达服务器所需的往返时间(延迟)越长,期间价格变动的概率就越高。若在自家电脑上运行,或使用距经纪商服务器所在地(多为伦敦、纽约等)较远地区的 VPS,10004/10021 的出现频率会在结构上升高。MT5 右下角显示的 ping 值是一个参考指标(数百毫秒明显不利,数十毫秒以下较为理想)。

对策:

  1. 确认 MT5 右下角的 ping 值,若持续偏大,应重新审视 EA 的运行环境
  2. 迁移到靠近经纪商服务器所在地区的 VPS。坦率地说,由延迟引起的 10004/10021 无法通过代码减少,物理上靠近服务器是唯一的对策。选择方法请参考 EA 专用 VPS 选购指南
  3. 剥头皮类 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 时,MqlTradeResultbid / 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
🛠 开发重试是否仅限于价格类 retcodeSymbolInfoTick 重新获取价格,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目前均为首发价,并将随销量阶梯式上调。在每次涨价前收到通知,另有每日一封邮件:自动交易本质、正确解读回测、券商选择技巧。

※ 严格保护隐私。可随时取消订阅。

评论与提问