2. 因子库设计原则

因子库是量化投研体系的“数据底盘”。设计阶段若架构、模型、存储三者脱节,后续因子计算、回测、评分接口将频繁出现性能瓶颈与数据不一致。本节从三个维度拆解设计原则:架构分层、数据模型、存储与索引。

2.1 架构设计:分层解耦与读写分离

因子库架构采用三层分离模式,将原始数据、因子计算、因子服务解耦,避免单点故障与计算风暴。

核心原则:写入路径与读取路径物理隔离;因子计算层无状态,支持水平扩展。
因子库三层架构图 因子库三层架构 原始数据层(Raw Data Layer) 行情快照 · 财务数据 · 另类数据 · 事件数据 因子计算层(Factor Compute Layer) 因子表达式引擎 · 分布式计算 · 因子衍生与标准化 因子服务层(Factor Service Layer) 因子查询API · 评分接口 · 缓存加速 · 权限管控 读写分离 · 无状态计算
架构要点:原始数据层只做清洗与对齐,不做因子计算;因子计算层输出标准化因子值后写入服务层;服务层通过缓存(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),便于数据生命周期管理与过期清理。
因子存储与索引流程 因子存储与索引流程 因子计算输出 热存储(Redis) 温存储(ClickHouse) 冷存储(HDFS) 索引构建(主键+二级) 评分接口查询 数据按热度自动降冷,索引覆盖查询路径
避坑提示:不要对所有因子列都建索引。因子数量超过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个),窄表用于实验因子。评分接口优先查询宽表,若因子不在宽表中,则回退到窄表并缓存结果。