2. 因子库设计原则
因子库是量化投研体系的“数据底盘”。设计阶段若架构、模型、存储三者脱节,后续因子计算、回测、评分接口将频繁出现性能瓶颈与数据不一致。本节从三个维度拆解设计原则:架构分层、数据模型、存储与索引。
2.1 架构设计:分层解耦与读写分离
因子库架构采用三层分离模式,将原始数据、因子计算、因子服务解耦,避免单点故障与计算风暴。
核心原则:写入路径与读取路径物理隔离;因子计算层无状态,支持水平扩展。
架构要点:原始数据层只做清洗与对齐,不做因子计算;因子计算层输出标准化因子值后写入服务层;服务层通过缓存(Redis)与列存(Parquet)加速查询,评分接口直接读取服务层快照。
2.2 数据模型设计:宽表 vs 窄表
因子数据模型需平衡写入效率与查询灵活性。主流方案为宽表模型与窄表模型的混合使用。
| 维度 | 宽表模型(Wide Table) | 窄表模型(Narrow Table) |
|---|---|---|
| 存储结构 | 每行一个股票×日期,每列一个因子 | 每行一个股票×日期×因子ID,因子值在value列 |
| 查询性能 | 多因子同时查询极快(一次IO) | 多因子查询需多次JOIN或过滤 |
| 扩展性 | 新增因子需加列,DDL成本高 | 新增因子直接插入新行,无DDL |
| 存储冗余 | 低(无重复因子ID) | 高(因子ID重复存储) |
| 适用场景 | 因子数量稳定(< 200个),高频查询 | 因子数量动态增长,低频全量扫描 |
推荐实践:核心因子(动量、价值、质量等)使用宽表存储,保证评分接口毫秒级响应;实验性因子使用窄表存储,降低运维成本。两者通过统一的因子元数据表(factor_meta)关联。
2.3 因子存储与索引策略
存储选型直接影响因子库的吞吐与延迟。结合量化场景的时间序列密集写入与截面查询特点,推荐分层存储方案。
2.3.1 存储引擎选型
| 存储类型 | 引擎 | 优势 | 劣势 |
|---|---|---|---|
| 热存储 | Redis + Parquet | 微秒级读取,支持TTL自动过期 | 容量有限,不适合全量历史 |
| 温存储 | ClickHouse / DolphinDB | 列存压缩,时序聚合查询极快 | 运维复杂度较高 |
| 冷存储 | HDFS / S3 + Parquet | 成本低,容量无限 | 查询延迟秒级 |
2.3.2 索引策略
- 主索引: (trade_date, stock_code) 复合主键,覆盖99%的截面查询。
- 二级索引: 因子ID + trade_date,支持按因子维度快速过滤。
- 物化视图: 对高频评分接口预计算因子排名、分位数等聚合结果,避免实时计算。
- 分区策略: 按年月分区(如 factor_202501),便于数据生命周期管理与过期清理。
避坑提示:不要对所有因子列都建索引。因子数量超过100时,全列索引会导致写入性能急剧下降。只对 trade_date、stock_code、factor_id 三个核心字段建索引,其余因子值通过列存压缩与向量化计算加速。
2.4 数据模型代码示例
以下为因子宽表与窄表的建表SQL示例(基于ClickHouse语法),展示实际落地时的字段设计。
-- 宽表模型:每列一个因子
CREATE TABLE factor_wide (
trade_date Date,
stock_code String,
factor_momentum Float64,
factor_value Float64,
factor_quality Float64,
factor_size Float64,
factor_liquidity Float64,
update_time DateTime DEFAULT now()
) ENGINE = MergeTree()
PARTITION BY toYYYYMM(trade_date)
ORDER BY (trade_date, stock_code);
-- 窄表模型:因子ID + 值
CREATE TABLE factor_narrow (
trade_date Date,
stock_code String,
factor_id String,
factor_value Float64,
update_time DateTime DEFAULT now()
) ENGINE = MergeTree()
PARTITION BY toYYYYMM(trade_date)
ORDER BY (trade_date, stock_code, factor_id);
实践建议:宽表用于核心因子(不超过50个),窄表用于实验因子。评分接口优先查询宽表,若因子不在宽表中,则回退到窄表并缓存结果。