NOTE · 技术笔记

A 股复权口径的三个坑

做量化研究,最容易被忽略又最致命的前提,是「你手上的价格序列到底是哪个口径」。这篇不讲概念,而是把我在这条路上踩过的三个坑摊开——每个都有具体的日期、具体的数字、具体的修复代码。看完你可以直接拿下面的校验代码测自己的数据。

坑一:前复权会「整体漂移」——今天的数字算不出昨天的结论

前复权的算法是「从最新价往回推」:把每一次除权除息事件反向修正到历史价格上。这意味着——每发生一次新的除权,整条历史序列都会被追加重算一遍。

我实测过一组对照:同一只票、同一段区间,只因为中间发生了一次分红除息,历史段的收盘值序列发生了系统性平移,年化波动率的差异到了小数点后一位。它不是数据源的 bug,是前复权自身定义决定的。

更贵的学费在另一处:早年我回填复权因子时漏了部分除权记录,拿错误序列算出来的 IC 偏差到约 5%——这个量级足够让一个因子的显著性判断翻向。

规矩:涉及「可复现」的结论,锚定数据版本(数据集版本号),并记录复权口径;不要用「今天的前复权数据」去否定「昨天的前复权数据」。

坑二:不复权序列在除权日有「假跳空」

用不复权价格直接算收益,除权日当天会出现一次「假跌」:价格被除权除息机制性地下调,但这不是市场真实下跌。

后果很具体:短线策略的「破位信号」会被这种假跳空错误触发——你以为价格跌破了关键位,其实只是分红派息。凡是用不复权价格算「涨跌幅」的地方,都必须显式区分「真实下跌」与「除权造成的价格调整」——在我系统里这两者走不同的库表路径。

规矩:回测入口强制要求带复权口径的已收盘日线;不复权序列只用于「交易撮合」等需要真实报价的场景,且与收益计算严格分开。

坑三:区间查询漏掉「起点前的那次除权」

这是我修过的真实缺陷——数据源查询区间早于回填起点一段就查不到:

# 危险写法:只查回填窗口内的除权事件
factors = query_adjust_factor(code, start_date=回填起点, end_date=今天)
# 实测问题:某标的 2021-01-04 的复权因子需要 2020-07-23 的除权事件,
# 从 2021 年起查 ⇒ 漏掉 → 该票前段因子全错(错误码 0 + 返回 0 行,不报错!)

前复权因子是累积语义(前复权因子 = Π 未来各次除权比例),序列首段的因子恰恰依赖起点之前最近一次事件——「起点后无事件」不等于「因子 = 1.0」。

修复(可复现):

1. 查询起点一律从回填起点再往前推 24 个月(覆盖多次分红,含中期分红);

2. 对「首行晚」的标的,从 2005 年起把除权记录全量重拉、追加进因子表;

3. 记住一个判据:「错误码 0 + 0 行」不等于正常——要显式区分「指数/无除权标的(本来就该 0 行)」和「数据缺失」。

最后防线:back 恒等式与脏行修复

所谓 back 恒等式:后复权价 ÷ 前复权价 = 常数(同一区间、同一数据版本下)。

它看起来只是数学性质,实际是数据质量的最后一道防线:任何一次除权事件只要前、后两套序列中有一套算错,或者除权因子缺失,这个比值就会漂移。我在数据层加了这条恒等式作为入库校验——不满足就拒绝整批数据,而不是「记一条 warning 继续跑」。

它抓到过一类最阴的脏行:某数据源偶尔返回 backAdjustFactor=1.0 的异常行(不报错、不像缺失,就是值不对)。处理方式不是直信、也不是直接删,而是双源反推修复:

# 1) 拉双源:adjustflag='3' 原始价 raw_close / adjustflag='2' 前复权价 pre_close
# 2) 段内因子:对嫌疑标的取事件日之前的日期段,逐日算 pre_close / raw_close,取中位数 f0(抗离群)
# 3) 反推补行:add_back = f0 × factor_table[-1].value(保留 6-8 位小数)
# 4) 真值校验:adjustflag='2' 全窗前复权价 vs raw × hist(≤t)/latest 计算价
#    偏差 ≤ 1e-3 → 接受;> 1e-2 → 判脏行,修复后才入库

验收清单(可直接对照)

1. 回测入口只认带 adjusted 口径的数据,缺口径参数直接拒绝(不给默认值);

2. 每个结论携带数据版本指纹,版本变更旧结论自动作废;

3. 入库校验:back 恒等式逐日断言 + 行数守恒 + 除权日跳空检查,不过即拒批;

4. 除权因子查询区间从回填起点前推 24 个月;「0 行」必须与「无事件」区分开;

5. 脏行的处理路径是「双源反推 + 偏差阈值(1e-3 / 1e-2)」,不是猜测性填充。

三个坑其实是一件事:复权口径的每个「默认行为」都藏着一个合同陷阱——前复权默认会漂移、不复权默认有假跳空、区间查询默认会漏事件。处理它们的成本,比事后发现结论错了再回头排查,低一个数量级。


本站内容为个人研究记录与方法分享,模拟,不构成投资建议;据此操作风险自负。


Joyous 的技术笔记 · 沪ICP备2026053770号


隐私最小化:不设 cookie、无表单、无第三方脚本;访问统计仅取服务端日志(≤30 天,仅聚合),不导出 IP。