引言:回测里最隐蔽的"未来函数"陷阱
量化研究中,最让研究员头疼的问题之一不是策略亏钱,而是回测赚钱、实盘亏钱------而且找不出原因。
很多时候,根子不在模型,而在数据。
设想这样一个场景:
你用"申万一级行业分类"作为特征因子训练了一个选股模型。2023年6月,某股票从"房地产"调入"建筑装饰"行业。你用2024年的快照数据跑2023年的回测------这只股票全程被标记为"建筑装饰"。模型在2023年学到的"建筑装饰行业溢价",实际上偷看了未来的行业归属。
这就是典型的"未来函数"问题。而它的本质,正是维度属性随时间缓慢变化,但数据仓库没有正确建模。
在量化投研体系中,特征因子(factor)本身就是一种维度数据------它们描述股票、期货、基金等标的在某一个时间点的状态。这些状态会变,而且变化频率远低于逐笔行情,但一旦变了,就会直接影响回测的准确性。
缓慢变化维(SCD),就是解决这个问题的系统性框架。
量化场景中的 SCD 类型映射
Type 0:不可变属性------因子的"出生证"
典型场景:股票首次上市日期、原始证券代码、IPO 时的发行方式
Type 0 写入后永不更新。在量化中,这类属性是时间锚点,用于计算上市年限、新股因子、事件研究等。
股票维度表(节选):
证券代码 | 上市日期 | 交易所 | 证券类型
000001.SZ | 1991-04-03 | 深交所 | A股
即使后来该股票被借壳、更名、迁址,上市日期永远不变。研究员计算"上市满3年因子"或做事件研究时,依赖的就是这个不可变锚点。
Type 1:直接覆盖------修正而非变更
典型场景:股票简称更正、Wind/聚源数据源的字段纠错
Type 1 用新值直接覆盖旧值,不保留历史。
股票维度表(修正前):
证券代码 | 证券简称 | 总市值(亿)
000001.SZ | 平安银 | 2,100 ← 简称录入漏字
股票维度表(修正后,Type 1 覆盖):
证券代码 | 证券简称 | 总市值(亿)
000001.SZ | 平安银行 | 2,100
这种"修正"对量化研究没有影响------你不会用证券简称做因子。但如果覆盖的是因子计算错误(比如某日市值字段因数据源 bug 填错),就需要配合操作日志追溯,确保回测中不会混入脏数据。
Type 2:版本行------量化 SCD 的绝对主力
*典型场景:行业分类变更、指数成分股调整、ST/ST 状态变化、板块归属调整
Type 2 是量化投研中使用最广泛的 SCD 类型 。它通过版本行 + 生效/失效时间,确保回测时每个交易日都能关联到当时正确的因子状态。
案例:行业分类变更(最经典的未来函数来源)
行业维度表(SCD Type 2):
证券代码 | 行业分类标准 | 行业代码 | 生效日期 | 失效日期 | 当前标志
000002.SZ | 申万一级 | 房地产 | 2014-01-01 | 2023-06-14 | N
000002.SZ | 申万一级 | 建筑装饰 | 2023-06-15 | 9999-12-31 | Y
配合日频特征因子表:
日频因子事实表:
交易日 | 证券代码 | 收盘价 | 换手率 | 行业维度关联键
2023-06-10 | 000002.SZ | 12.50 | 1.2% | DIM_202301
2023-06-16 | 000002.SZ | 11.80 | 2.1% | DIM_202302
当回测引擎在 2023-06-10 计算"建筑装饰行业均值"时,关联逻辑是:
WHERE 生效日期 <= '2023-06-10' AND 失效日期 > '2023-06-10'
→ 命中"房地产"那行 → 该股票被归入房地产行业
而在 2023-06-16 计算时:
→ 命中"建筑装饰"那行 → 该股票被正确归入建筑装饰行业
回测完全消除了行业分类上的未来函数。
案例:ST 状态变化
ST 状态对量化策略影响极大(很多策略直接剔除 ST 股),而且变更频率不低:
股票状态维度表(SCD Type 2):
证券代码 | 特别处理状态 | 生效日期 | 失效日期 | 当前标志
600240.SH | 正常 | 2022-01-01 | 2023-04-30 | N
600240.SH | *ST | 2023-05-01 | 2024-06-30 | N
600240.SH | 正常 | 2024-07-01 | 9999-12-31 | Y
回测中,2023年5月之前该股票参与选股,5月之后被自动过滤------与实盘行为完全一致。
案例:指数成分股调整
指数成分维度表(SCD Type 2):
指数代码 | 证券代码 | 权重(%) | 生效日期 | 失效日期 | 当前标志
000300.SH | 601012.SH | 1.25 | 2023-06-12 | 2023-12-10 | N
000300.SH | 601012.SH | 1.42 | 2023-12-11 | 9999-12-31 | Y
这对指数增强策略的回测至关重要------成分股调整日前后,权重和约束条件完全不同。如果只用最新快照,回测中的"跟踪误差"会被严重低估。
Type 3:有限历史------上一次就够了
典型场景:因子值的环比变化、行业迁徙分析
Type 3 在原记录上新增一列存储上一次的值,适合只需要知道"上一次是什么"的场景。
行业维度表(SCD Type 3):
证券代码 | 当前行业 | 上一行业 | 变更日期
000002.SZ | 建筑装饰 | 房地产 | 2023-06-15
用途举例:
"从房地产调入建筑装饰的股票,调入后60个交易日的平均超额收益是多少?"
只需要当前行业和上一行业就能回答,不需要完整变更链。在因子研究中,这种**"迁徙效应因子"**是机构量化中常见的另类特征。
Type 4:历史表分离------因子库的工程优化
典型场景:基本面因子(财务数据)的版本管理
财务数据有一个特殊性质:会追溯调整。一家公司4月发布年报,9月发布更正版------两个版本都是"对的",只是后者更准。量化研究员既需要分析"市场当时看到了什么"(用当时版本),也需要用"最终修正版"做长期因子研究。
财务因子主表(当前最新版本):
证券代码 | 报告期 | 净利润(亿) | ROE | 数据状态
600519.SH | 2023-12-31 | 747.34 | 32.5% | 已审计终版
财务因子历史版本表:
证券代码 | 报告期 | 净利润(亿) | ROE | 数据提取日期 | 版本说明
600519.SH | 2023-12-31 | 735.20 | 31.8% | 2024-04-03 | 初步披露版
600519.SH | 2023-12-31 | 747.34 | 32.5% | 2024-09-15 | 审计更正版
- 事件研究(如"年报首次披露后的市场反应")→ 用历史版本表,还原市场当时看到的数据
- 横截面因子回测(如"用最新 ROE 排序选股")→ 用主表,保证因子质量
这是 Type 4 在量化工程中最典型的应用模式。
Type 6:混合模式------因子平台的终极形态
典型场景:大型量化机构因子库同时服务回测、实盘信号、合规审计
Type 6 组合 Type 2(版本链)+ Type 3(上一值)+ 当前值冗余列:
股票维度表(SCD Type 6):
证券代码 | 行业分类 | 生效日期 | 失效日期 | 当前标志 | 当前行业(冗余)
000002.SZ | 房地产 | 2014-01-01 | 2023-06-14 | N | 建筑装饰
000002.SZ | 建筑装饰 | 2023-06-15 | 9999-12-31 | Y | 建筑装饰
- 回测引擎:按生效/失效日期精确关联 → 消除未来函数 ✅
- 实盘信号生成:直接读"当前行业"冗余列 → 低延迟 ✅
- 合规审计:逐行扫描版本链 → 证明"回测没有数据穿越" ✅
对于需要向监管或 LP(有限合伙人)证明"策略回测严谨性"的量化基金,Type 6 是因子数据平台的理想架构。
量化 SCD 设计的三个关键实践
1. 因子字段的 SCD 类型混搭
同一个股票维度表中,不同因子字段用不同的 SCD 类型:
| 因子字段 | SCD 类型 | 理由 |
|---|---|---|
| 证券代码 | Type 0 | 不可变标识 |
| 证券简称 | Type 1 | 修正性变更 |
| 行业分类 | Type 2 | 核心因子,需完整版本链 |
| 指数成分权重 | Type 2 | 调仓日精确对齐 |
| ST 状态 | Type 2 | 策略过滤条件,不能穿越 |
| 上一行业 | Type 3 | 迁徙效应因子 |
| 上市日期 | Type 0 | 新股因子锚点 |
2. 生效/失效时间的精确约定
量化回测对时间精度要求极高,建议约定:
-
行业分类、ST 状态等定期调整类因子:以公告生效日为准(A 股通常为交易日)
-
财务数据类因子:以实际披露日为生效日期(不是报告期!),否则会引入未来函数
-
失效日期统一用
9999-12-31表示当前有效版本-- 回测中查询某交易日的行业归属
SELECT 行业代码
FROM 行业维度表
WHERE 证券代码 = '000002.SZ'
AND 生效日期 <= '2023-06-10' -- 注意:用实际披露日,不是报告期
AND 失效日期 > '2023-06-10'
3. 变更检测的工程实现
量化因子库的 SCD 变更检测通常由以下方式触发:
- 公告日历驱动:跟踪交易所/指数公司的定期公告(如中证指数成分股调整公告)
- 数据源 diff:每日对比 Wind、聚源、Tushare 等数据源的快照,检测字段级变更
- 财务数据版本管理:在每次财报季,按"数据提取日期"建立新的版本行
结语:SCD 是量化回测可信性的底线
量化投研的核心信条是**"回测可复现"。但可复现的前提,不是代码写对了,而是数据在时间维度上的语义是正确的**。
一个没有正确建模 SCD 的因子库,就像一栋地基不稳的楼------策略再漂亮,实盘也会塌。
SCD 在量化中的本质:确保每一个因子值,在回测的每一个时间点上,都等于市场当时实际看到的那一个。
这句话,值得贴在每一个量化研究员和因子工程师的显示器上。