因子投资-08-股票池复权财报与时间对齐
回测最危险的错误通常不是公式,而是“在过去使用了后来才知道的信息”。今天下载的价格、财报、指数成分和公司状态往往是最终修订版本。点时研究要求在每个决策日重建:当时有哪些股票、哪些数据已经公开、哪些价格可成交。
本节解决什么问题
回测最危险的错误通常不是公式,而是“在过去使用了后来才知道的信息”。今天下载的价格、财报、指数成分和公司状态往往是最终修订版本。点时研究要求在每个决策日重建:当时有哪些股票、哪些数据已经公开、哪些价格可成交。
前置知识与学习目标
需要第 01 讲收益口径和基本表格操作。学完后你应该能:
- 设计 prices、fundamentals、universe 三张点时核心表;
- 区分报告期、公告日、生效日和交易日;
- 正确处理历史股票池、复权、停牌、涨跌停、ST、新股和退市;
- 画出信号形成、下单、成交、持有和收益计量的时间轴;
- 用断言阻止未来信息进入特征。
直觉与手算
某公司一季报报告期为 3 月 31 日,但 4 月 28 日收盘后才公告净利润。研究者今天能在数据表看到“2026-03-31 净利润”,不代表 3 月 31 日投资者已经知道它。
若公告在 4 月 28 日盘后:
- 3 月 31 日至 4 月 28 日的信号不能使用该数字;
- 最早可把它用于 4 月 29 日开盘后的决策;
- 若策略只在月末调仓,可能在 4 月 30 日收盘后形成信号、5 月首个可交易时点成交;
- 收益必须从成交之后开始计量。
把 report_period 直接与月末收益连接,相当于提前约一个月知道财报。这样的回测可以很漂亮,但现实中无法执行。
定义与公式
三张核心点时表
价格表 prices(date, asset, close, adj_factor)
| 字段 | 含义 | 关键约束 |
|---|---|---|
| date | 交易日期 | date、asset 联合唯一 |
| asset | 永久证券标识 | 不依赖简称,处理代码复用 |
| close | 当日未复权收盘价 | 元/股,正数或有明确缺失原因 |
| adj_factor | 截至数据版本的复权因子 | 记录供应商、下载日和方向 |
研究价格可定义为 close×adj_factor,但总回报应与分红送转事件交叉核对。成交模拟仍需要未复权价格、涨跌停价和最小交易单位。
财报表 fundamentals(report_period, announce_date, asset, field, value)
| 字段 | 含义 | 关键约束 |
|---|---|---|
| report_period | 会计期间结束日 | 不能作为市场可得日 |
| announce_date | 首次公开日期时间 | 决策时必须已发生 |
| asset | 永久证券标识 | 与公司行动映射一致 |
| field | 指标名及合并/母公司口径 | 有数据字典 |
| value | 原始数值 | 保留单位、币种和修订版本 |
若同一报告期多次修订,严格点时库还要保存 revision_time 和版本。任意决策日 d 使用的数据必须满足:
等号能否使用取决于公告和决策的日内顺序,只有日期而无时间时应采取保守滞后。
股票池表 universe(date, asset, eligible)
eligible 是在 date 时是否满足策略可投资规则,而不是今天是否仍上市。建议同时保存 reason 列,例如上市不足、ST、停牌、涨停不可买、跌停不可卖、板块不在范围或数据不足。
一条无歧义时间轴
数据公开 → 信号形成 → 下单 → 成交 → 持有 → 收益结束
每一步都应有时间戳。月末收盘特征若包含当日收盘价,最早只能在收盘后形成;默认用同一收盘价成交就是未来函数。
Python 最小实验
下面在决策日只选择已经公告的最新财报。
1 | |
B 的一季报在 5 月 5 日才公告,所以 4 月 30 日只能使用年报值 8。这个“看起来旧”的值才是当时真实信息集。
A 股实现注意事项
- 历史股票池。 每日重建上市、退市、板块、ST 和指数成分,不能用当前证券列表回看历史。
- 复权。 保存原始价、公司行动和复权因子版本。前复权序列会随未来分红改变历史数值,适合展示但不天然是点时数据。
- 公告日。 以首次公告时间为可得边界;报告期末、预约披露日和数据库入库日都不能互相替代。盘后公告至少滞后到下一可交易时点。
- 停牌。 持仓估值可按规则沿用旧价,但买卖能力为零;复牌跳跃必须进入收益。不能删除停牌期后只保留顺利成交样本。
- 涨跌停。 根据板块和日期应用 5%、10%、20%、30% 等不同制度。触及价格不必然无法成交,一字板、成交量和订单方向要另行判断。
- ST。 是否排除 ST 是投资规则,不是数据清洗。使用历史 ST 生效与解除日期,并报告排除造成的选择效应。
- IPO 等待期。 新股上市初期价格限制、无历史特征和高换手会造成特殊分布。等待 60、120 或 180 个交易日应在研究前规定并做敏感性。
- 退市收益。 退市股票不能在最后正常交易日无损消失。应纳入退市整理期、现金补偿或保守损失假设,并报告缺失处理。
- 代码与公司映射。 公司重组、换代码和借壳会让“证券”“上市公司”“经济实体”不是同一 ID;因子定义要选择正确主键。
- 财报口径。 累计利润需转换单季时注意上一版本;TTM、合并报表、币种、单位和负分母必须有数据字典。
常见错误与边界
- 用 2026 年仍上市股票回测 2010 年,制造幸存者偏差。
- 以 report_period 连接当期收益,提前使用财报。
- 用今天的前复权历史价格直接当作当时可见价格。
- 收盘信号用同一收盘价成交,没有任何延迟或滑点。
- 把停牌价格前向填充后允许每天自由换仓。
- 删除涨跌停、退市和负权益股票,却不报告删除规则。
- 使用当前指数成分和行业分类回溯历史。
- 对全样本计算去极值阈值和标准化参数。
点时数据也不能保证数据正确。供应商可能漏公告、错误映射公司行动或回填退市收益。应以样本抽查、跨源对账、事件计数和会计恒等式做独立验证。
原书与论文
原书第 5 章(PDF 文件页 192–223)集中讨论因子实践问题,第 6 章(224–290)进入中国市场实证。这里把散落的实现风险整理为可执行数据模型。
Hou、Xue 与 Zhang(2020)复现大量异象时强调微盘、样本和实现选择对结论的影响:DOI 10.1093/rfs/hhy131。McLean 与 Pontiff(2016)区分样本外与发表后衰减,也要求严格定义时间边界:DOI 10.1111/jofi.12365。
练习
- 画出“月末收盘计算价值信号、次日开盘成交、持有一个月”的完整时间轴。
- 给 fundamentals 加 revision_time,同一报告期有初始值和修订值;写查询返回决策时最新可得版本。
- 设计 universe 的 reason 编码,至少覆盖未上市、已退市、ST、停牌、涨停不可买、跌停不可卖和 IPO 等待期。
- 对一只发生分红送转的股票,用未复权价加现金流与复权价两种方法计算总回报并对账。
- 给出退市末端价格缺失的乐观、中性、保守三种假设,说明策略结论是否敏感。
最低验收断言应包括:键唯一、日期有序、价格为正、announce_time 早于 decision_time、收益日期晚于信号日期、每次股票池变化有原因。
小结与下一步
点时研究不是“给财报滞后几个月”这一条规则,而是完整保留当时可得的数据版本、历史股票池和交易状态。prices、fundamentals、universe 三张表加一条明确时间轴,是后续所有回测的地基。
下一讲将在这个地基上做投资组合排序:把截面信号变成高组、低组和多空因子收益。