因子计算框架:从逻辑到性能的实战拆解
因子计算,说白了就是把原始行情数据变成有预测能力的信号。这个环节做得好不好,直接决定了你后面所有工作的质量。我见过太多人花大把时间调模型,结果回头一看,因子计算那步就埋了雷。
今天咱们就聊聊因子计算的核心逻辑,以及怎么用向量化计算把性能拉满。
因子计算的核心逻辑
因子计算本质上就三步:输入数据 → 计算逻辑 → 输出因子值。听起来简单,但实际落地时坑不少。
我个人习惯把因子计算拆成三个层次来看:
- 数据层:原始行情、财务数据、另类数据等,需要做清洗和对齐
- 逻辑层:具体的计算公式,比如动量、反转、波动率等
- 执行层:用什么工具、什么方式去算,这直接影响效率
举个例子,一个简单的动量因子:
# 伪代码逻辑
动量因子 = (当前收盘价 - N天前收盘价) / N天前收盘价
这个逻辑看着简单,但放到全市场几千只股票、每天几万条数据上,怎么算得快、算得准,就是门学问了。
核心要点:因子计算不是写公式,而是设计一套可复用的计算框架。好的框架能让你快速迭代、批量计算、避免重复劳动。
向量化计算 vs 循环计算:性能差距有多大?
我记得刚入行那会儿,写因子计算全用 for 循环。跑一个简单的均线因子,3000只股票、500个交易日,愣是跑了快10分钟。后来换成向量化计算,同样的逻辑,3秒搞定。
为什么会这样?
Python 的 for 循环是解释执行的,每循环一次都要做类型检查、内存分配。而 NumPy 和 Pandas 的向量化操作底层是 C 语言实现的,一次操作处理整个数组,省掉了大量 Python 层面的开销。
咱们直接看对比:
| 计算方式 | 代码量 | 可读性 | 执行速度(100万行数据) |
|---|---|---|---|
| 纯 Python 循环 | 少 | 差 | ~8-12秒 |
| 列表推导式 | 中等 | 中等 | ~5-8秒 |
| Pandas 向量化 | 少 | 好 | ~0.3-0.8秒 |
| NumPy 向量化 | 中等 | 好 | ~0.1-0.3秒 |
我曾经在一个项目中,用循环计算了50多个因子,每次回测光计算就要等半小时。改成向量化后,整个因子库的更新压缩到2分钟以内。嗯,那感觉就像从绿皮火车换成了高铁。
避坑指南:我曾经在计算滚动窗口统计量时,不小心在循环里重复创建 DataFrame,导致内存暴涨。向量化虽然快,但也要注意中间变量的内存管理,尤其是处理全市场数据时。
用 Pandas 和 NumPy 进行因子计算
实际工作中,我主要用 Pandas 做数据操作,NumPy 做数值计算。两者配合起来,基本能覆盖90%的因子计算场景。
Pandas 常用操作
groupby+apply:按股票分组计算rolling/expanding:滚动窗口和扩展窗口shift:数据对齐,避免未来信息pct_change:收益率计算
举个例子,计算每个股票的20日滚动波动率:
import pandas as pd
import numpy as np
# 假设 df 是 MultiIndex 格式,索引为 (date, stock)
# 列包含 close 收盘价
# 计算日收益率
df['ret'] = df.groupby('stock')['close'].pct_change()
# 计算20日滚动波动率(年化)
df['vol_20d'] = df.groupby('stock')['ret'].rolling(20).std() * np.sqrt(252)
# 重置索引
df = df.reset_index(level='stock', drop=True)
你看,三行代码就搞定了。如果用循环写,至少得十几行,还容易出错。
NumPy 加速技巧
有些计算 Pandas 做起来比较慢,比如复杂的矩阵运算、自定义函数等。这时候我会把数据转成 NumPy 数组来处理。
# 假设需要计算一个自定义的加权移动平均
def weighted_ma(arr, window=20):
weights = np.exp(np.linspace(-1, 0, window))
weights /= weights.sum()
result = np.full_like(arr, np.nan)
for i in range(window-1, len(arr)):
result[i] = np.dot(arr[i-window+1:i+1], weights)
return result
# 应用到每个股票
df['wma_20'] = df.groupby('stock')['close'].transform(
lambda x: weighted_ma(x.values, 20)
)
这里的关键是 .values,它把 Pandas Series 转成 NumPy 数组,计算效率能提升一个数量级。
小技巧:我习惯在因子计算前,先把数据按日期排序,然后用 groupby 分组。这样能保证每个股票内部的时间序列是连续的,避免计算滚动统计量时出现错位。
因子计算框架的整体流程
下面这张图是我自己总结的因子计算框架,你可以参考一下:
这个流程里,最关键的决策点就是「向量化计算 vs 循环计算」。我的建议是:能用向量化就别用循环。只有在逻辑特别复杂、无法向量化的时候,才考虑用循环,而且一定要用 numba 或 cython 加速。
实战中的几个坑
最后分享几个我踩过的坑,希望能帮你少走弯路:
- 未来信息泄露:计算因子时一定要用
shift对齐,确保只用历史数据。我曾经在计算均线时忘了 shift,结果因子值包含了当天的信息,回测结果漂亮得离谱,实盘直接打脸。 - 分组计算时的索引问题:
groupby之后记得重置索引,否则后续操作容易出错。 - 缺失值处理:因子计算中经常出现 NaN,比如滚动窗口初期、停牌日等。一定要想好怎么处理,是填充、删除还是保留。
- 内存爆炸:全市场数据量很大,中间变量不要随意复制。能用
inplace=True就用,能删的临时变量就删。
总结一下:因子计算框架的核心就是「数据清洗 → 向量化计算 → 结果输出」。把这三步做好,你的因子库就有了坚实的基础。至于性能,记住一句话:能用 NumPy 就别用 Pandas,能用向量化就别用循环。