3. 数据准备:历史行情数据、因子数据、标签数据的获取与清洗

在 Champion Challenger 回测流程中,数据是模型验证的基石。本章聚焦于三类核心数据的获取与清洗:历史行情数据(价格、成交量)、因子数据(特征信号)和标签数据(未来收益或分类标记)。数据质量直接决定回测结果的可靠性,因此清洗环节必须严格遵循金融时序规范。

3.1 三类数据的定义与来源

数据类型 定义 典型来源 关键字段
历史行情数据 资产在交易所产生的价格与成交量记录 Wind、Bloomberg、交易所API open, high, low, close, volume, adj_factor
因子数据 基于行情或另类数据计算的特征信号 自建计算、第三方因子库 动量因子、价值因子、波动率因子等
标签数据 用于监督学习的未来目标变量 基于未来行情衍生 未来N日收益率、涨跌分类、风险等级
实践要点: 历史行情数据通常以日频或分钟频存储;因子数据必须与行情数据在时间戳和资产代码上严格对齐;标签数据应避免未来信息泄露,即标签计算时点必须晚于特征时点。

3.2 数据获取流程

数据获取遵循“先宽后窄、先全后精”的原则。以下为标准化获取步骤:

  1. 确定时间范围与资产池:设定回测起始日、结束日,筛选流动性充足的股票/期货/加密货币。
  2. 拉取原始行情:通过API或数据库批量获取OHLCV数据,包含复权因子。
  3. 计算原始因子:基于行情数据计算基础因子(如过去20日收益率、RSI、成交量变化率)。
  4. 生成标签:使用未来窗口数据计算标签(如未来5日收益率 > 2% 标记为1,否则为0)。
  5. 合并数据集:以日期+资产代码为键,左连接行情、因子、标签,形成宽表。
# 示例:Python 伪代码 - 数据合并
import pandas as pd

# 假设已获取行情、因子、标签三个DataFrame
price_df = pd.read_parquet('price.parquet')  # columns: date, asset, open, high, low, close, volume
factor_df = pd.read_parquet('factor.parquet')  # columns: date, asset, momentum, volatility
label_df = pd.read_parquet('label.parquet')    # columns: date, asset, future_return_5d

# 合并
merged = price_df.merge(factor_df, on=['date', 'asset'], how='inner')
merged = merged.merge(label_df, on=['date', 'asset'], how='inner')
merged = merged.sort_values(['asset', 'date']).reset_index(drop=True)

3.3 数据清洗核心规则

金融时序数据清洗不同于普通表格数据,必须处理以下问题:

  • 缺失值处理:行情数据缺失时,前向填充(ffill)最多不超过3个交易日;因子数据缺失时,使用截面中位数填充或删除该样本。
  • 异常值检测:对收益率、因子值进行MAD(中位数绝对偏差)去极值,通常阈值设为5倍MAD。
  • 复权处理:使用后复权价格计算因子,避免除权除息导致的收益率跳跃。
  • 未来信息检查:确保标签计算时使用的未来数据在回测时点不可见,可通过shift操作验证。
  • 停牌与退市处理:停牌期间因子值保持最后有效值,退市股票在退市日之后删除。
常见陷阱: 使用未来函数计算因子(如用当日收盘价计算当日开盘因子)会导致回测结果虚高。务必在清洗阶段做时间戳对齐检查,确保因子计算只使用历史数据。

3.4 数据清洗流程SVG

下图展示了从原始数据到可用训练集的完整清洗流水线:

数据清洗流程 数据清洗流水线 原始行情/因子/标签 缺失值填充 MAD去极值 后复权调整 未来信息检查 停牌/退市过滤 清洗后训练数据集 (无缺失、无异常、无未来信息) 清洗顺序不可颠倒:先填充缺失 → 再去极值 → 复权 → 检查未来信息 → 最后过滤停牌退市

3.5 因子与标签的标准化处理

清洗后的因子和标签通常还需要进行标准化,以消除量纲影响:

处理方法 适用场景 计算公式 注意事项
Z-score标准化 因子值近似正态分布 (x - μ) / σ 需使用截面均值与标准差,避免时序泄漏
Rank标准化 因子值分布偏态严重 rank(x) / N 将因子转换为0~1均匀分布
标签二值化 分类任务 1 if return > threshold else 0 阈值需根据策略目标设定(如2%、5%)
# 示例:因子Z-score标准化(截面处理)
def cross_sectional_zscore(df, factor_col, date_col='date'):
    """对每个时间截面的因子做Z-score标准化"""
    df['factor_z'] = df.groupby(date_col)[factor_col].transform(
        lambda x: (x - x.mean()) / x.std()
    )
    return df

# 示例:标签生成(未来5日收益率二值化)
def create_label(df, price_col='close', window=5, threshold=0.02):
    df['future_return'] = df.groupby('asset')[price_col].transform(
        lambda x: x.shift(-window) / x - 1
    )
    df['label'] = (df['future_return'] > threshold).astype(int)
    return df

3.6 数据质量验证清单

在数据进入回测引擎前,建议逐项检查以下指标:

  • 时间完整性:每个资产在回测区间内交易日覆盖率是否 > 95%?
  • 因子相关性:因子间相关系数是否过高(>0.95)?需考虑去冗余。
  • 标签平衡性:分类标签中正负样本比例是否严重失衡(如 < 1:9)?
  • 未来信息泄漏测试:随机抽取100个样本,人工验证标签计算时点是否晚于特征时点。
  • 数据一致性:合并后的行数是否等于各子表内连接后的预期行数?
最佳实践: 建议将清洗后的数据保存为Parquet格式,按资产分区存储,并记录清洗日志(包括缺失率、去极值数量、删除样本数)。这有助于后续回测结果的可复现性审计。

3.7 数据版本管理SVG

在Champion Challenger流程中,数据版本管理至关重要。下图展示了数据从原始到最终版本的演进路径:

数据版本管理 数据版本管理演进 v0.1 原始数据 未清洗,含缺失/异常 v0.2 清洗数据 缺失填充、去极值 v0.3 标准化数据 Z-score/Rank标准化 v1.0 回测就绪 通过质量验证 版本管理规则:每次清洗操作生成新版本,保留所有中间版本以便回滚审计 Champion与Challenger模型必须使用同一版本数据,确保对比公平 T+0 T+1 T+2 T+3

通过以上流程,我们能够构建一个干净、无未来信息、可复现的数据集,为后续 Champion 与 Challenger 模型的公平对比奠定坚实基础。