Skip to content

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)$$

其中 l 是损失函数(如MSE或Logistic Loss),Ω 是正则项:

$$\Omega(f) = \gamma T + \frac{1}{2} \lambda \|w\|^2$$

T 是叶子节点数,w 是叶子权重,γλ 控制树的复杂度。通过二阶泰勒展开,XGBoost可以高效地找到每轮迭代的最优树结构。

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分类

PYTHON4 行 · 127 B
📄此处有展示代码4 行 · 127 B展开 ▼
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:完整的监督学习选股流水线

PYTHON152 行 · 5.0 KB
📄此处有展示代码152 行 · 5.0 KB展开 ▼
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:多模型集成选股

PYTHON53 行 · 2.1 KB
📄此处有展示代码53 行 · 2.1 KB展开 ▼
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~6n_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
  • 计算每个因子重要性的均值和标准差
  • 分析:重要性排名最稳定的因子是哪些?它们是否有经济逻辑支撑?
  • 是否存在"重要性漂移"——因子的重要性随时间发生系统性变化?

延伸阅读

  1. Gu, S., Kelly, B., & Xiu, D. (2020). Empirical Asset Pricing via Machine Learning. Review of Financial Studies, 33(5), 2223-2273. — 全面地比较了多种ML模型在资产定价/选股中的表现。
  2. De Prado, M. L. (2018). Advances in Financial Machine Learning. Wiley. — 金融ML的标杆著作,覆盖数据预处理、标签定义、交叉验证等核心问题。
  3. Chen, T., & Guestrin, C. (2016). XGBoost: A Scalable Tree Boosting System. KDD. — XGBoost算法的原始论文。
  4. Ke, G., et al. (2017). LightGBM: A Highly Efficient Gradient Boosting Decision Tree. NeurIPS. — LightGBM的GOSS和EFB技术。
  5. 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)稳健
  • 机器学习的优势在于捕捉非线性关系因子交互,但不能替代金融直觉