因子公式系统
这是一篇关于量化因子系统的技术笔记。我们的公式引擎从最开始的手写 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 后端 → 编译为矢量运算表达式经验教训
- 窗口函数性能:PG 的窗口函数在 5000+ 股票上跑 100+ 因子,合理索引下 3s 内完成
- 保值性:Python 端浮点精度与 SQL 端不一致会导致因子微小偏差,统一用
NUMERIC(20,6)解决 - 缓存是银弹:中间计算结果缓存(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。