量化因子挖掘引擎:从Alpha158到CogAlpha LLM启发式挖掘的完整实战
为什么需要因子挖掘引擎
传统量化研究的瓶颈在于因子发现依赖人力。一个有经验的量化研究员,一年能手工构造20-50个因子,其中有效的可能只有3-5个。而CogAlpha论文(arXiv:2511.18850)提出了一种革命性的方法:让LLM自动编写因子代码,用遗传算法驱动因子进化。
本文分享我们基于CogAlpha论文搭建的因子挖掘引擎——从Alpha158精简因子库到LLM驱动的7级Agent层次进化系统,覆盖5000+只A股和988只可转债。
架构全景
整个因子挖掘引擎分为三层:
┌─────────────────────────────────────────────────┐
│ 因子挖掘引擎架构 │
├─────────────────────────────────────────────────┤
│ │
│ Layer 1: 经典因子层 │
│ ├── Alpha158精简版 (20个量价因子) │
│ ├── 可转债5维因子 (技术/信用/正股/组合/定价) │
│ └── 股票30+因子 (动量/波动/成交量/形态) │
│ │
│ Layer 2: LLM因子生成层 │
│ ├── 7级Agent层次 (21个领域专家Agent) │
│ ├── 遗传进化 (Mutation + Crossover) │
│ ├── 多样化引导 (5种释义模式) │
│ └── Multi-Agent质量检查器 │
│ │
│ Layer 3: 五维评估层 │
│ ├── IC (Pearson相关性) │
│ ├── RankIC (Spearman秩相关) │
│ ├── ICIR (IC信息比率) │
│ ├── RankICIR (秩IC信息比率) │
│ └── MI (互信息) │
│ │
└─────────────────────────────────────────────────┘
第一层:经典因子库
Alpha158精简版
微软Qlib的Alpha158因子库是业界标准,包含158个量价因子。但完整安装Qlib依赖复杂,我们从Alpha158中提取了最相关的20个因子,直接用pandas计算:
def calc_factors(df: pd.DataFrame) -> pd.DataFrame:
"""从日频OHLCV计算Alpha158精选因子"""
f = pd.DataFrame(index=df.index)
c, o, h, l, v = df['close'], df['open'], df['high'], df['low'], df['vol']
# ---- 动量类 ----
ema12 = c.ewm(span=12, adjust=False).mean()
ema26 = c.ewm(span=26, adjust=False).mean()
dif = ema12 - ema26
dea = dif.ewm(span=9, adjust=False).mean()
f['macd'] = dif - dea
delta = c.diff()
gain = delta.where(delta > 0, 0).rolling(14).mean()
loss = (-delta.where(delta < 0, 0)).rolling(14).mean()
rs = gain / loss.replace(0, np.nan)
f['rsi_14'] = 100 - 100 / (1 + rs)
f['roc_5'] = c.pct_change(5)
f['roc_10'] = c.pct_change(10)
f['roc_20'] = c.pct_change(20)
# ---- 波动率类 ----
ret = c.pct_change()
f['volatility_5'] = ret.rolling(5).std() * np.sqrt(252)
f['volatility_10'] = ret.rolling(10).std() * np.sqrt(252)
f['volatility_20'] = ret.rolling(20).std() * np.sqrt(252)
# ATR
tr = pd.concat([
h - l,
(h - c.shift(1)).abs(),
(l - c.shift(1)).abs()
], axis=1).max(axis=1)
f['atr_14'] = tr.rolling(14).mean()
f['atr_ratio'] = f['atr_14'] / c
# ---- 成交量类 ----
f['vol_ma_5'] = v.rolling(5).mean()
f['vol_ma_20'] = v.rolling(20).mean()
f['vol_ratio'] = f['vol_ma_5'] / f['vol_ma_20'].replace(0, np.nan)
# OBV
obv_dir = np.sign(c.diff())
f['obv'] = (obv_dir * v).cumsum()
# 量价相关
f['vp_corr_10'] = c.rolling(10).corr(v)
# ---- 形态类 ----
f['upper_shadow'] = (h - pd.concat([o, c], axis=1).max(axis=1)) / (h - l + 0.001)
f['lower_shadow'] = (pd.concat([o, c], axis=1).min(axis=1) - l) / (h - l + 0.001)
return f
可转债五维因子体系
可转债因子比股票复杂——它同时具有债性和股性。我们设计了5大类36个因子:
| 因子类别 | 因子数量 | 代表因子 | 核心逻辑 |
|---|---|---|---|
| 技术面 | 13个 | 价格位置、波动率CV、网格潜力 | 从OHLCV提取的量价信号 |
| 信用面 | 7个 | 转股溢价率、纯债价值、债底距离 | 可转债特有的定价因子 |
| 正股因子 | 10个 | 正股动量、正股波动率、CB对正股Beta | 正股驱动的转债信号 |
| 组合因子 | 5个 | 低溢价高动量、深度价值、波动率套利 | 多因子合成 |
| 定价因子 | 1个 | BS错误定价 | Black-Scholes模型定价偏差 |
其中BS错误定价因子特别有趣——用Black-Scholes期权定价模型计算可转债内含期权部分的”理论价值”,与市场价格比较,发现系统性低估/高估:
from bs_pricing_factor import compute_bs_mispricing, load_maturity_cache
# BS定价因子:正=市场低估,负=市场高估
# 利用可转债的期权属性,用BS模型反推隐含波动率,
# 与历史波动率比较,发现定价偏差
第二层:LLM驱动的因子进化
这是整个引擎的核心创新——让LLM扮演量化研究员,自动提出因子假设、编写因子代码、接受评估反馈、然后改进。
7级Agent层次
借鉴CogAlpha论文Section 3.1,我们定义了7个层次21个领域专家Agent:
| 层级 | 名称 | Agent数量 | 探索方向 |
|---|---|---|---|
| Level I | 市场结构与周期 | 2 | 长周期相位转移、波动率状态切换 |
| Level II | 极端风险与脆弱性 | 2 | 尾部风险暴露、崩溃早期预警 |
| Level III | 量价动力学 | 4 | 流动性、订单不平衡、量价一致性 |
| Level IV | 价格-波动行为 | 3 | 波动率曲面、GARCH效应 |
| Level V | 多尺度特征 | 4 | 跨时间尺度因子(微观/中观/宏观) |
| Level VI | 稳定性与持续性 | 3 | 因子衰减速度、信号持续性 |
| Level VII | 几何与拓扑融合 | 3 | 非线性组合、曲面几何特征 |
每个Agent都有专属的Prompt模板:
SEVEN_LEVEL_HIERARCHY = {
1: {
"name": "Level I: 市场结构与周期",
"agents": [
{"id": "AgentMarketCycle", "description": "长周期相位转移与结构性拐点"},
{"id": "AgentVolatilityRegime", "description": "平静与波动状态切换检测"},
],
"prompt_template": """你是一个量化因子发现Agent,专注于{level_name}。
你的探索方向: {agent_desc}
从5分钟OHLCV数据中,设计一个可计算的可转债因子函数。
因子的核心逻辑应围绕{agent_desc}。
函数接收DataFrame(df)包含: datetime, open, high, low, close, volume。
函数返回一个pandas Series(因子值序列)。
请输出:
1. 因子名称和公式
2. 完整的Python函数代码
3. 因子逻辑的经济学/金融学解释
4. 预期预测方向(正/负)及原因"""
},
# ... Level II ~ VII 类似
}
遗传进化流程
初始Alpha池 (80个候选)
│
▼
┌─────────┐
│ 五维评估 │ ─── IC / RankIC / ICIR / RankICIR / MI
└────┬────┘
│
▼
┌─────────┐
│ 分级筛选 │ ─── Qualified (65th percentile)
│ │ ─── Elite (80th percentile, Top2保留到下一代)
└────┬────┘
│
▼
┌─────────┐
│ 遗传变异 │ ─── Mutation: LLM对单个因子改写
│ │ ─── Crossover: LLM对两个因子交叉融合
└────┬────┘
│
▼
┌─────────┐
│ 多样化引导 │ ─── light / moderate / creative / divergent / concrete
└────┬────┘
│
▼
新一代Alpha池 → 进入下一轮进化
关键配置参数:
COGALPHA_CONFIG = {
"qualified_percentile": 65, # Qualified ≥ 65th percentile
"elite_percentile": 80, # Elite ≥ 80th percentile
"ic_min": 0.005, # IC最小绝对值
"icir_min": 0.05, # ICIR最小值
"elite_carryforward": 2, # 每代保留Top2精英
"parent_pool_size": 32, # 父代池容量
"generations": 24, # 进化代数
"diversified_modes": ["light", "moderate", "creative", "divergent", "concrete"],
"generation_temps": [0.7, 0.8, 0.9, 1.0, 1.1, 1.2],
}
LLM生成因子代码
我们用DeepSeek/GLM API让LLM直接生成可执行的Python因子代码:
def create_llm_generator():
"""创建LLM因子生成器"""
creds = get_credentials() # 优先DeepSeek, fallback GLM
client = OpenAI(api_key=creds["api_key"], base_url=creds["base_url"])
def generate(prompt: str) -> Optional[str]:
resp = client.chat.completions.create(
model=creds["model"],
messages=[
{"role": "system", "content": "你是一个专业的量化因子发现Agent。"
"你从OHLCV数据中设计有经济解释的Alpha因子。"},
{"role": "user", "content": prompt},
],
temperature=0.8,
max_tokens=8192,
timeout=180,
)
content = resp.choices[0].message.content
# 提取Python代码块
code_match = re.search(r"```python\n(.*?)```", content, re.DOTALL)
if code_match:
return code_match.group(1).strip()
return content.strip()
return generate
Mutation与Crossover
遗传算法的两大操作:
- Mutation(变异):LLM对单个因子进行改写,比如将简单移动平均替换为指数加权,或加入成交量条件
- Crossover(交叉):LLM将两个因子的逻辑融合,比如将波动率因子与动量因子结合
每代进化时,有效因子和无效因子的分析摘要会注入到下一代的生成Prompt中,让LLM”学习”什么方向有效:
# 自适应学习:将历史经验注入Prompt
effective_summary = f"""
基于前{generation}代进化结果:
- 有效方向: {top_factors} (IC>0.03)
- 无效方向: {failed_factors} (IC<0.01)
- 共性特征: {common_patterns}
请在新的方向上探索,避免重复无效的因子构造方式。
"""
第三层:五维评估体系
传统因子评估只看IC(信息系数),CogAlpha提出五维评估:
def compute_five_metrics(factor_values, returns) -> dict:
"""CogAlpha五维评估: IC, RankIC, ICIR, RankICIR, MI"""
from scipy.stats import spearmanr, pearsonr
vals = np.asarray(factor_values, dtype=float)
rets = np.asarray(returns, dtype=float)
mask = ~(np.isnan(vals) | np.isnan(rets))
vals, rets = vals[mask], rets[mask]
if len(vals) < 10:
return {"ic": 0, "rankic": 0, "icir": 0, "rankicir": 0, "mi": 0}
# 1. IC: Pearson线性相关
ic, _ = pearsonr(vals, rets)
# 2. RankIC: Spearman秩相关(对异常值更鲁棒)
rankic, _ = spearmanr(vals, rets)
# 3 & 4. ICIR / RankICIR: 时间序列上的稳定性
# (需要按日期分组计算每日IC的均值/标准差)
# 5. MI: 互信息(捕捉非线性关系)
from sklearn.feature_selection import mutual_info_regression
mi = mutual_info_regression(
vals.reshape(-1, 1), rets,
random_state=42, n_neighbors=3
)[0]
return {"ic": ic, "rankic": rankic, "icir": icir,
"rankicir": rankicir, "mi": mi}
| 指标 | 计算方式 | 捕捉的关系 | 阈值 |
|---|---|---|---|
| IC | Pearson相关 | 线性相关性 | ≥ 0.005 |
| RankIC | Spearman秩相关 | 单调关系(对异常值鲁棒) | ≥ 0.005 |
| ICIR | mean(IC) / std(IC) | IC的时间序列稳定性 | ≥ 0.05 |
| RankICIR | mean(RankIC) / std(RankIC) | RankIC的稳定性 | ≥ 0.05 |
| MI | 互信息 | 非线性关系 | ≥ 0.02 |
分级机制
不是所有因子都能通过——CogAlpha采用百分位分级:
- Qualified(合格):五维指标全部超过65th percentile + 绝对值门槛
- Elite(精英):超过80th percentile + 更高绝对值门槛,Top2精英自动保留到下一代
这个机制确保了因子池的质量随进化代数递增,而不是被平庸因子稀释。
股票因子挖掘:CSI300/CSI500
除了可转债,我们还针对股票市场搭建了独立的因子挖掘引擎:
def load_stock_panel(codes, db_path=DB_PATH, lookback_days=500):
"""从DuckDB加载股票面板数据"""
con = duckdb.connect(db_path, read_only=True)
cutoff = (datetime.now() - timedelta(days=lookback_days)).strftime('%Y-%m-%d')
codes_str = "','".join(codes)
df = con.execute(f"""
SELECT code, date, open, high, low, close, vol as volume, amount
FROM stock_daily
WHERE code IN ('{codes_str}') AND date >= '{cutoff}'
ORDER BY code, date
""").fetchdf()
con.close()
df['ret'] = df.groupby('code')['close'].pct_change()
df['vwap'] = df['amount'] / (df['volume'] * 100 + 1e-10)
return df
评估配置针对股票市场做了调整——前向收益周期从1天到20天,每日最少50只股票才计算截面IC:
EVAL_CONFIG = {
'forward_periods': [1, 3, 5, 10, 20],
'ic_min': 0.005,
'icir_min': 0.05,
'qualified_pct': 65,
'elite_pct': 80,
'min_stocks_per_day': 50,
}
ML因子预测
在因子挖掘的基础上,我们还搭建了ML因子预测Pipeline,用机器学习模型预测哪些因子在当前市场环境下更有效:
MODELS = {
'Ridge': Ridge(alpha=1.0),
'Lasso': Lasso(alpha=0.1),
'RandomForest': RandomForestRegressor(
n_estimators=200, max_depth=5, min_samples_leaf=5, random_state=42
),
'GradientBoosting': GradientBoostingRegressor(
n_estimators=200, max_depth=4, learning_rate=0.05,
min_samples_leaf=5, subsample=0.8, random_state=42
),
'MLP': MLPRegressor(
hidden_layer_sizes=(64, 32, 16), # 类似ResNet的深层结构
activation='relu', solver='adam',
max_iter=1000, early_stopping=True,
validation_fraction=0.2, random_state=42
),
}
使用Walk-Forward验证而非简单train/test split,避免过拟合。核心思想:只用历史数据训练,预测未来。
认知Alpha引擎:LLM情绪分析
除了量价因子,我们还扩展到了文本因子——利用LLM分析新闻和研报的情绪,构建情绪因子:
class SentimentAnalyzer:
def __init__(self):
self.creds = get_credentials()
self.client = OpenAI(
api_key=self.creds['api_key'],
base_url=self.creds.get('base_url')
)
self.model = self.creds.get('model', 'glm-5.1')
def analyze_sentiment(self, text: str) -> float:
"""用LLM分析文本情绪,返回[-1, 1]的情绪分数"""
resp = self.client.chat.completions.create(
model=self.model,
messages=[
{"role": "system", "content": "你是金融文本情绪分析专家。"
"分析给定文本的市场情绪,返回-1到1之间的分数。"
"-1=极度悲观, 0=中性, 1=极度乐观。只返回数字。"},
{"role": "user", "content": text}
],
temperature=0.0,
max_tokens=8
)
score = float(resp.choices[0].message.content.strip())
return max(-1.0, min(1.0, score))
当LLM不可用时,自动降级到关键词情绪分析,确保系统始终可用。
实战效果
因子挖掘结果
在实际运行中,系统从988只可转债的5分钟数据中:
- 初始因子池:80个经典因子(技术面+信用面+正股+组合+定价)
- LLM进化:3代进化,每代生成32个子代因子
- 有效因子:IC ≥ 0.03的因子约占15-20%
- 精英因子:IC ≥ 0.05且ICIR ≥ 0.3约占5%
因子有效性验证
对7个经典因子做8个季度截面的Spearman IC检验:
| 因子 | IC均值 | ICIR | 方向一致性 | 有效性 |
|---|---|---|---|---|
| 非流动性(Amihud) | +0.116 | +1.06 | 88% | ✅ 有效 |
| 5日最大跌幅(反转) | +0.109 | +0.69 | 88% | ✅ 有效 |
| 20日动量(追涨) | -0.082 | -0.92 | 25% | ❌ 反向 |
| 20日波动率 | -0.150 | -0.87 | 25% | ❌ 反向 |
这个结果验证了一个重要结论:可转债市场存在显著的反转效应,而动量效应是反向的(追涨反而亏钱)。
5个实战避坑
1. LLM生成的代码有30%直接不可运行
LLM生成的因子代码中,约30%有语法错误或调用了不存在的pandas方法。解决方案是加入Multi-Agent质量检查器:
# 质量检查流水线
# Step 1: 语法检查 → exec() 能否通过
# Step 2: 逻辑检查 → 输入测试数据,看是否返回有效Series
# Step 3: 修复 → 如果有bug,让另一个LLM尝试修复
# Step 4: 执行评估 → 通过的因子进入五维评估
2. 互信息(MI)计算极慢
sklearn的mutual_info_regression在数据量大时极慢(单次计算>30秒)。建议先用IC/RankIC快速筛选,只对通过初筛的因子计算MI。
3. 温度参数影响因子多样性
temperature=0.7时LLM生成的因子高度雷同,temperature=1.2时因子创意丰富但质量下降。我们的经验是6个温度轮流采样(0.7~1.2),每个温度生成5-10个因子。
4. 精英保留(Elite Carryforward)防止退化
不加精英保留时,进化3代后因子池质量开始下降(优秀因子被平庸因子稀释)。保留Top2精英到下一代是必要的。
5. 前向收益计算容易引入未来函数
计算因子值时用的是当前bar的数据,计算前向收益时用的是未来bar的收益。如果两者使用了同一根bar的close价格,就会引入look-ahead bias。正确做法是因子值用t-1 bar,前向收益从t bar开始计算。
总结
| 维度 | 传统人工 | CogAlpha LLM挖掘 |
|---|---|---|
| 因子发现速度 | 20-50个/年 | 100+个/天 |
| 因子多样性 | 受研究员经验限制 | 7级21个Agent覆盖全维度 |
| 因子质量 | 依赖人工审核 | 五维评估+百分位分级 |
| 迭代速度 | 人工修改→回测→验证 | 遗传进化自动迭代 |
| 成本 | 研究员年薪 | API调用费约10元/代 |
核心启示:LLM不会直接给你赚钱的因子,但它能帮你快速探索因子空间,发现你可能从未想到的因子构造方式。最终决定因子是否有效的,还是五维评估和严格的回测验证。
本文所有代码均来自实际运行的因子挖掘引擎,完整源码可在GitHub仓库查看。
延伸阅读
- DuckDB搭建A股量化数据库教程 — 本地量化数据库实战
- 自建量化回测引擎V2 — 事件驱动回测框架
- Barra因子模型多因子选股实战 — 10因子ICIR合成
- 白马股多因子选股策略 — ROE+增速+估值三因子
- 聚宽组合策略 — 4因子组合Sharpe 1.94
- 国内量化免费数据源全景指南 — 数据架构实战