Caloogy 文章
English

Agent 挖因子:自动化的因子工程与过拟合防线

日期
2025 年 10 月 12 日
系列
量化 Agent
阅读
4 分钟
主题
Ai agentQuantFactor miningOverfitting

一句话版本

让大模型 Agent 自动挖因子很有吸引力:它能读论文、提出假设、写代码、跑回测,一夜之间尝试上千个想法。但这也把量化研究最古老的问题放大了一千倍:试得越多,越容易在噪声里找到"显著"的结果(见《多重检验与因子动物园》)。 本文用一个模拟实验说明:在 2000 个候选因子中只藏了一个真因子,按常规的 t > 2 标准,会有 60 个候选"通过",其中 59 个是假的。解决办法不是让 Agent 少尝试,而是建立不可绕过的防线:锁起来的样本外"保险箱"、按尝试次数提高的门槛、经济逻辑审查、以及完整的尝试记录。

免责

本文仅为技术科普,不构成任何投资建议。文中代码使用模拟数据。


第一层:通俗理解

1.1 一个不知疲倦的研究员

想象你雇了一个研究员,他:

第二天早上,他交给你 60 份"显著有效"的因子报告。你该相信多少?

1.2 Agent 挖因子的典型流程

是

否

读论文 / 数据描述

提出因子假设

写因子代码

回测、算 IC

显著?

写报告

这个循环本身就是一台多重检验机器:那个"否 → 回到提出假设"的箭头,每转一圈,就多一次在同一份数据上的尝试。


第二层:模拟实验

2.1 设置

import numpy as np, pandas as pd
rng = np.random.default_rng(99)
T, N, K = 120, 300, 2000          # 120 个月、300 只股票、Agent 生成 2000 个候选因子
fwd = rng.normal(size=(T, N))     # 未来收益
true_factor = rng.normal(size=(T, N)); fwd += 0.05 * true_factor   # 只藏了一个真因子

def mean_ic(F, R):
    Fz = (F - F.mean(1, keepdims=True)) / F.std(1, keepdims=True)
    Rz = (R - R.mean(1, keepdims=True)) / R.std(1, keepdims=True)
    ic = (Fz * Rz).mean(1)
    return ic.mean(), ic.mean() / ic.std() * np.sqrt(len(ic))   # IC 均值与 t 值

split = 84   # 前 7 年给 Agent"研究",后 3 年锁进保险箱
res = []
for k in range(K):
    F = true_factor if k == 0 else rng.normal(size=(T, N))
    ic_is, t_is = mean_ic(F[:split], fwd[:split])
    ic_oos, t_oos = mean_ic(F[split:], fwd[split:])
    res.append((k, ic_is, t_is, ic_oos, t_oos))
df = pd.DataFrame(res, columns=["k", "ic_is", "t_is", "ic_oos", "t_oos"])

passed = df[df["t_is"] > 2]
print("样本内 t>2 的候选数:", len(passed), "(其中真因子:", int((passed['k'] == 0).sum()), "个)")
print("这些候选的样本外 IC 均值:", round(passed["ic_oos"].mean(), 4))
print("样本外仍 t>2 的:", int((passed["t_oos"] > 2).sum()), "个;真因子样本内 t =",
      round(df.loc[0, "t_is"], 1), " 样本外 t =", round(df.loc[0, "t_oos"], 1))
strict = df[df["t_is"] > 3.0]
print("用 t>3 门槛:", len(strict), "个通过,其中真因子", int((strict['k'] == 0).sum()), "个")

输出:

样本内 t>2 的候选数: 61 (其中真因子: 1 个)
这些候选的样本外 IC 均值: 0.0014
样本外仍 t>2 的: 3 个;真因子样本内 t = 7.4  样本外 t = 5.7
用 t>3 门槛: 7 个通过,其中真因子 1 个

2.2 解读

环节 结果
常规门槛 t > 2 61 个通过,60 个是假的(单侧检验下约 2.3% 的噪声会通过,2000 × 2.3% ≈ 46,加上随机波动)
这 61 个的样本外 IC 平均约 0.001,基本为零
保险箱(样本外)检验 只剩 3 个,真因子在其中,样本外 t = 5.7
提高门槛 t > 3 7 个通过,假的大幅减少

两条防线组合起来(提高门槛 + 样本外保险箱),能把假发现压到很少。但要注意:样本外"t > 2"也有约 2% 的噪声能碰巧通过,所以那 3 个里仍有 2 个是假的。


第三层:防线设计

3.1 样本外保险箱

规则 说明
物理隔离 保险箱数据不放在 Agent 能访问的环境里,Agent 的工具无法读取
一次性 每个候选因子只能被打开检验一次
批量开箱 不是每个因子单独开箱,而是一批候选一起检验,并对这批的数量做多重检验校正
定期更换 随着时间推移,新的数据进入保险箱;旧的保险箱数据被"用过"之后降级为研究数据

如果 Agent 能看到保险箱的结果再回去修改因子,保险箱就变成了样本内。这一点必须靠系统权限而不是靠 Agent 自律来保证(见《Agent交易系统的安全边界》、Agent权限设计)。

3.2 按尝试次数提高门槛

3.3 经济逻辑审查

纯粹从数据中挖出来、没有合理解释的因子,更可能是噪声。要求每个候选因子回答:

问题 说明
为什么有效? 风险补偿还是行为偏差?谁在亏钱给你?
为什么还没被套利掉? 有什么限制让别人无法利用它?
在哪里应该无效? 如果逻辑成立,在什么市场、什么时期它应该失效?去检验这个预测

第三个问题特别有力:一个好的解释能做出可证伪的预测。这和因果分析减少金融幻觉中"负对照"的思路一致。

3.4 其他防线

防线 说明
与已知因子的相关性 新因子如果只是市值、动量、行业的组合,就不是新发现
跨市场、跨时期检验 在 A 股发现的因子,在美股、港股是否也有效
参数稳定性 参数稍微变一点,效果是否还在(见《回测的七宗罪》)
扣除成本 用真实的成本模型重新评估(见《交易成本建模》)
多样性约束 防止 Agent 反复生成同一个想法的微小变体(它们本质上是一次尝试的多次计数,但会让门槛估算失真)
人工复核 最终入库前由人审阅代码和逻辑

3.5 Agent 的真正价值

在这些防线之下,Agent 挖因子仍然很有价值,但价值不在于"找到更多显著的因子",而在于:


总结

层次 要点
风险 Agent 是一台多重检验机器;2000 次尝试中,t > 2 会让约 60 个噪声"通过"
保险箱 样本外数据物理隔离、只开一次、批量校正
门槛 系统记录尝试次数,自动提高门槛
审查 经济逻辑、可证伪预测、增量信息、成本、人工复核