你的筛选器跑出来一个结果:某个标的日均成交额 800 万,深度够,成本可覆盖。
数字看着很正常,你信了。
而那 800 万里有六成是刷出来的——你的管线没有报错,也不会报错。它忠实地把交易所给的数字加了起来。
⚠️ 这一篇原本是第 15 篇的后四节。 那一篇越写越长,而它其实混了两件不同的事:
| 回答什么 | |
|---|---|
| 第 15 篇 | 盘口和成交数据怎么读 |
| 本篇 | 你自己采集和计算这些数据时会遇到什么 |
⭐ 而它和第 26 篇也有明确分工:
第 26 篇给判据(阈值、否决顺序);本篇给实现(这些数怎么算才是对的)。
本系列仅用于记录与普及基础知识,不构成投资建议。本篇是工程内容,不含任何交易规则。
零、⭐ 贯穿全篇的一句话
⭐ 数据错误不会报错。它只会静默地给出一个看起来合理的结果。
打个比方
代码错误像汽车抛锚:车不走了,你立刻知道出事了,然后去修。
数据错误像里程表少算了 10%:车照常开,仪表盘一切正常,你所有的油耗计算都基于一个错的数——而它永远不会提醒你。 你会一直用着,直到某天在别的地方对不上账。
⚠️ 这就是为什么这一篇的组织方式和一般的工程文章不同:它不按"功能模块"排,而是先列出所有会静默出错的地方,再讲怎么给它们造出一个能报警的机制。在这个领域,“没报错"从来不是"没出错"的证据。
代码错误会崩、会抛异常、会被测试抓住。数据错误不会——它产出的是一个数,而那个数落在合理区间里,你没有任何理由怀疑它。
⚠️ 这和第 38 篇第九节讲的回测引擎问题是同一类:规则自洽、代码正确、结果偏乐观,没有任何一处会报错。
⭐ 所以这一篇的组织方式是:先讲会静默出错的地方(第一节),再讲怎么造出有已知答案的校验(第二节)。
一、⚠️ 五个会静默出错的地方
① 刷量(Wash Trading)
部分交易所的成交量含虚假成分(自成交、激励性刷量),这直接污染 RVOL 和成交速度这类指标。
⭐ 判断方向:优先用流动性最深的几家;对小平台的绝对成交量保持怀疑;交叉比对不同来源。而第 15 篇那条推论在这里就是解法——刷量能做大成交额,做不出真实的买单深度,因为挂真单要承担被成交的风险。
② 时间戳与 K 线口径
K 线区间是左闭右开还是左开右闭?
时间戳是成交时间还是撮合引擎接收时间?
时区与夏令时(⚠️ 美股时段会漂移一小时,见第 3 篇)
不同平台对同一根 K 线的切分可能不一致
⚠️ 后果:跨平台比对时,你可能在拿两根不同时间范围的 K 线做对比,而两边都是"正确"的数据。
⭐ 一条工程纪律:全链路存 UTC 时间戳,只在展示层做时区转换;交易日历(开收盘、假期、夏令时切换)作为独立的数据源维护,不要从时间戳硬推。
⚠️ 而第 40 篇第六节说过,加密的「日线」起点纯粹是约定——这不只是分析问题,它首先是一个数据管道的配置项,而这个配置必须被显式写下来。
③ WebSocket 断线与补数据
实时流会断。断线期间要用 REST 补齐,而补齐逻辑很容易出错:
重复数据 补的区间与已有数据重叠
缺失数据 ⚠️ 断线未被检测到
顺序错乱 补进来的数据插在了错误的位置
正确做法是用连续性做校验,而不是假设流是完整的:用成交 ID 或 updateId 的连续性判断,检测到缺口就明确记录,不要静默跳过。
增量维护订单簿的场景尤其严重:漏一条更新,本地簿就永久偏了,而且之后每一次读数都是错的——它同样不会报错。
④ REST 与 WebSocket 不一致
两个接口可能有不同的聚合方式、精度和延迟。
同一个指标用两条路算出来对不上,通常不是 bug,是口径不同。
但先别假设是口径——第二节会说明,这个差异恰恰可以被做成一个校验。
⑤ 幸存者偏差
已下架、已归零的币会从数据源上消失。用「现在还能拿到数据的币」做历史回测,结果必然偏乐观(第 7 篇、第 26 篇)。
这一条的性质和前四条不同:前四条是采集问题,这一条是存储问题——它要求你在标的还活着的时候就把它记下来,事后无法补救。
point-in-time 标的池不是一个查询,是一个必须从第一天就开始积累的资产。
二、⭐ 造出有已知答案的校验
⭐ 这一节做的就是给里程表配一个已知长度的路段:先造出一段你知道正确答案的数据,让管线跑一遍,看它算出来的对不对。没有这个动作,你的所有指标都是没校准过的仪表。
第 38 篇第九节给了一个思路:你所有的回测都没有已知答案,所以它们无法验证引擎;只有那个随机策略测试可以,因为它有已知答案。
数据管道可以用同样的方法,而且更容易——因为数据本身带着好几个恒等式。
⭐ ① 聚合可加性:这是恒等式,不是近似
第 27 篇第三节那条结论——周期之间是恒等关系不是相关关系——在这里变成了一个可执行的断言:
1 根 1 小时 K 线 ≡ 它内部那 12 根 5 分钟 K 线
⇒ 成交量: Σ(12 根 5min 的量) == 1 根 1h 的量
⇒ 最高价: max(12 根 5min 的高) == 1h 的高
⇒ 最低价: min(12 根 5min 的低) == 1h 的低
⇒ 开盘价: 第 1 根 5min 的开 == 1h 的开
⇒ 收盘价: 第 12 根 5min 的收 == 1h 的收
⭐ 这五条必须精确成立(浮点误差以内)。不成立就是数据有问题,没有第二种解释。
⚠️ 它能抓到的东西非常多:口径不一致、缺 K 线、时区错位、补数重复。而且它不需要你知道"正确答案"是什么——恒等式自己就是答案。
② OHLC 的不等式:逐根可断言
low ≤ min(open, close) 且 max(open, close) ≤ high
volume ≥ 0
成交额 ≥ 0,且 (成交额 ÷ 成交量) 落在 [low, high] 之内
最后一条最有用:成交额 ÷ 成交量 就是这根 K 线的 VWAP(第 15 篇第一节),它必然落在最高价和最低价之间。落在区间外,说明两个字段来自不同的口径或不同的时间范围。
③ 盘口的不等式
best_bid < best_ask (交叉了说明快照撕裂)
买单价格严格递减,卖单价格严格递增
相对价差 = (ask − bid) / mid ⚠️ 不应为负,也不应大到离谱
深度累计量单调不减
⚠️ 「快照撕裂」是增量维护订单簿最常见的静默故障:买一价高过卖一价,而程序照样算出一个 OBI 数值给你。
④ 两条路径的交叉校验
第一节 ④ 说 REST 与 WS 口径可能不同。把它反过来用:
每天定时,用 REST 拉一段历史,和 WS 落库的同一段比对
记录差异的分布,而不是只看"对不对"
差异应当是有界且稳定的。 差异的分布突然变化,说明有一边出了问题——这比"某天发现数字不对"要早得多。
⑤ 回填与实时必须走同一条代码路径
这一条是工程结论,但它的后果直接落在第 38 篇上:
如果历史回填走一套代码、实时计算走另一套,你回测的和你实盘跑的就是两个不同的指标——而两边都不会报错。
正确做法是:指标计算只写一份,回填和实时都调它,区别只在数据从哪来。
顺带一条:本节这几个校验有一个很实用的性质——它们不在乎代码是谁写的。所以让 AI 写实现是安全的,只要校验由你定义、且是有已知答案的那种(第 47 篇第四节)。
三、一个筛选器该算什么(指标层)
📌 这里给的是指标层——该算哪些数。把它们组装成一台每天能跑一遍、输出「今天哪些能做」的完整漏斗,见 第 26 篇,那里有阈值、分层否决顺序和三个数据陷阱。
必须有(回答「能不能交易」)
□ 相对波动率 ATR / 价格
□ 波动扩张 当前 ATR / 历史 ATR 均值
□ 成交量异常 同时段 RVOL 或 Z-score ← ⚠️ 注意基准(第 15 篇第三节)
□ 价差成本 (Ask − Bid) / 中间价,bps
□ 冲击成本 逐档吃穿目标仓位(第 15 篇第五节)
□ 出场压力比 仓位 ÷ ±1% 买单深度 (深度口径,第 26 篇判据 > 0.3 出局)
□ 出场流量比 仓位 ÷ 最差时段分钟成交量 (成交量口径)
□ ⭐ 数据健康 成交 ID 连续性、最后更新时间、第二节那几条断言
⭐ 最后一行是这份清单里最容易被跳过、也最该有的一项:
如果你不知道数据是不是好的,上面六个数算出来是多少都没有意义。
⚠️ 两个「出得去吗」不是同一个数
清单里那两行经常被当成同义词,但它们口径不同、失效方式也不同——⭐ 而区别正好是第 33 篇第五节那张时态表:
| 分母 | 时态 | ⚠️ 它的弱点 | |
|---|---|---|---|
| 出场压力比 | ±1% 买单深度 | 现在·快照 | 挂单可撤,所以它偏乐观 |
| 出场流量比 | 最差时段分钟成交量 | 过去·流量 | 是历史,未必代表今天 |
两个都要算:深度告诉你「此刻簿子上有多少」,成交量告诉你「这个时段通常能吸收多少」。深度好但成交量薄,意味着那些买单可能是一次性的(第 15 篇第五节:补充速度才是真流动性)。
而无论用哪个口径,两条纪律相同(第 2 篇:能买到 ≠ 能卖出去):用买单侧,用最差时段——因为你需要出场的时候,很可能就是那种时刻。
可选(描述当前状态,不产生信号)
□ 主动买卖比 第 15 篇第二节
□ 成交速度 笔/分钟
□ CVD 累计,仅作状态观察
□ 资金费率 第 4 篇(⚠️ 封顶后失真)
□ 未平仓量变化 第 4 篇 + 第 33 篇第五节的四象限
明确不要做的
✗ 把任何单一指标设成阈值就触发「买入提示」
✗ 用 OBI 做分钟级判断 (第 15 篇第六节:时间尺度不匹配)
✗ 用「前几根 K 线」作成交量基准(第 15 篇第三节)
✗ 回填和实时用两套计算代码 (第二节 ⑤)
✗ 在没过第 38 篇那条流水线之前,把任何组合当成策略
四、⭐ 写这些代码到底值多少
回到第 7 篇的四个优势来源:
信息优势 ❌ 对个人基本封死
速度优势 ❌ 彻底封死——你不可能赢过专业做市商的机房
分析优势 🟡 有限,且会衰减
⭐ 行为与结构优势 ✅ 开放
⭐ 一个能自动筛选、自动算仓位、自动记日志的工具,作用的是最后一项。(这一项的完整展开见第 46 篇。)
它让你更可能按计划执行,而不是让你比别人更快。
⚠️ 这一条值得说死,因为自建数据管道最常见的动机是错的:很多人写这些代码是为了"更早发现信号”——而那是速度优势,是封死的那一格。
⭐ 而它真正能兑现的价值有三项,全部可查证:
| 为什么确定 | |
|---|---|
| 降低执行成本 | 第 26 篇:往返成本从 0.2% 压到 0.04%,可交易比值提高 5 倍 |
| 消除情绪干扰 | 第 9 篇:七种可预测的失败,规则化能挡掉大部分 |
| 让检验成为可能 | 第 38 篇:没有干净的数据,整条检验流水线都无从谈起 |
第三项是最容易被低估的:数据管道本身不产生任何收益,但没有它,你连"这个想法有没有用"都无法回答。
五、能回答和不能回答什么
| 问题 | |
|---|---|
| 我的 K 线数据有没有问题 | ✅ ⭐ 聚合可加性是恒等式,不成立就是错的 |
| 这两个字段口径一致吗 | ✅ 成交额 ÷ 成交量 必须落在 [低, 高] 内 |
| 我的订单簿本地副本还准吗 | ✅ 连续性校验 + 买一卖一不交叉 |
| 我回测用的指标和实盘一样吗 | ✅ ⭐ 只写一份计算代码,问题就不存在 |
| 我该算哪些量 | ✅ 第三节清单 |
| 这些量的阈值该定多少 | ❌ 那在第 26 篇 |
| 这个平台的成交量是真的吗 | ⚠️ 不能直接判定——但可以改用深度,刷量伪造不了它 |
| 我能补回已下架标的的历史吗 | ❌ ⚠️ 不能,必须从第一天开始存 |
| 写了这套工具我能不能赚更多 | ❌ 它作用于行为与结构优势,不是速度优势 |
六、这一篇的结论
- ⭐ 贯穿全篇的一句话:数据错误不会报错,只会静默地给出一个看起来合理的结果。 这和第 38 篇的回测引擎问题是同一类——规则自洽、代码正确、结果错误。
- ⚠️ 五个静默出错的地方:刷量、K 线口径与时区、断线补数、REST 与 WS 不一致、幸存者偏差。⭐ 而幸存者偏差性质不同——前四个是采集问题,它是存储问题:point-in-time 标的池不是一个查询,是必须从第一天就开始积累的资产。
- 对付静默错误的唯一办法是造出有已知答案的校验,而数据本身带着好几个恒等式:
- 聚合可加性:12 根 5 分钟的量之和 必须精确等于 1 根 1 小时的量,高低开收同理。不成立就是数据有问题,没有第二种解释。 它能抓到口径不一致、缺 K 线、时区错位、补数重复——⚠️ 而且不需要你知道正确答案,恒等式自己就是答案。
成交额 ÷ 成交量必然落在 [最低价, 最高价] 之内——落在区间外,说明两个字段口径不同。- 快照撕裂(买一价高过卖一价)是增量维护订单簿最常见的静默故障,而程序照样能算出一个 OBI 给你。
- 把 REST 与 WS 的差异做成日常校验,看的是差异的分布而不是"对不对"——分布变了比某天数字不对要早得多。
- 回填和实时必须走同一条代码路径,否则你回测的和实盘跑的是两个指标,而两边都不会报错。
- 指标清单里最该有、也最常被跳过的一项是「数据健康」:不知道数据好不好,其余六个数算出来是多少都没有意义。第二容易漏的是:「出得去吗」有两个不同口径的数——出场压力比(深度·现在,但挂单可撤所以偏乐观)和出场流量比(成交量·过去)。两个都要算,且都必须用买单侧、最差时段。
- 写这些代码的价值不在"更早发现信号"——那是速度优势,对个人是封死的。它作用的是行为与结构优势:降低执行成本、消除情绪干扰,以及让检验成为可能——数据管道本身不产生收益,但没有它,你连「这个想法有没有用」都无法回答。