← 返回模块
4.1.1.1beta 可读 · 未来免费内容校验中内容版本 2026-05-28

行情数据产品分类与采集

4.1.1 · 市场与微观结构数据 · 量化全流程

周一早盘,某上海私募的量化研究员盯着屏幕:你昨天回测的沪深300日线截面动量因子,年化收益比上周报表显示的低了 280bp。同样的标的池、同样的回看窗口、同样的代码——但 bug 不在信号里,而在数据里。供应商的「复权收盘价」字段在隔夜悄悄又重算了一轮分红复权(back-adjustment),你的损益列于是双重计入了复权调整。本课要交给你的,就是看 5 分钟而不是 5 小时能识别这一类 bug 的词汇表:量化策略实际读到的是什么字节、它们的时间戳含义、哪些字段是信号、哪些是测量伪影。

五种核心行情数据产品

本模块后续每一课——L2 的限价委托簿(limit order book, LOB)机制、L3 的微观结构现象、L4 的清洗管线——都默认你能把下面五个产品对象按顺序复述:

1. OHLCV bar                — 固定时长的成交活动汇总;ts_close 标记 bar 的结束时刻
2. tick / trade print       — 每条逐笔成交一行;price / size / side / condition_flags / sequence_no
3. top-of-book quote        — 买一价 + 卖一价 + 对应挂单量;BBO 任一字段变化即刷新
4. aggregated depth-of-book — 每档累计挂单量(前 N 档买卖盘,L2 payload)
5. full order-by-order book — 每一笔挂单按 order-id 暴露(L3 payload)

OHLCV bar 承载 symbolts_closeopenhighlowclosevolume,常附 vwapn_trades。读 ts_close 时务必小心:它标记的是该 bar ​数据可知​ 的那一刻——窗口的 ​结束​​,不是开始。能防止重采样里 off-by-one 错误的生产规则是「始终存收盘时间戳;开盘时间戳由减法导出」。

tick(也叫 trade print / 逐笔成交)是每条匹配成交一行。字段:symboltspricesizeside(或等价的 aggressor_flag)、condition_flagssequence_noside 字段记录主动方——吃流动性的可成交订单。上交所 / 深交所 Level-2 逐笔成交直接发布该字段(值为 主买 / 主卖 / 中性);缺失该字段的供应商归一化(vendor-normalised)数据流用 Lee-Ready 的 tick rule 还原。top-of-book quote 是四元组(买一价、买一量、卖一价、卖一量),BBO 任一字段变化即刷新——也即所谓 L1 feed payload。

aggregated depth-of-book(L2)对每侧的前 N 档价位暴露累计挂单量——足以计算 queue-imbalance 信号,但不足以做队列位置(queue position)建模,因为它不暴露个体挂单。full order-by-order book(L3)暴露每一条挂单,附 order_idsize、到达序号——足以算出你自己挂单的队列位置,也足以仅凭 {add, cancel, trade} 事件流从冷启动重建委托簿(order book)。CN 的 L3 payload 对应方案是上交所 / 深交所 Level-2 的逐笔委托 + 逐笔成交。

信息包含关系是 L1 ⊂ L2 ⊂ L3。三元权衡(延迟、存储成本、供应商订阅费)随档位升高而急剧上升。研究栈默认:L1 + L2;L3 留给场内微观结构研究和执行算法工作。

频率层级

挑「仍能分辨策略时间尺度」的最粗 bar;更细一档都是浪费——多余存储、多余算力、多余过拟合风险:

| frequency        | storage              | use case                                  | horizon            |
| daily            | ~1 KB / symbol-year  | 因子研究 + 隔夜回测                       | days-to-months     |
| minute           | ~100 KB / symbol-year | 日内信号 + 执行调度                       | minutes-to-hours   |
| second-or-finer  | ~10 MB / symbol-year | 短期统计套利 + 执行算法                   | seconds-to-minutes |
| tick             | ~1-10 GB / symbol-year | 微观结构研究 + alpha 生成              | sub-second         |

A 股全市场约 5000 只标的、一年的日线数据约 5MB——一台笔记本就能装下;同样的全市场一年分钟线约 500MB;同样的全市场 tick 数据动辄数 TB,存于 TimescaleDB 超表(hypertable),背后是数据工程组维护的管线。策略持仓一天,日 K 是对的;持仓一小时,分钟 K 是对的;除非策略真的在消费更细颗粒度,否则更细就是浪费。

复权:为什么列会被双重计入

公司行动(corporate action)涵盖发行人对既有股份的股数或现金权利做出的一切变更——拆股、现金分红、股票分红、分拆、并购。对 A 股而言,两件事承担 95% 的工作量。

​送股 / 拆股因子(乘法)。​ 除权除息日 D 的 10 送 10 转 30(即拆股因子 5x)让 D 之前每个历史价格除以 5、每个历史成交量乘以 5。累计拆股因子(cumulative split factor)= 该价格序列起点到当下所有送转因子的乘积。贵州茅台 600519 历史上有过送转事件,今天 1 股的持仓在更早期需要按累计因子还原成多股的等价头寸。

​现金分红(在 back-adjustment 约定下做加法)。​ 除权除息日之前的每个历史价格被减去对应的现金分红金额,从而保证跨越除权除息日的持仓总收益(total return)连续。另一种 (close - div) / close 的乘法约定对指数总收益序列是对的,但对单只 A 股的持仓损益 ​不是​ 对的。

显式定义:

# use unadjusted close for live pnl; use adjusted close for research returns; never both
ret_for_pnl = unadj_close.pct_change()       # 跨越除权除息日的持仓总收益计算会出错
ret_for_signal = adj_close.pct_change()      # 研究收益序列的正确算法

unadjusted close(不复权收盘价)是当天真实可交易价格——你的损益(pnl)列必须用它。back-adjusted research close(后复权研究价)是收益计算、因子信号、动量与价值因子流水线的输入。生产规则:​​损益列用不复权;信号列用复权;同一列绝对不能两用​​。这条规则要防的就是开篇钩子里那种悄悄双重计入的 bug。

A 股复权数据的供应商来源:万得(Wind)与 通联数据 / Choice 数据 提供 前复权因子 / 后复权因子 表,由供应商按日重算并维护;上交所 / 深交所 信息披露公告 + CSRC 中国证监会(China Securities Regulatory Commission)公告 是监管口径的源头。一个坑:​​公告日(announcement date)不是除权除息日(ex-date)​​。Wind 与 通联 都把两者作为单独字段发布。复权计算必须只从除权除息日向前应用——绝对不能从公告日应用。从公告日应用就有前视偏差(look-ahead bias):策略在历史上「知道」了未来才被市场学到的复权事件。L4 会把这条规则锤死。

时间戳:三种时基,一个权威形式

每一个行情时间戳都属于下面三种形式之一。存储挑一种、坚持用;展示时再转换:

1. exchange local time   — 仅用于展示;面向人的阅读时刻表
2. UTC                   — 权威墙钟;展示前由本地转换得到
3. epoch nanoseconds     — 权威存储形式;整数、无时区、可排序

资深同事第一天就会强制执行的生产规则:​​存一份权威形式(UTC 下的 epoch nanoseconds);展示再派生本地形式,永远不在存储层做时区计算​​。下游每张表里每个名字以 ts_* 开头的列都用 BIGINT NOT NULL 的 epoch nanoseconds。

CN 半日交易陷阱:除夕节前可能半日交易(按交易所通知);中秋、国庆等节假日依交易所发布的官方日历调整;每年的官方交易日历由上交所 / 深交所 在年初发布,生产管线必须读它、绝不假设固定时间表。夏令时:A 股本身没有夏令时,但当你的策略同时跨 A 股与美股时(例如港股通 / Stock Connect 之外的离岸结构),需要在跨市场的时间戳归一化里把美股的 EDT / EST 切换写进 ingest 层。把 2024-03-15 09:30:00 Asia/Shanghai 转换到 UTC:减去 +08:00 得到 01:30:00 UTC;再乘以 1_000_000_000 与 Unix 历元相减得到 epoch nanoseconds。

Consolidated vs Direct vs Vendor-normalised

第三个轴:字节是怎么到你这台机器的。

| tier              | CN 平台 / 供应商                              | cost          | latency | completeness |
| consolidated      | Level-1 公开行情                              | low / free    | medium  | medium       |
| direct            | Level-2 直连 多播(需会员席位)               | high          | low     | high         |
| vendor-normalised | 通联 / 万得 / 恒生 vendor                     | medium        | medium-high | high     |

Level-1 公开行情由上交所 / 深交所 免费实时发布,含 BBO 与 5 档行情;Level-2 行情是付费产品,含 10 档 + 逐笔委托 + 逐笔成交;典型的 私募 量化 团队 通过 通联数据 / 万得 / 恒生电子 提供的归一化 TCP 流接入 Level-2,直连多播需要交易所会员席位、几乎只有券商自营与极少数大型机构具备。CN Level-2 的供应商订阅约 10 万元 / 年 / 家;直连席位则是百万级别。

研究栈默认 Vendor-normalised L1 + L2 是 A 股 私募 95% 工作流的正确选择;只有极少数高频与最严苛的执行交易(exchange membership 直连)需要 direct。​​研究 数据 来自 万得 / 通联 的 已 复权 日 / 分钟 K线;实盘 行情 来自 vendor-normalised Level-2;直连 多播 行情 是 HFT 才 考虑​​——这是行业内通用的操作现实口号。

参考卡:四条收尾规则

四条规则收尾:

  1. ​写明你的数据集已经做了 / 没做哪些复权调整​​——绝不在没核对的情况下信任供应商的「复权收盘价」。
  2. ​bar 的时间戳是 ​结束​​,不是开始​​——ingest 时换算一次,从此不再换算。
  3. ​挑还能分辨策略时间尺度的最粗 bar​​——更细一档都是存储、算力、过拟合的浪费。
  4. ​研究受成本约束 → consolidated;交易受延迟约束 → direct​​;vendor-normalised 是买方默认。

本课触碰的术语—— 订单簿、限价订单簿、买卖价差、最小变动单位、市场冲击、延迟、成交量加权平均价(VWAP)、时间加权平均价(TWAP)、A 股、ETF 期权、印花税、T+1 结算、涨停、跌停、CSRC 证监会——是 Track-4 反复出现的标准词汇。它们的机制在 L2 与 L3 拆解。

练习

Exercise

你正在为三套量化工作流挑数据 feed。对每套工作流,写出:(i) 在 {daily, minute, second-or-finer, tick} 中仍能分辨策略时间尺度的最 ​粗​ bar 频率;(ii) 在 {consolidated, vendor-normalised L1 + L2, direct} 中满足延迟预算的 feed 档位;(iii) 鉴于策略的 pnl 与 signal 分工,是存 unadjusted close 还是 back-adjusted research close(或两列都存)。

工作流 A:沪深300 + 中证500 全市场约 5000 只 A 股的截面动量因子,周频再平衡,隔夜持仓。

工作流 B:流动性高的 沪深300 ETF(510300)上的 30 分钟均值回归策略,5 分钟决策一次。

工作流 C:同一只 ETF 上的双边做市策略,每条 L1 报价更新都调整挂单。

把三组答案写成三行表格。

提示
bar 频率的选择问「策略最短的决策周期是多少」。隔夜持仓意味着日 K 够;5 分钟决策意味着分钟 K 够;每条 BBO 更新就动手意味着需要 tick 或 L1 报价事件级数据。
提示
对收盘价列选择,回到规则口号。要算 pnl 数字的策略用不复权;要算 signal 的策略用后复权;做市策略直接消费 LOB,收盘价列对其损益基本无关。

Formula Explorer

adj\_close = unadj\_close \cdot \prod_{e} split\_factor_e - \sum_{e} cash\_div_e

参考卡

本课装配的组件,按次序:

  • Inline-code listing — 五个核心行情数据产品对象及其角色描述符;L1 ⊂ L2 ⊂ L3 包含规则。
  • Inline-code table — bar 频率层级与存储 / 用例 / 时间尺度的映射。
  • Fenced ```python 代码块 — pct_change() 纪律对照:unadj_close vs adj_close
  • Inline-code listing — 三种时间戳约定与存储规则。
  • Inline-code table — consolidated / direct / vendor-normalised 三档 feed 的映射。
  • Exercise — 三个工作流(隔夜动量、日内均值回归、BBO 做市),含 Two progressive Hints 各保持简短。
  • FormulaExplorer — 后复权公式 adj_close = unadj_close * ∏ split − ∑ div

四句话纪律总结:写明每一种复权调整;bar 的时间戳是结束;挑能分辨策略时间尺度的最粗 bar;研究受成本约束 → consolidated;交易受延迟约束 → direct。

下一课

把产品分类放进脑子后,下一课打开核心对象——限价委托簿(limit order book),并展示本课提到的每一条报价、每一笔成交、每一档深度,本质上都是价格-时间优先队列对 add / cancel / trade 事件流做出反应的 ​输出​​。OHLCV bar、trade print、L1 / L2 / L3 feed 都是同一个底层数据结构的派生量。L2 把它搭起来;L3 度量它产生的现象;L4 清洗它流出的 tick。本课内化的规则——写明每一种复权调整、只存一种权威时间戳、挑最粗 bar——是后续模块的基石。