🗄️ 因子存储与数据质量监控

因子的两种存储模型与元数据表、版本管理与新旧对比报告、每日更新流水线的原子切换纪律、六项数据质量检查与策略层的契约——让因子库成为可审计的资产。

研究早期,因子是散落在 Notebook 里的几行代码;研究成熟后,因子必须成为可复用、可审计、可监控的资产——因为组合模型每天都要消费几十个因子的最新值,任何一个静默出错都会污染仓位。本篇讲因子资产的工程化:存储、版本、流水线与质量监控。

因子的存储模型

因子输出是统一形状的三维数据:因子 × 日期 × 股票。两种主流存储:

  1. 宽表(每因子一张 Parquet 表,index=日期,columns=股票代码):与研究代码的 DataFrame 直通、按因子读取快。适合个人与中小团队。缺点:加股票要改所有表、稀疏(停牌日)占空间——用 nullable 类型 + 压缩可解;
  2. 长表(一张大表,列 = date, code, factor, value):按日期/股票切片灵活、新增因子零迁移、对数据库友好。适合因子数 > 数百的团队。可上 ClickHouse/DuckDB(见调度与数据库 )。

无论哪种,必须与因子元数据表配套:

1
2
factor_meta: factor_name, version, definition(公式/伪代码), universe(适用股票池),
             frequency, window(回看窗口), depends_on(依赖字段), owner, created_at, status

「这个动量因子用的是 20 日还是 60 日、复权了吗、含不含 ST」——半年后没有人记得,元数据表就是记忆(这与Git 的 commit 纪律同构:因子定义的每次修改提交一次,version 递增)。

版本管理:因子值是会漂移的

同一因子名的值随三件事漂移:定义修改(v1 用 20 日、v2 用 60 日)、数据回填(数据商修正历史财报)、股票池调整。不管理版本的下场:三个月前的回测结果无法复现,且说不清是因子变了还是数据变了。

最小可行方案:因子表名带版本(momentum_60d@v2),新版本生成时跑一次新旧对比报告(相关性、IC 差异、差异最大的 20 个股票-日期对),人工确认后切换默认版本、旧版本只读归档。这是「数据层的 A/B 测试」(见实验设计 )。

每日更新流水线

生产因子的更新是一条定时流水线(工程骨架见任务调度 ):

1
2
17:00 行情落地 → 17:10 校验通过 → 17:15 计算增量因子 → 17:20 质量检查
→ 17:25 写入正式表(原子切换) → 17:30 通知策略层

三条工程纪律:先写临时表、校验通过后原子重命名(绝不直接覆盖正式表——写到一半崩溃会让策略层读到半张表);增量计算要定期全量重算对账(每月一次全量重算,与增量结果 diff,捕捉滚动窗口类因子的累积误差);依赖排序(财报因子依赖财报表更新完成,用调度器声明依赖而不是猜时间)。

数据质量监控:六项检查

每天因子落地后自动运行,任何一项失败即告警并冻结策略信号(宁可停一天,不可带病交易):

  1. 新鲜度:最新日期 = 预期交易日;行数 vs 股票池规模的偏差 < 2%;
  2. 零方差检测:某因子当日全市场标准差 ≈ 0 → 计算逻辑异常(如数据没接上全为默认值);
  3. 极值占比:|z| > 5 的比例突变(正常 < 0.5%)→ 单点异常或数据源切换;
  4. 缺失率突变:某字段缺失率较 20 日均值跳升 > 3σ → 上游接口变化;
  5. 横截面秩稳定性:与昨日相比,因子排名的 Spearman 相关骤降(非调仓日)→ 排序被污染;
  6. 回溯对账:随机抽 20 个股票-因子-日期三元组,用「当时的原始数据 + 当前代码」重算比对——捕捉「代码悄悄变了」的漂移。

这六项检查是「多源校验」之外的最后一道防线——数据商接口静默变更、上游口径调整、代码意外退化,都会在其中一个检查上现形(数据来源的多源方案见数据来源 )。

与策略层的契约:策略代码只读「正式表 + 版本号」,并在日志中记录当日消费的每个因子版本——回撤归因时才能回答「当时组合吃的是什么版本的动量」(见事后风控 )。

成本意识的提醒

因子工程的复杂度要与研究规模匹配:5 个因子 + 1 个人,宽表 + Parquet + 上面这套检查的「手动脚本版」就够;别在因子数 < 10 时搭建分布式特征平台——工程应该追着研究的需求跑,而不是反过来(见生产系统架构 的「何时升级」讨论)。

关联阅读:数据清洗 是流水线中「校验」一步的依据;Alpha 研究流程 定义因子从 idea 到入库的完整路径;生产运维 把流水线纳入 CI/CD。