4. 风险因子数据模型:因子表设计
风险因子数据模型是组合风险归因的基石。因子表的设计质量直接决定归因计算的准确性、查询性能与可扩展性。本节从市场因子、行业因子、风格因子三个维度展开表结构设计,并讨论时间序列存储与索引优化策略。
4.1 因子分类与存储粒度
因子按驱动来源分为三类,每类在表中以不同粒度存储:
| 因子类型 | 典型因子 | 存储粒度 | 更新频率 |
|---|---|---|---|
| 市场因子 | 大盘收益率、波动率、利率、信用利差 | 日频 / 单条 | 每日收盘后 |
| 行业因子 | 申万一级行业、GICS 行业收益率 | 日频 / 行业 | 每日收盘后 |
| 风格因子 | 市值、估值、动量、波动、成长 | 日频 / 个股 | 每日收盘后 |
设计原则:因子表采用“宽表 + 窄表”混合模式。市场因子与行业因子使用窄表(因子名 + 值),风格因子使用宽表(每列一个因子),以平衡查询灵活性与存储效率。
4.2 市场因子表设计
市场因子表存储全局性、不可分散的宏观因子。采用窄表结构,便于新增因子而不改表结构。
CREATE TABLE factor_market (
trade_date DATE NOT NULL,
factor_name VARCHAR(50) NOT NULL, -- 因子名称,如 'mkt_ret', 'vol_30d', 'yield_10y'
factor_value DOUBLE NOT NULL,
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (trade_date, factor_name)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
关键点:主键为 (trade_date, factor_name),确保每个交易日每个因子只有一条记录。查询某日所有市场因子时,只需按 trade_date 过滤即可。
4.3 行业因子表设计
行业因子表记录各行业指数的日收益率。同样采用窄表,支持多行业分类标准。
CREATE TABLE factor_industry (
trade_date DATE NOT NULL,
industry_code VARCHAR(20) NOT NULL, -- 行业代码,如 'SW_银行', 'GICS_4510'
industry_name VARCHAR(100), -- 行业名称,冗余存储便于展示
factor_value DOUBLE NOT NULL, -- 行业日收益率
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (trade_date, industry_code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
注意:行业分类标准可能随时间调整(如申万行业分类每两年修订一次)。建议在 industry_code 中嵌入版本标识,或在关联表中维护行业分类版本映射。
4.4 风格因子表设计
风格因子表存储个股层面的因子暴露。采用宽表结构,每列一个因子,便于批量读取和矩阵运算。
CREATE TABLE factor_style (
trade_date DATE NOT NULL,
stock_code VARCHAR(10) NOT NULL,
size_factor DOUBLE, -- 市值因子
value_factor DOUBLE, -- 估值因子
momentum_6m DOUBLE, -- 6个月动量因子
volatility_1y DOUBLE, -- 1年波动因子
growth_factor DOUBLE, -- 成长因子
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (trade_date, stock_code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
宽表优势:归因计算时通常需要同时读取多个风格因子,宽表可避免多次 JOIN,显著提升计算性能。若因子数量超过 20 个,可考虑按因子类别拆分为多张宽表。
4.5 时间序列数据存储策略
因子数据是典型的时间序列数据,存储策略需兼顾写入性能与查询效率:
- 分区策略:按年/月对表进行 RANGE 分区,便于历史数据归档与清理。
- 压缩策略:对历史分区启用 InnoDB 页压缩(page_compression=1),减少存储成本。
- 写入策略:使用批量 INSERT … ON DUPLICATE KEY UPDATE 避免重复写入。
-- 按月分区示例(市场因子表)
ALTER TABLE factor_market
PARTITION BY RANGE (YEAR(trade_date) * 100 + MONTH(trade_date)) (
PARTITION p202401 VALUES LESS THAN (202402),
PARTITION p202402 VALUES LESS THAN (202403),
PARTITION p202403 VALUES LESS THAN (202404),
PARTITION p_future VALUES LESS THAN MAXVALUE
);
4.6 主键与索引优化
因子表的查询模式以时间范围扫描和因子名/代码过滤为主,索引设计需围绕这些模式:
| 表名 | 主键 | 辅助索引 | 适用查询场景 |
|---|---|---|---|
| factor_market | (trade_date, factor_name) | factor_name | 查询某因子历史序列 |
| factor_industry | (trade_date, industry_code) | industry_code | 查询某行业历史序列 |
| factor_style | (trade_date, stock_code) | stock_code | 查询某股票历史因子暴露 |
索引优化要点:
- 主键本身就是聚簇索引,按时间+实体排序,天然支持时间范围扫描。
- 辅助索引使用覆盖索引(如 factor_market 上建 (factor_name, trade_date, factor_value)),避免回表。
- 对 style 表,若经常按 stock_code 查询历史,可建 (stock_code, trade_date) 索引。
- 避免在 factor_value 等连续值列上建索引,选择性低且更新开销大。
4.7 因子数据模型架构总览
下图展示三类因子表在风险归因系统中的位置与关联关系:
4.8 因子表设计检查清单
在实际建模时,建议逐项核对以下要点:
- 时间对齐:所有因子表使用同一 trade_date 粒度,确保归因时无需跨表时间转换。
- 主键唯一性:每个交易日每个实体(因子名/行业/股票)只有一条记录,避免重复数据导致归因偏差。
- 索引覆盖:高频查询(如某因子历史序列)使用覆盖索引,减少磁盘 I/O。
- 分区管理:按时间分区,便于历史数据归档与 TTL 清理。
- 因子标准化:风格因子建议在入库前做 Z-score 或分位数标准化,保持量纲一致。
- 元数据管理:建立因子字典表,记录因子名称、计算方式、数据来源、更新时间,便于审计与追溯。
常见陷阱:不要将市场因子、行业因子、风格因子混入同一张表。虽然简化了表结构,但会导致查询性能下降、索引设计困难、数据冗余增加。三类因子在更新频率、存储粒度、使用场景上差异明显,应保持独立。