Skip to content

因子公式系统

这是一篇关于量化因子系统的技术笔记。我们的公式引擎从最开始的手写 SQL 演变成了一个完整的 DSL 编译器。

背景

在量化因子研究中,我们经常需要定义各种衍生指标:

MA(close, 20)                 → 20日均线
(zscore(volume) * rank(close, 500)) / MA(amt, 20)
sign(corr(returns, volume, 20)) * clip(MA(close, 5), 0.95, 1.05)

早期我们直接写 PostgreSQL SQL,但问题很多:

  • SQL 写复杂因子需要嵌套 CTE,代码膨胀
  • 因子定义不透明,难评审
  • 回测和实盘要维护两套

DSL 引擎 (v3)

我们的公式编译器基于 Shunting-yard 算法 将中缀表达式转为 RPN,然后根据后端不同生成不同的执行代码。

算子库 (18 个核心算子)

类型算子
统计ma, std, zscore, rank
时序corr, cov, delta, ts_sum
截面rank, group_rank, quantile
变换sign, clip, log, scale
逻辑if, and, or, not

双后端架构

公式字符串


Shunting-yard Parser → RPN Token 流

    ├── SQL 后端 → 编译为 PG 窗口函数查询
    └── NumPy 后端 → 编译为矢量运算表达式

经验教训

  1. 窗口函数性能:PG 的窗口函数在 5000+ 股票上跑 100+ 因子,合理索引下 3s 内完成
  2. 保值性:Python 端浮点精度与 SQL 端不一致会导致因子微小偏差,统一用 NUMERIC(20,6) 解决
  3. 缓存是银弹:中间计算结果缓存(LRU)可以减少 60% 重复计算

代码片段

python
def compile_formula_to_sql(formula: str) -> str:
    """将公式字符串编译为 PostgreSQL SQL"""
    tokens = tokenize(formula)
    rpn = shunting_yard(tokens)
    return generate_sql(rpn)

完整的公式系统代码托管在 Gitea。

基于 VitePress 构建