摘要:系统看到买一突然挂出一笔大单,触发观察逻辑,事后复盘发现那只是挂单状态的一次正常变化------不是成交信号,不是主力入场,只是盘口结构在刷新。这次误判的根因不是数据质量,而是设计上把"展示层"的视觉信息直接当成了"信号候选层"的决策输入。本文复盘这次误读,拆解盘口数据从展示、状态观察到信号候选的三层边界,给出字段验证表、日志设计、raw_snapshot 保存方案和 fail closed 状态机的具体实现。关键词:盘口数据、bids/asks、深度行情、信号验证、工程复盘。
一、一次复盘:那笔大单去哪了
晚上复盘时,同事指着监控日志里的一条记录问我:昨天下午两点零三分,系统标记了 600519 买一出现大单,为什么后来什么都没发生?
日志是这样的:
[14:03:05] bid_qty_change: +1200 lots @ 1850.00
[14:03:05] signal_candidate: large_bid_detected
[14:03:07] bid_qty_change: -1100 lots @ 1850.00
[14:03:07] signal_candidate: cleared
两秒钟,大单来了又走了。系统在这两秒内触发了一次"观察",然后自行清除。没有成交,没有方向,什么都没有。
排查过程不复杂:我们把那条记录的 raw_snapshot 调出来,对比了前后三秒的完整盘口快照。结论是------那笔大单是挂单结构的正常变化。卖一在那一瞬间被成交掉了,新的卖一上移了一个价位,原来压在买一附近的挂单被重新归类,看起来像是"买一突然多了一笔大单"。实际上供需关系没变,只是报价结构变了。
问题不在数据。数据如实地反映了那一刻的盘口状态。问题在于设计:系统把"盘口上出现了一笔大单"等同于"出现了一个值得关注的信号",中间没有经过状态观察和可复盘验证。
这次复盘之后,我们把盘口数据的处理拆成了三层:展示层只显示,状态观察层做诊断,信号候选层必须可回测、可复盘。每层之间有显式的升级条件,不满足条件的数据不能进入下一层。
二、展示、状态观察、信号候选,是三层不同的东西
从"看见盘口"到"研究信号",中间必须经过字段、时间和样本验证。
第一层:盘口展示
这是最基础的层次。系统能收到 bids/asks 数据,能把价格和数量渲染在屏幕上。屏幕上的数字在跳动,颜色在变化,信息在流动。
展示层只做一件事:把数据源返回的盘口快照原样呈现。 不做判断,不做比较,不做标记。它回答的问题是"现在屏幕上有什么",不回答"这意味着什么"。
但很多系统的第一版实现,直接把展示层的数据接到了判断逻辑里。看到买一量大,就觉得支撑强;看到卖一量薄,就觉得要突破。这是把静态截图当成决策输入,和看着一张照片猜测下一秒会发生什么一样不可靠。
第二层:状态观察
当你不再盯着单笔大单找方向,而是系统性地用盘口数据观察市场状态时,你进入了第二层。
这一层可以观察四样东西:
- 价差变化:bid/ask spread 在扩大还是缩小
- 流动性厚薄:买一侧和卖一侧的深度是否均衡,有没有突变
- 报价完整性:某一侧突然变薄或消失------这是异常信号,不是交易机会
- 盘口异常:价格跳空、挂单突变、刷新中断
状态观察层只做诊断,不做预测。它能告诉你市场现在是活跃还是冷清、稳定还是波动、正常还是异常。它给你的不是"应该交易",而是"现在适不适合观察"。
设计取舍 :状态观察层的输出是结构化的状态标记,不是信号。如果某一侧深度突然变薄,观察层的输出是 market_status: abnormal_thinning,附带时间戳和当前快照。它不会输出 action: sell。从"状态异常"到"交易决策"之间,隔着信号候选层的一整套验证流程。
第三层:信号候选
当你把观察内容量化为可回测的规则,并且通过了字段、时间、机制和样本的验证之后,盘口数据才进入"信号候选"的范畴。注意,是"候选",不是"有效信号"。
这一层有三道门槛:
字段关:bids/asks 的价格和数量语义是否明确?层级定义是否清晰------你看到的是一档还是五档还是十档?历史样本是否完整可核对?如果数据源只返回一档盘口,但你的信号逻辑假设能看到五档以上的深度变化,你的信号从一开始就少了一半输入维度。
时间关:时间戳是行情发生时间还是接收时间?刷新是全量快照还是增量更新?两次更新之间的间隔是否稳定?买一卖一在几十毫秒内可能轮换多次,如果你的时间戳是接收时间,不同延迟的快照排在一起,你看到的时序可能和真实发生的时序完全相反。
机制关:你的判断逻辑在不同市场机制下是否一致?A 股涨停板上的买一挂几十万手,是封板机制,不是"买盘强劲"。美股同一只股票在不同交易所的盘口可能不一样。港股有竞价时段和持续交易时段的区别。同一个盘口现象在这些不同制度下含义完全不同。
绝大多数人困在第二层。不是因为不够聪明,是因为从"观察"到"候选"之间,隔的不是一个指标,而是一整套验证流程。
三、真正要检查的 5 个问题
在把盘口数据从"观察"升级为"信号候选"之前,有五件事必须查清楚。
① 你看到的是一档盘口,还是多档深度?
一档盘口只有买一卖一,多档深度能看到更多层挂单。如果你的信号逻辑假设能看到五档以上的深度变化,但数据源只返回一档,你的信号从一开始就少了一半输入维度。
② bids/asks 里的 price、size/qty,到底代表挂单还是成交?
盘口数据返回的是挂单价和挂单量,不是成交价和成交量。这两个概念在数据字段里可能只差一个字段名,但在判断逻辑里差一个世界。买一量突然增加,真实原因可能是原来的卖一被成交掉了,新的卖一价格更高,原来的买一价位的挂单被重新归类------供需关系没变,只是报价结构变了。
③ timestamp 是行情发生时间、服务端处理时间,还是你本地收到时间?
不同数据源的时间戳语义不同。如果你的时间戳是接收时间,不同延迟的快照排在一起,你看到的时序可能和真实发生的时序完全相反。
④ 刷新是完整快照、增量更新,还是展示层截取?
完整快照每次都发全量数据,但两次快照之间发生的快速变化你不知道。增量更新只发变化的部分,但如果推送有节流或丢包,中间的关键变动可能被漏掉。你看到的刷新频率是前端 UI 的更新速率,还是交易所原始数据的真实频率?
⑤ 你的判断能不能跨市场复用?
A 股、美股、港股的交易机制不同,同一个盘口现象在不同市场含义完全不同。跨市场直接套用盘口判断框架,等于在不懂规则的情况下做出推断。
四、字段验证表与日志字段设计
4.1 盘口字段验证表
| 字段 | 核对点 | 不通过时处理 |
|---|---|---|
symbol |
与请求一致,含正确后缀 | 阻断,need_review |
bids |
为数组,每项含 price 和 size/qty | 阻断,need_review |
asks |
同上 | 阻断,need_review |
timestamp |
整数且非 bool,语义明确 | 阻断,标记 time_unknown |
| 层级数量 | 确认是几档深度 | 记录,不阻断但标记 level_mismatch |
| price 字段 | 可解析为有限 Decimal | 阻断,fail closed |
| size/qty 字段 | 可解析为有限 Decimal | 阻断,fail closed |
4.2 日志字段设计
每条盘口快照落日志时,至少包含以下字段。这不是业务日志,是可追溯的证据记录。
| 字段 | 来源 | 说明 |
|---|---|---|
symbol |
请求参数 | 规范化后的品种代码 |
source |
数据源标识 | 区分多源接入 |
timestamp_received |
客户端 | 本地接收时间 |
timestamp_source |
数据源返回 | 原始时间戳 |
market_status |
数据源返回或推导 | open/closed/pre_market/unknown |
bid_price_1 |
数据源返回 | 买一价 |
bid_size_1 |
数据源返回 | 买一量 |
ask_price_1 |
数据源返回 | 卖一价 |
ask_size_1 |
数据源返回 | 卖一量 |
spread |
计算 | ask_price_1 - bid_price_1 |
depth_levels |
数据源返回 | 实际返回的档位数 |
status |
校验结果 | pass / need_review / fail_closed |
raw_snapshot |
数据源返回 | 完整原始返回 |
五、raw_snapshot 保存方案与状态机
5.1 raw_snapshot 保存
raw_snapshot 是数据质量的最后一道防线。它不判断对错,但让每一次冲突和异常变得可追溯。
保存方案很简单,但有几个约束必须遵守:
- 原始返回在落日志之前不做任何字段转换,保留数据源返回的原始结构和类型。
- 落库或写文件时,
raw_snapshot作为一个完整 JSON 字段存储,不拆散成独立列------拆散可能改变嵌套层级,影响后续回放。 - 本地文件按日期和 symbol 分区:
raw_snapshots/2026-06-30/600519.SH/。
5.2 fail closed / need_review 状态机
校验结果分三种状态,不是二元的"成功/失败"。
| 状态 | 含义 | 触发条件 | 下游行为 |
|---|---|---|---|
| pass | 所有字段校验通过 | 必填字段齐全、类型正确、数值合法 | 进入正常处理流程 |
| need_review | 部分字段异常,但快照可保存 | 层级不匹配、市场状态未知、可选字段缺失 | 快照写入日志,标记需人工复查 |
| fail_closed | 关键字段校验失败 | symbol 不匹配、bids/asks 为空、price 解析失败 | 阻断,不进入下游;记录错误详情 |
设计考量:为什么需要
need_review这个中间状态?因为不是所有字段异常都应该阻断。市场状态未知时,快照仍有参考价值。层级不匹配时,数据仍可用于一档分析。但如果因为"不完全符合预期"就丢弃数据,大量有效样本会白白浪费。need_review让数据先进日志,让复查后决定,不默认为失败,也不默认为安全。
六、TickDB 在这里的合理位置
【适合谁】
已经在看盘口、bids/asks、深度数据,但想把观察过程结构化记录下来的人------量化行情观察员、金融研究员、需要验证行情字段的数据工程读者。
【解决什么】
TickDB 可以作为候选结构化行情入口,帮助你把盘口和深度相关字段、时间信息、样本记录和异常排查放进同一套验证流程里。它不证明你的盘口信号有效,不替你判断买卖,不提供完整 Level 2、NBBO、暗池、全量订单簿或高频能力。
【怎么验证】
用自己的 symbol 和市场场景,按五个问题逐项核对:层级、字段语义、时间戳、刷新语义、跨市场适用性。保存请求参数、原始返回和检查时间。先证明数据能被解释,再讨论它是否能进入信号研究。
【不适合什么】
不适合用来证明某个盘口信号一定有效;不适合做荐股;不适合承诺收益;不替代策略回测、样本检验和风控系统。
七、盘口能做什么,不能做什么
| 能做 | 不能直接做 |
|---|---|
| 观察 bid/ask spread | 预测涨跌 |
| 判断流动性状态 | 证明策略有效 |
| 发现报价缺失或异常 | 给买卖建议 |
| 记录盘口变化样本 | 重建完整订单流 |
| 作为信号研究的候选输入 | 承诺高频做市或套利能力 |
| 验证字段结构和数据质量 | 证明某个数据源永远更准 |
盘口是一扇窗,不是一张地图。窗让你看到更多,但地图告诉你方向。把窗当成地图,你看到的东西越多,越容易走错路。
你现在看盘口时,是把它当展示、当状态观察,还是已经当成信号了?有没有遇到过买一大单看起来很强,结果很快撤掉或完全不按预期走的情况?评论区聊聊你的排查经历。
本文不讨论策略有效性,不构成投资建议,只拆解盘口数据的观察边界和验证方法。
标签:盘口数据 / bids/asks / 深度行情 / 信号验证 / 工程复盘