tick-stock-panel复权机制详解:除权因子、ETF复权与回测指标口径一致性
【免费下载链接】tick-stock-panelTSP自托管、零运维的 A 股「选股 + 监控 + 回测」量化工作台 | LLM能力驱使策略定制+个股分析+复盘 | 自由接入第三方数据源与个性化扩展数据 | 个人开源项目地址: https://gitcode.com/GitHub_Trending/ti/tick-stock-panel
对于 A 股量化新手来说,"复权"往往是第一个踩坑的深水区:除权除息日价格跳空,回测收益算出来全是错的。tick-stock-panel 是一个自托管、零运维的 A 股「选股 + 监控 + 回测」量化工作台,它用一套三层复权架构(事实层存原始价、因子层存除权因子、投影层读取时复权)彻底解决了复权口径混乱的问题,让 K 线、技术指标与回测指标天然一致。这篇完整指南带你一次性看懂它的实现原理。
为什么复权口径是回测的"隐形杀手"?
📌 新手最常见的三个错误:
- 前复权价直接落库——每次分红送转后全量数据要重算,历史分区容易和最新数据"各说各话";
- ETF 拆分不复权——ETF 份额折算后 K 线留下人为跳空,均线、波动率指标全部失真;
- 分钟K与日K口径不一致——均价线(VWAP)用的是原始成交额,价格却用前复权价,两者单位都"对",合在一起就是错的。
tick-stock-panel 的解法是:本地永远只存原始价,复权只发生在"读取时"。这样无论除权因子怎么更新,历史分区都不用重写,天然幂等。
三层复权架构:事实层、因子层、投影层
这套架构在 minute_adjust.py 的模块头注释中写得很清楚,日K与分钟K共用同一套设计(可参考 CONTRIBUTING.md 的"价格与复权"一节):
1️⃣ 事实层:只存原始价
- 日K/分钟K(
kline_daily、kline_minute等分区)落盘后不可变,全部是不复权的原始 OHLC; - 数据契约要求:enriched 表的
open/high/low/close为前复权价,同时保留raw_close/raw_high/raw_low三个不复权原始字段,双口径可随时切换; - 数据源插件(provider)不得自行复权,只能回传原始价 + 除权因子,复权统一由 enriched 管道完成(见 plugin-development.md)。
2️⃣ 因子层:除权因子只追加
- 股票与 ETF 分别存在
adj_factor/adj_factor_etf表中,字段为symbol / trade_date / ex_factor; - 因子表只追加不修改(全市场一年约 5000 条事件,仅数千行),并带 mtime 内存缓存,读盘约 10ms。
3️⃣ 投影层:读取时复权
复权比率的核心公式:
ratio = 截至 D 日累积因子 ÷ 全部累积因子
价格四件套(OHLC)与amount(成交额)同乘 ratio,而volume保持不变——成交额同乘保证 VWAP 与复权价严格同口径。由于"今天不可能有未来事件",当日 ratio 恒为 1,盘中实时路径整体短路,全市场当日复权开销为零;单股单日 240 根分钟K复权实测仅增加 6~9ms。
ETF 复权:为什么必须用完整本地历史重算?
这是新手最容易忽视的场景。股票路径在新除权事件到达后会重算受影响标的的全部日期;而 ETF 路径如果只把本次拉取的窗口送进复权管道,历史分区就会停留在未复权价上,日K在份额折算日留下明显跳空。
项目的做法是:ETF 增量日K同步时,用完整本地历史重算前复权,把拆分日之前的历史收盘价改写为前复权价,同时保留raw_close原始值。这个逻辑在 kline_sync.py 中实现,并有专门的回归测试 test_etf_adj_factor.py 覆盖"增量拉取一日、历史必须改写"的场景。
💡 没有除权因子权限(Starter 以下档位)时,系统会优雅降级为未复权并在页面提示,而不是默默给错数字。
分钟K的原始基准迁移:.raw_basis 标记
老用户库里可能存在**"拉取时前复权"的旧数据**(混合口径)。tick-stock-panel 提供了一个幂等的存量迁移任务:
- 以日K原始收盘价为锚点,检测价格比值 k:k≈1 说明已是原始价,k 偏离 1 则该分区是前复权价,除以 k 还原;
- 全部分区换算成功后创建标记文件
data/kline_minute/.raw_basis,此后读取自动应用复权投影、新拉取改为adjust='none'; - 迁移失败不写标记,随时可续跑,幂等安全。
相关 API 入口见 api/kline.py 中的分钟K迁移与 enriched 全量重算接口——后者不获取任何数据,仅基于已有的kline_daily + adj_factor重算复权与指标,是核对口径最快的工具。
回测与模拟交易的口径一致性
复权机制最终的价值,体现在"每一分钱都对得上":
- 回测:统一使用 enriched 前复权 OHLC 计算信号与收益,除权日不产生虚假跳空,Sharpe、回撤等指标可信(回测入口见 backtest/);
- 模拟交易(paper trading):价格一律用不复权 raw 价(与真实下单一致),持仓市值用
raw_close;日终结算时若检测到当日除权因子,则自动按因子调整持仓数量与成本、现金分红入账,并记一条corporate_action冲正成交,保证净值曲线连续(详见 paper-trading-plan.md); - 数据核对清单:项目把"单位、复权、交易日、时区、资产类型"列为开发必查项,复权相关测试(test_minute_adjust.py、test_price_limits.py)全部采用固定样本数值断言。
如何验证自己项目的复权口径?
✅ 上手 tick-stock-panel 后,建议按这个顺序自查:
- 在数据页触发一次除权因子同步,确认
adj_factor表有当日/近期事件; - 找一只近期除权的股票,对比
raw_close与close的比值是否等于累积因子; - 运行全量重算 enriched,确认指标在除权日前后无跳空;
- 跑一次跨除权日的回测,检查收益曲线与模拟净值曲线是否连续。
总结
tick-stock-panel 的复权机制可以浓缩为一句话:存原始、因子只追加、读取时投影。这套设计让 K 线展示、技术指标、回测与模拟交易共用同一套可信口径,ETF 拆分、分钟K均价线这些新手高频踩坑点都有明确的实现与回归测试兜底。对于想搭建 A 股量化工作台的开发者,这套三层架构是非常值得借鉴的工程实践。
延伸阅读:docs/features.md(复权功能定位)、docs/factor-system-design.md(因子与指标流水线设计)。
【免费下载链接】tick-stock-panelTSP自托管、零运维的 A 股「选股 + 监控 + 回测」量化工作台 | LLM能力驱使策略定制+个股分析+复盘 | 自由接入第三方数据源与个性化扩展数据 | 个人开源项目地址: https://gitcode.com/GitHub_Trending/ti/tick-stock-panel
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考