摘要
凌晨,运维团队收到一条告警:某看板页面显示的 700.HK 价格已超过15分钟未更新。排查发现,WebSocket 连接在网关维护窗口中被短暂断开并成功重连,但重连期间错过的推送数据未被标记为缺失。看板没有报错,也没有显示空白,只是悄然将旧价格留在了屏幕上。告警的触发,是因为值班同事多看了一眼那个价格旁边的更新时间颜色。
这个场景虽是假设,但它描述的风险真实存在。2026年6月6日,在一次实际的 MCP 工具调用中,输入 600519.SH、700.HK、AAPL.US、BTCUSDT、EURUSD 五类行情代码,get_ticker 返回 code=0,且五个品种均出现在 data 中。对于一个正在评估多市场行情接入方案的团队而言,这一刻很容易产生一种感觉——“通了。”
但验证通过与验收合格之间,还隔着三层校验的距离。

正文
一个看似成功的测试——用 MCP 工具单次查询 A 股、港股、美股、加密和外汇五类行情,返回 code=0 且五个品种均出现在 data 中——很容易让人产生“统一查询已经跑通”的错觉。但验证一批 symbol 能返回数据只是起点。本文提出云上多市场行情接入的四层架构,并指出统一查询之后还需要补上三层校验:查询层的缺失 symbol 校验、适配层的字段与时间戳规范化、消费层在数据不完整时的阻断逻辑。同时明确本轮验证的边界:已确认 ticker 快照的批量查询能力,不代表其他端点、WebSocket 推送或全品种覆盖同样成立。
本轮实测能确认什么,不能外推什么
| 状态 | 内容 | 来源 |
|---|---|---|
| 已确认 | 单次 get_ticker 可同时返回五类行情快照 | 本轮实测 |
| 已确认 | code=0(本轮工具返回业务成功码,不证明结果完整),data 含五个目标 symbol | 本轮实测 |
| 已确认 | 五项数据均含 symbol、type、last_price、timestamp | 本轮实测 |
| 已确认 | last_price 为字符串,timestamp 为本轮 13 位整数(按官方口径解释为毫秒 UTC;13 位说明字段精度,不证明毫秒级新鲜度) | 本轮实测 |
| 已确认 | 混合查询无效 symbol 时只返回有效品种,仅查无效 symbol 时返回空数组;两种情况下 code 均为 0 | 本轮实测 |
| 未验证 | 全市场品种覆盖(仅五个代表品种通过) | 待核验 |
| 未验证 | get_kline、get_order_book、get_recent_trades 等其他 MCP 工具 | 待核验 |
| 未验证 | WebSocket 实时推送的稳定性与数据一致性 | 待核验 |
| 未验证 | 数据时效性、准确性、复权正确性、历史数据深度 | 待核验 |
| 未验证 | 高并发场景的性能与限流表现 | 待核验 |
三层校验总览
把多市场行情接入想象成机场的行李分拣系统:你交出了五件行李,拿到了五张行李票,但这不等于五件行李都上了飞机。你需要逐件核对传送带上的行李是否与行李票一致——这是查询层。行李到达中转站后,不同航空公司使用的标签格式不同,有的用公斤,有的用磅。你必须把它们统一成同一个度量衡——这是适配层。最后装车时,如果发现少了一件行李,调度系统必须停下来问清楚“这件行李是晚到了还是根本没托运”,而不是把四件行李当成五件发走——这是消费层。
┌─────────────────────────────────────────────────────────┐
│ 消费层 │
│ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │
│ │ 金融看板 │ │ 告警引擎 │ │ AI Agent │ │
│ │ 旧值+stale│ │ 缺失→抑制 │ │ 缺失→暂停推理│ │
│ └─────┬────┘ └─────┬────┘ └──────┬───────┘ │
│ │ 数据不完整时阻断,不喂入计算 │ │
├────────┴─────────────┴───────────────┴──────────────────┤
│ 适配层 │
│ symbol 显式映射 │ 字段统一命名 │ timestamp 精度标记 │
├─────────────────────────────────────────────────────────┤
│ 查询层 │
│ 必需字段校验 │ 逐 symbol 比对 │ last_price Decimal 解析 │
├─────────────────────────────────────────────────────────┤
│ 数据源层 │
│ A股行情 │ 港股行情 │ 美股行情 │ 加密行情 │ 外汇行情 │
└─────────────────────────────────────────────────────────┘
失败传播链(纵向):
查询层未逐symbol比对 → 适配层正常映射 → 消费层基于不完整数据输出错误结论
三层校验,解决同一个问题:让每一层把“我不知道”显式地传递给下一层。
| 层级 | 校验重点 | 具体规则 | 可观测指标 |
|---|---|---|---|
| 查询层 | 逐 symbol 比对请求与返回 | 必需字段检查:symbol、type、last_price、timestamp 是否存在;last_price 必须为非空字符串并能被有限 Decimal 解析;检查重复、意外和缺失 symbol;缺失不等于故障(停牌/未覆盖/瞬态延迟),但必须记录 | symbol 一致性、code、returned_symbols_count、缺失 symbol 列表及发生时间 |
| 适配层 | symbol 规范化、字段映射、timestamp 精度标记 | 按协议和资产类别维护显式 symbol 映射表(不发布未经完整核验的全市场规则);建立统一内部字段命名规范并集中映射;标记精度级别(ticker 为 13 位毫秒,美股 trades 为 10 位秒级) | field_mapping_status、timestamp_precision、missing_fields |
| 消费层 | 数据不完整时阻断,不喂入计算 | 看板可显示旧值但必须标注 stale 状态和最后更新时间;告警引擎在数据不完整时抑制本次判断;Agent 在关键品种缺失时暂停推理 | data_completeness_ratio、stale_status、blocked_reason、last_data_arrival_time |
看板“缺失就显示空白”并不是固定的规则。可以显示旧值,但必须明确标注 stale 状态和最后更新时间,由业务规则来决定。
校验规则的执行困难通常不在技术上,而在组织上。当一个看板同时服务交易、风控和合规三个部门时,“缺失数据显示空白”意味着有人看到一个不完整的屏幕,“缺失数据显示旧值并标注 stale”意味着有人看到一个看似完整但可能误导的屏幕。这两种选择都有代价,而架构文档通常只写校验规则,不写谁来承担哪种代价。三层校验落地的第一步,往往不是写代码,而是明确每个字段缺失时的默认行为由哪个角色决策。
失败传播链
一个字段的缺失如何穿过三层,最终污染决策:
| 层级 | 发生的事 | 遗漏的检查 |
|---|---|---|
| 数据源层 | 某港股 ticker 数据停止更新 | — |
| 查询层 | 批量查询返回四个品种,该 symbol 静默缺失 | 未逐 symbol 比对请求与返回 |
| 适配层 | 拿到四个品种的数据,正常映射字段和 timestamp | 缺失未被标记 |
| 消费层 | 用四个品种计算跨市场相关性,缺失品种被忽略 | 未校验数据完整度 |
| 最终输出 | 计算结果偏差——缺失品种正是当天波动最大的那个 | 输出未经完整性校验即被消费 |
链上的每一层都在做分内的事——没有一层报错,最终输出的结论却是错的。这才是最让人头疼的地方。
MCP、REST、WebSocket:三通道独立验证边界
MCP 工具调用、REST 查询和 WebSocket 推送是三种独立的验证维度。一轮 MCP 的 get_ticker 验证通过,不能作为 REST 或 WebSocket 通道的验收依据。
| 通道 | 本轮状态 | 待验证项 | 注意 |
|---|---|---|---|
MCP get_ticker | 五类行情快照通过 | — | — |
MCP get_kline | 未验证 | 各市场日线/分钟线的字段一致性与数据完整性 | — |
MCP get_order_book | 未验证 | 不同市场返回的深度档位是否一致 | — |
MCP get_recent_trades | 未验证 | 各市场的时间戳精度(美股为秒级,加密为毫秒级——此结论来自官方/历史实测事实,非本轮 H03 实测) | 不得用本轮 ticker 结果推断 |
| REST 端点 | 未在本轮运行 | /v1/market/ticker 等端点的跨市场表现 | REST 和 MCP 命名空间不同,证据不得互相继承 |
| WebSocket 推送 | 未验证 | 连接稳定性、重连机制、数据连续性 | WS 校验不能用 MCP 集合差方法:应使用规范化 symbol、last_seen、交易时段和超时策略 |
三个常见问题
统一 API Key 是否等于鉴权相同?
不同通道的鉴权方式可能不同——REST 使用 Header X-API-Key,WebSocket 使用 URL 参数 ?api_key=,MCP 使用独立的服务端点鉴权。即使凭证相同,各通道的鉴权失败处理逻辑也相互独立,不能假设一个通道鉴权通过则其他通道同样通过。这是很多团队在实际中容易踩的坑。
13 位时间戳是否等于低延迟?
13 位毫秒 UTC 时间戳说明的是字段精度,不是数据新鲜度。数据从交易所产生到通过 API 返回,中间经过的链路延迟不由时间戳位数决定。因此,不要把字段格式和端到端延迟混为一谈。
MCP 验证通过是否等于 WebSocket 通过?
MCP 工具调用和 WebSocket 推送是两种正交的数据通道。MCP 是按需查询,WebSocket 是持续推送,两者的连接模型、失败模式和恢复策略不同。一轮 MCP 验证通过,绝不能作为 WebSocket 通道的验收依据。
回答那个最初的问题
统一查询跑通之后,还要补哪三层校验?
第一层,查询层:逐 symbol 比对请求与返回,缺失的显式记录。code=0 不代表所有品种都在。必需字段存在、last_price 可解析、无意外 symbol。
第二层,适配层:按协议和资产类别维护显式 symbol 映射,统一字段命名,标记时间戳精度。不要让消费层去猜测上游的格式,那会非常被动。
第三层,消费层:数据不完整时阻断决策——看板标注 stale 状态,告警抑制判断,Agent 暂停推理。阻断条件因场景而异,但原则是明确的:不好就停,不喂入计算。
三层校验的共同目标,不是让每一层更重,而是让每一层都把“我不知道”诚实地传递给下一层。这种显式的信息传递,就是统一接口的核心工程价值:减少接入协调和迁移成本,但不消除不同市场在制度、数据语义及授权上的固有差异。说穿了,就是让系统知道它不知道什么,而不是假装知晓一切。
