主题切换
8.1 监督学习选股
概念详解
监督学习是量化选股中最核心的机器学习范式。其基本思路是:利用历史数据中的特征(因子)和标签(未来收益率),训练一个模型来预测每只股票未来的超额收益,然后根据预测结果构建投资组合。
与传统的线性因子模型(如Fama-French)不同,监督学习方法能够自动捕捉因子之间复杂的非线性关系、交互效应和阈值效应,从而可能提取传统方法无法发现的Alpha。
量化选股中常用的监督学习模型包括:
XGBoost / LightGBM 基于梯度提升树的集成学习算法。它们是目前量化选股中最广泛使用的模型,原因包括:(1) 天然处理缺失值和异常值;(2) 捕捉非线性关系和交互效应;(3) 提供特征重要性排序;(4) 训练速度快,调参成本低。在Kaggle金融比赛中,XGBoost/LightGBM长期占据统治地位。
LSTM(长短期记忆网络) 一种循环神经网络变体,擅长处理时间序列数据中的长期依赖关系。在量化选股中,LSTM可以用来捕捉因子在时间维度上的演变模式,例如:质量因子的"动量"(过去几个月质量持续改善的股票表现更优)。
Transformer 基于自注意力机制的深度学习架构。Transformer的attention机制天然适合金融数据中的"cross-sectional"关系——每只股票关注的因子维度不同,attention权重可以自适应地学习这种差异。
TabNet 一种专为表格数据设计的深度学习架构,结合了树模型的稀疏特征选择和神经网络的自表示学习。
监督学习选股的核心挑战不在于模型本身,而在于:(1) 标签的定义(未来1天、5天、20天的收益率?原始收益还是超额收益?);(2) 样本的构建(股票x时间的截面 vs 时序);(3) 数据的非平稳性(市场regime变化导致模型失效);(4) 过拟合的防御。
名词解释:XGBoost
基于梯度提升树的集成学习算法,高效处理表格数据,常用于金融因子模型,提供特征重要性排序。
名词解释:信息系数(IC)
预测排名与未来收益排名之间的秩相关系数(通常用Spearman或Pearson)。IC是衡量选股因子/模型有效性的核心指标。
名词解释:因子正交化
在回归框架中对因子进行相互回归取残差,消除因子间的多重共线性,使每个因子提供独立的信息增量。
数学原理
XGBoost的目标函数
XGBoost的优化目标由损失函数和正则化项组成:
$$\mathcal{L}(\phi) = \sum_{i} l(\hat{y}_i, y_i) + \sum_{k} \Omega(f_k)$$
其中
$$\Omega(f) = \gamma T + \frac{1}{2} \lambda \|w\|^2$$
IC与Rank IC
信息系数衡量的是预测值排名与真实收益率排名的一致性:
$$IC = \text{corr}(\text{rank}(\hat{y}), \text{rank}(y))$$
Rank IC(秩相关系数)在量化选股中比Pearson IC更常用,因为它对异常值不敏感。
分层IC(Stratified IC)按行业、市值分组计算IC,用于评估模型在不同子空间的表现:
$$IC_{sector} = \text{corr}_{i \in sector}(\hat{y}_i, y_i)$$
Python实战
📌 案例1:XGBoost分类
此处有展示代码展开 ▼
python
from xgboost import XGBClassifier
model = XGBClassifier()
model.fit(X_train, y_train)
probs = model.predict_proba(X_test)[:,1]点击展开可浏览运行结果
训练集准确率: 0.929 测试集准确率: 0.753 预测概率范围: [0.064, 0.978] 特征重要性: Feature 1: 0.3995 Feature 2: 0.3012 Feature 3: 0.1234 Feature 4: 0.0757 Feature 5: 0.1002
📌 案例2:完整的监督学习选股流水线
此处有展示代码展开 ▼
python
import numpy as np
import pandas as pd
from xgboost import XGBRegressor
from sklearn.model_selection import TimeSeriesSplit
from sklearn.preprocessing import RobustScaler
from sklearn.metrics import mean_squared_error
def build_stock_selection_pipeline(factor_data, returns, n_splits=5):
"""
完整的监督学习选股流水线
factor_data: DataFrame, index=日期, columns=股票x因子 (MultiIndex)
returns: DataFrame, index=日期, columns=股票, values=未来N日收益率
"""
# 1. 构建特征矩阵和标签
X = factor_data.copy()
y = returns.stack() # 将宽表转为长表
# 2. 处理极端值和缺失值
# 截面中性化(跨股票标准化)
X = X.groupby(level='date').transform(
lambda x: (x - x.median()) / (x.quantile(0.75) - x.quantile(0.25))
)
X = X.clip(-3, 3) # Winsorize
X = X.fillna(0)
# 3. 时序交叉验证(避免前视偏差)
tscv = TimeSeriesSplit(n_splits=n_splits)
dates = sorted(set(X.index.get_level_values('date')))
fold_results = []
feature_importances = []
predictions = []
for fold, (train_dates_idx, test_dates_idx) in enumerate(tscv.split(dates)):
train_dates = [dates[i] for i in train_dates_idx]
test_dates = [dates[i] for i in test_dates_idx]
# 分割训练和测试样本
X_train = X.loc[train_dates]
X_test = X.loc[test_dates]
y_train = y.loc[train_dates]
y_test = y.loc[test_dates]
# 4. 训练模型
model = XGBRegressor(
n_estimators=200,
max_depth=5,
learning_rate=0.05,
subsample=0.8,
colsample_bytree=0.7,
reg_alpha=0.1,
reg_lambda=1.0,
random_state=42,
n_jobs=-1
)
model.fit(X_train, y_train)
# 5. 预测与评估
y_pred = model.predict(X_test)
# 计算Rank IC
from scipy.stats import spearmanr
ic = spearmanr(y_test, y_pred)[0]
# 按日期分别计算IC
daily_ic = []
for date in test_dates:
mask = X_test.index.get_level_values('date') == date
if mask.sum() > 10: # 至少10只股票
ic_d = spearmanr(y_test.loc[date], y_pred[mask])[0]
daily_ic.append(ic_d)
fold_results.append({
'fold': fold,
'IC': ic,
'IC_mean': np.mean(daily_ic),
'IC_std': np.std(daily_ic),
'ICIR': np.mean(daily_ic) / np.std(daily_ic) if np.std(daily_ic) > 0 else 0,
'RMSE': np.sqrt(mean_squared_error(y_test, y_pred)),
'train_period': f"{train_dates[0]} ~ {train_dates[-1]}",
'test_period': f"{test_dates[0]} ~ {test_dates[-1]}"
})
# 6. 特征重要性
importance = pd.Series(
model.feature_importances_,
index=X.columns
).sort_values(ascending=False)
feature_importances.append(importance)
predictions.append(pd.DataFrame({
'y_true': y_test.values,
'y_pred': y_pred,
}, index=y_test.index))
# 汇总结果
results_df = pd.DataFrame(fold_results)
avg_importance = pd.concat(feature_importances, axis=1).mean(axis=1).sort_values(ascending=False)
print("=" * 60)
print("监督学习选股回测结果")
print("=" * 60)
print(f"平均IC: {results_df['IC_mean'].mean():.4f}")
print(f"IC标准差: {results_df['IC_std'].mean():.4f}")
print(f"ICIR: {results_df['ICIR'].mean():.4f}")
print(f"平均RMSE: {results_df['RMSE'].mean():.4f}")
print(f"\nTop 10 重要因子:")
for i, (factor, imp) in enumerate(avg_importance.head(10).items()):
print(f" {i+1}. {factor}: {imp:.4f}")
return {
'model': model,
'fold_results': results_df,
'importance': avg_importance,
'predictions': pd.concat(predictions)
}
# ===== 示例数据生成 =====
np.random.seed(42)
n_dates = 500
n_stocks = 300
# 模拟因子数据(10个因子)
factor_names = ['momentum_1m', 'momentum_3m', 'volatility', 'turnover',
'size', 'value', 'quality', 'growth', 'leverage', 'reversal']
dates = pd.date_range('2020-01-01', periods=n_dates, freq='B')
# 构建 MultiIndex DataFrame
arrays = []
for date in dates:
for stock in range(n_stocks):
arrays.append((date, f'STOCK_{stock:04d}'))
index = pd.MultiIndex.from_tuples(arrays, names=['date', 'stock'])
factors = pd.DataFrame(
np.random.randn(len(index), len(factor_names)),
index=index,
columns=factor_names
)
# 模拟未来收益率(加入与某些因子的真实关系)
true_alpha = (
0.5 * factors['momentum_1m'] +
0.3 * factors['quality'] -
0.4 * factors['volatility'] +
0.2 * factors['value'] +
np.random.randn(len(factors)) * 0.5 # 噪声
)
returns = pd.Series(true_alpha, index=index)
print("注意:以上为示例数据。实际应用中factor_data和returns需使用真实数据。")点击展开可浏览运行结果
注意:以上为示例数据。实际应用中factor_data和returns需使用真实数据。
📌 案例3:多模型集成选股
此处有展示代码展开 ▼
python
from sklearn.ensemble import VotingRegressor, StackingRegressor
from lightgbm import LGBMRegressor
from sklearn.linear_model import Ridge
def ensemble_stock_selection(X_train, y_train, X_test):
"""
使用多模型集成提升选股稳定性
"""
base_models = [
('xgb', XGBRegressor(n_estimators=100, max_depth=4, learning_rate=0.05, random_state=42)),
('lgb', LGBMRegressor(n_estimators=100, max_depth=5, learning_rate=0.05, random_state=42)),
('ridge', Ridge(alpha=1.0))
]
# 方法1:平均集成
voting_model = VotingRegressor(base_models)
voting_model.fit(X_train, y_train)
voting_pred = voting_model.predict(X_test)
# 方法2:Stacking集成(使用Ridge作为元学习器)
stacking_model = StackingRegressor(
estimators=base_models[:2], # XGBoost + LightGBM
final_estimator=Ridge(alpha=1.0),
cv=5
)
stacking_model.fit(X_train, y_train)
stacking_pred = stacking_model.predict(X_test)
# 比较
from scipy.stats import spearmanr
# 单模型预测
xgb_model = XGBRegressor(n_estimators=100, max_depth=4, random_state=42)
xgb_model.fit(X_train, y_train)
xgb_pred = xgb_model.predict(X_test)
lgb_model = LGBMRegressor(n_estimators=100, max_depth=5, random_state=42)
lgb_model.fit(X_train, y_train)
lgb_pred = lgb_model.predict(X_test)
print("多模型集成效果对比:")
print(f"{'模型':<15} {'Rank IC':>10} {'MSE':>10}")
print("-" * 40)
print(f"{'XGBoost':<15} {spearmanr(y_test, xgb_pred)[0]:>10.4f} "
f"{mean_squared_error(y_test, xgb_pred):>10.4f}")
print(f"{'LightGBM':<15} {spearmanr(y_test, lgb_pred)[0]:>10.4f} "
f"{mean_squared_error(y_test, lgb_pred):>10.4f}")
print(f"{'Voting':<15} {spearmanr(y_test, voting_pred)[0]:>10.4f} "
f"{mean_squared_error(y_test, voting_pred):>10.4f}")
print(f"{'Stacking':<15} {spearmanr(y_test, stacking_pred)[0]:>10.4f} "
f"{mean_squared_error(y_test, stacking_pred):>10.4f}")
return voting_model, stacking_model点击展开可浏览运行结果
📘 本段代码定义了 1 个函数/类:函数 `ensemble_stock_selection`(使用多模型集成提升选股稳定性)。该片段为教学展示(未包含独立运行的输入数据),可在实战练习中结合真实数据调用。
常见误区
误区1:XGBoost/LightGBM的参数越多越好。 金融数据的高噪声特性意味着复杂模型很容易过拟合。通常 max_depth=3~6 和 n_estimators=100~300 的取值范围就能获得不错的泛化性能。超过这些范围产生的"性能提升"极大概率是过拟合。
误区2:直接将所有因子扔进模型训练。 因子之间的多重共线性会导致:(1) 特征重要性估计偏差;(2) 模型不稳定(对抗样本敏感);(3) 因子权重难以解释。应先进行因子筛选、正交化或使用主成分分析预处理。
误区3:随机交叉验证在时序金融数据中是合理的。 金融数据具有强时序相关性,使用随机K-Fold交叉验证会引入严重的前视偏差(look-ahead bias)。必须使用时序交叉验证或Purged K-Fold。
误区4:机器学习可以取代金融直觉。 ML模型擅长模式识别,但不擅长因果推理。一个有经济逻辑支撑的"弱"因子(IC=0.02)往往比一个没有逻辑但IC=0.04的"神奇"因子更可靠。
误区5:模型训练和预测的间隔时间不重要。 样本内(in-sample)和样本外(out-of-sample)的时间跨度设置对策略的实盘表现有重大影响。一般建议训练集至少覆盖一个完整的市场周期(12-24个月)。
实战练习
练习1: 实现一个"滚动训练"选股框架:
- 每个月重新训练一次XGBoost模型(使用过去24个月的数据)
- 用训练好的模型预测下个月的股票收益
- 每月买入预测排在前20%的股票(等权配置),持有1个月
- 计算信息系数IR、ICIR、多空组合夏普比等评估指标
- 分析模型在不同市场环境(牛市/熊市/震荡市)下的表现差异
练习2: 使用XGBoost、LightGBM和CatBoost三个梯度提升模型,实现以下对比分析:
- 在相同数据、相同参数下,哪个模型的IC更高?
- 三个模型选出的"Top 10%股票"重叠度有多高?
- 多模型集成(平均预测、Stacking)是否显著提升选股效果?
练习3: 实现一个"特征重要性稳定性分析器":
- 在滚动窗口训练中,记录每个因子在每个训练期的feature importance
- 计算每个因子重要性的均值和标准差
- 分析:重要性排名最稳定的因子是哪些?它们是否有经济逻辑支撑?
- 是否存在"重要性漂移"——因子的重要性随时间发生系统性变化?
延伸阅读
- Gu, S., Kelly, B., & Xiu, D. (2020). Empirical Asset Pricing via Machine Learning. Review of Financial Studies, 33(5), 2223-2273. — 全面地比较了多种ML模型在资产定价/选股中的表现。
- De Prado, M. L. (2018). Advances in Financial Machine Learning. Wiley. — 金融ML的标杆著作,覆盖数据预处理、标签定义、交叉验证等核心问题。
- Chen, T., & Guestrin, C. (2016). XGBoost: A Scalable Tree Boosting System. KDD. — XGBoost算法的原始论文。
- Ke, G., et al. (2017). LightGBM: A Highly Efficient Gradient Boosting Decision Tree. NeurIPS. — LightGBM的GOSS和EFB技术。
- Freyberger, J., Neuhierl, A., & Weber, M. (2020). Dissecting Characteristics Nonparametrically. Review of Financial Studies, 33(5), 2326-2377. — 非参数因子筛选和选股的严谨方法。
本章要点
- XGBoost/LightGBM是量化选股中最主流的有监督学习模型,性能优异且调参友好
- 信息系数(IC)和ICIR是衡量选股模型有效性的核心指标
- 时序交叉验证(而非随机KFold)是金融ML的正确评估方式
- 因子预处理(中性化、标准化、Winsorize)是模型训练前的必要步骤
- 单一模型不如多模型集成(Voting/Stacking)稳健
- 机器学习的优势在于捕捉非线性关系和因子交互,但不能替代金融直觉