4、因子计算引擎:单因子计算、多因子并行计算、分布式计算框架选型(Dask/Ray)
因子计算引擎,说白了就是整个因子库的「心脏」。你因子设计得再好,逻辑再严密,跑不出来或者跑得太慢,一切都是白搭。我见过太多团队,因子研究做得风生水起,一到实盘回测就卡在计算环节,三天三夜跑不完一个月的因子数据。嗯,今天我们就来聊聊怎么把这个引擎造得又快又稳。
4.1 单因子计算:基本功要扎实
单因子计算是所有复杂计算的基础。你想想看,如果连一个因子的计算都搞不定,谈什么并行和分布式?
我个人习惯把单因子计算拆成三个步骤:数据准备 → 逻辑运算 → 结果校验。
- 数据准备:确保你拿到的行情数据、财务数据是干净的。我曾经因为一个除权除息没处理好,导致整个因子序列出现跳变,排查了整整两天。
- 逻辑运算:用向量化操作代替循环。这是铁律。在Python里,能用pandas/numpy的向量化函数,就别写for循环。速度差距是几十倍甚至上百倍。
- 结果校验:跑完因子后,一定要看一眼分布、相关性、覆盖率。别等到回测阶段才发现因子是坏的。
举个简单的例子,计算一个「过去5日收益率」因子:
import pandas as pd
import numpy as np
def calc_ret_5d(close: pd.DataFrame) -> pd.DataFrame:
"""
计算过去5个交易日的收益率
"""
# 向量化操作,一行搞定
ret_5d = close.pct_change(periods=5)
# 去掉前5天的NaN值
ret_5d.iloc[:5] = np.nan
return ret_5d
你看,就这么几行。但这里有个坑——pct_change默认是向后填充的,如果你数据里有停牌日,收益率会算错。我建议你提前把停牌日的数据填充为前值,或者用ffill处理一下。
4.2 多因子并行计算:别让CPU闲着
单因子算完了,接下来就是批量生产。假设你有100个因子要同时计算,一个一个跑?太慢了。这时候就要上并行。
多因子并行计算,我把它分成两种场景:
- 因子间并行:不同因子之间没有依赖关系,可以同时算。比如「动量因子」和「波动率因子」互不干扰。
- 因子内并行:同一个因子,但数据量太大(比如全市场5000只股票,10年日频数据),需要把数据切块并行处理。
在Python里,最简单的并行工具就是concurrent.futures。但说实话,对于量化场景,它有点「不够用」。为什么?因为因子计算往往涉及大量内存操作,而concurrent.futures的进程间通信开销很大。
multiprocessing.Pool就够了。超过这个量级,建议直接上分布式框架。
这里给一个简单的因子间并行示例:
from concurrent.futures import ProcessPoolExecutor
import pandas as pd
def compute_factor(factor_name: str, data: pd.DataFrame) -> pd.Series:
"""
根据因子名称调用对应的计算函数
"""
if factor_name == 'ret_5d':
return calc_ret_5d(data)
elif factor_name == 'volatility':
return calc_volatility(data)
# ... 其他因子
# 并行计算所有因子
factor_list = ['ret_5d', 'volatility', 'turnover', 'illiquidity']
with ProcessPoolExecutor(max_workers=4) as executor:
results = list(executor.map(compute_factor, factor_list, [data]*len(factor_list)))
嗯,这里要注意:ProcessPoolExecutor会把数据复制到每个子进程,如果数据太大,内存会爆。我曾经在16GB的机器上跑200个因子,结果直接OOM了。后来改用共享内存才解决。
4.3 分布式计算框架选型:Dask vs Ray
当你的因子库规模达到几百甚至上千个,单机并行已经撑不住了。这时候就需要分布式计算框架。目前业界主流就是两个:Dask 和 Ray。
我两个都用过,说说我的感受。
| 维度 | Dask | Ray |
|---|---|---|
| 学习曲线 | 低(API像pandas) | 中(需要理解Actor模型) |
| 数据处理 | 强(原生支持DataFrame) | 弱(需要自己实现) |
| 任务调度 | 静态图(DAG) | 动态图(更灵活) |
| 内存管理 | 自动(但容易OOM) | 手动(更可控) |
| 适合场景 | 批量数据处理、ETL | 实时计算、强化学习 |
说白了,如果你主要做因子计算和回测,Dask是更自然的选择。因为因子计算本质上就是大规模DataFrame操作,Dask的API几乎和pandas一模一样,迁移成本极低。
而Ray更适合需要复杂状态管理和实时交互的场景。比如你有一个因子需要实时订阅行情并更新,Ray的Actor模型就很好用。
4.4 实战选型建议
说了这么多,到底怎么选?我给出一个简单的决策树:
- 因子数量 < 100,数据量 < 50GB:单机并行就够了,别折腾分布式。
- 因子数量 100-500,数据量 50GB-500GB:上Dask。用它的
dask.dataframe替代pandas,几乎不用改代码。 - 因子数量 > 500,或者需要实时计算:考虑Ray。虽然学习成本高,但灵活性和扩展性更好。
另外,我建议你不要一开始就上分布式。先单机跑通,再并行,最后分布式。每一步都验证结果的一致性。分布式计算最怕的就是「结果对不上」,排查起来非常痛苦。
4.5 核心逻辑流程图
下面这张图展示了因子计算引擎的整体架构,从单因子到分布式,层层递进:
从数据源开始,经过单因子计算、多因子并行,再到分布式框架选型,最终输出因子库。每一步都有对应的技术选型和注意事项。我个人建议你从最左边开始,一步步往右走,不要跳级。
好了,关于因子计算引擎的内容就这些。记住:先单机,再并行,后分布式。每一步都稳扎稳打,你的因子库才能跑得又快又准。