3、特征工程概述:特征提取、特征选择、特征变换的基本流程,特征与标签的时序对齐问题

特征工程是量化回测中连接原始数据与预测模型的桥梁。原始行情数据、财务数据、另类数据必须经过系统化的特征工程处理,才能转化为模型可用的输入信号。本章聚焦特征工程的核心流程——提取、选择、变换,并重点剖析量化场景下最容易导致未来函数(Look-ahead Bias)的时序对齐问题。

3.1 特征工程三大核心流程

特征工程并非一次性操作,而是一个包含提取、选择、变换的迭代闭环。下表总结了每个环节的目标与典型方法:

环节 核心目标 典型方法 量化场景注意事项
特征提取 从原始数据中生成有预测力的候选特征 技术指标(均线、RSI、布林带)、波动率估计、截面因子(市值、动量)、文本情绪得分 避免使用未来数据计算指标(如用当日收盘价计算当日均线)
特征选择 从候选特征中筛选出对预测目标最有效的子集 单变量筛选(IC、Rank IC)、L1正则化(Lasso)、树模型特征重要性、递归特征消除(RFE) 必须在样本内进行选择,避免在样本外引入选择偏差
特征变换 调整特征分布或尺度,提升模型稳定性与收敛速度 标准化(Z-score)、分位数变换、对数变换、主成分分析(PCA)、交叉组合 变换参数(如均值和标准差)必须在训练集上拟合,再应用到验证/测试集
实践提示:在量化回测中,特征提取阶段最容易引入未来信息。例如,使用“当日收盘价”计算“当日5日均线”时,均线已经包含了收盘价信息,导致特征与标签之间存在信息泄漏。正确的做法是使用前一日或更早的数据计算特征。

3.2 特征提取:从原始数据到候选特征

特征提取是特征工程的第一步,其质量直接决定模型的上限。在量化领域,特征提取通常围绕以下三类数据展开:

  • 时序特征:基于单一资产的历史价格、成交量、持仓量计算。例如:过去N日收益率、波动率、成交量变化率、移动平均线交叉信号。
  • 截面特征:在同一时间点对所有资产进行排序或比较。例如:市值排名、过去一个月收益率的截面分位数、行业相对强度。
  • 另类特征:来自非传统数据源。例如:新闻情感得分、社交媒体热度、卫星图像数据、供应链关系指标。

以下是一个简单的特征提取代码示例,展示如何避免未来函数:

import pandas as pd
import numpy as np

# 假设 df 包含日期、股票代码、收盘价,已按日期排序
def extract_features(df, lookback=20):
    df = df.copy()
    # 使用 shift(1) 确保特征只用到历史数据
    df['ret_1d'] = df['close'].pct_change()
    df['ret_5d'] = df['close'].pct_change(5)
    df['ma_20'] = df['close'].rolling(window=lookback).mean().shift(1)
    df['vol_20'] = df['close'].rolling(window=lookback).std().shift(1)
    # 避免使用未来数据:shift(1) 后,当前行的特征值只包含截至前一日的信息
    df['ma_20_shift'] = df['ma_20'].shift(1)
    return df.dropna()

3.3 特征选择:降维与去噪

候选特征数量往往远大于有效特征数量。特征选择的目标是剔除冗余、噪声特征,提升模型泛化能力。常用的选择策略包括:

  1. 过滤法(Filter):独立于模型,基于特征与标签的统计关系。常用指标:信息系数(IC)、Rank IC、方差阈值。
  2. 包裹法(Wrapper):使用模型性能作为评价标准,迭代搜索特征子集。例如:递归特征消除(RFE)、前向/后向搜索。
  3. 嵌入法(Embedded):在模型训练过程中自动进行特征选择。例如:Lasso回归的L1正则化、树模型的特征重要性排序。
量化场景关键点:特征选择必须在训练集内完成,且不能使用未来信息。例如,使用全样本的IC值来选择特征会导致选择偏差。正确的做法是在每个滚动窗口的训练期内重新计算IC并选择特征。

3.4 特征变换:标准化与非线性映射

特征变换旨在改善特征分布,使其更适合模型假设。常见的变换操作包括:

变换类型 适用场景 量化注意事项
Z-score标准化 线性模型、距离度量模型 均值和标准差必须在训练集上计算,避免使用全局统计量
分位数变换 处理极端值、非正态分布 分位数映射表需在训练集上拟合,防止未来信息泄漏
对数/Box-Cox变换 右偏分布(如成交量、波动率) 变换参数需在训练集上估计,且变换后需检查分布形态
主成分分析(PCA) 高维特征降维、去相关性 PCA载荷矩阵必须在训练集上拟合,且需注意成分的可解释性

3.5 特征与标签的时序对齐:核心难点

时序对齐是量化特征工程中最容易被忽视但后果最严重的环节。错误的时序对齐会导致特征包含未来信息,使回测结果严重失真。下图展示了正确的时序对齐流程:

特征与标签时序对齐流程 T-2 T-1 T T+1 特征计算窗口(过去N日数据) 特征值 标签窗口(未来收益) 关键规则: 特征值必须在标签窗口开始之前生成,确保特征只使用截至T时刻的历史数据,标签使用T+1时刻的未来数据。

时序对齐的核心原则可以概括为:特征值的时间戳必须严格早于标签的时间戳。具体来说:

  • 特征时间戳:特征计算截止时刻(如上图中的T时刻),特征值仅使用该时刻之前的数据。
  • 标签时间戳:未来收益的计算起始时刻(如上图中的T+1时刻),标签使用该时刻之后的数据。
  • 对齐方式:将特征时间戳与标签时间戳进行匹配,确保每个样本的特征都来自标签之前的信息。
常见错误:许多回测框架在计算日频特征时,使用当日收盘价计算当日指标,然后预测当日收盘到次日收盘的收益。这会导致特征包含了当日收盘价信息,而标签也使用了当日收盘价,造成严重的信息泄漏。正确的做法是使用前一日收盘价计算特征,预测当日收盘到次日收盘的收益。

3.6 时序对齐的实践方案

在实际回测中,推荐使用以下方案确保时序对齐的正确性:

  1. 数据预处理阶段:在原始数据中显式添加特征时间戳和标签时间戳字段,避免依赖行索引。
  2. 特征计算阶段:所有滚动窗口计算完成后,使用 .shift(1) 将特征值向后移动一个时间单位,确保当前行的特征不包含当前时刻的信息。
  3. 样本构建阶段:使用显式的日期匹配逻辑,将特征数据与标签数据按时间戳对齐,而非按行号对齐。
  4. 验证阶段:在回测结果中随机抽取样本,手动检查特征值与标签值的时间先后关系,作为质量检查步骤。

以下代码展示了正确的时序对齐实现:

def align_features_and_labels(feature_df, price_df, label_window=5):
    """
    正确对齐特征与标签,避免未来函数
    
    参数:
        feature_df: 包含特征值及时间戳的DataFrame
        price_df: 包含价格及时间戳的DataFrame
        label_window: 未来收益计算窗口
    
    返回:
        对齐后的样本集
    """
    # 计算未来收益作为标签
    price_df['future_ret'] = (price_df['close'].shift(-label_window) 
                              / price_df['close'] - 1)
    
    # 合并特征与标签,使用左连接确保特征时间戳为主
    aligned = feature_df.merge(
        price_df[['date', 'future_ret']], 
        on='date', 
        how='left'
    )
    
    # 删除标签为NaN的样本(窗口末尾数据)
    aligned = aligned.dropna(subset=['future_ret'])
    
    # 验证:确保特征时间戳早于标签计算起始时间
    # 特征使用截至date的数据,标签使用date之后的数据
    return aligned
总结:特征工程是量化回测的基石,而时序对齐是特征工程中最关键的约束条件。特征提取、选择、变换三大流程必须在严格的时序对齐框架下执行,才能保证回测结果的真实性和可靠性。在后续章节中,我们将深入探讨每种特征工程方法在量化场景下的具体实现与优化技巧。