3. 数据准备:历史行情数据、因子数据、标签数据的获取与清洗
在 Champion Challenger 回测流程中,数据是模型验证的基石。本章聚焦于三类核心数据的获取与清洗:历史行情数据(价格、成交量)、因子数据(特征信号)和标签数据(未来收益或分类标记)。数据质量直接决定回测结果的可靠性,因此清洗环节必须严格遵循金融时序规范。
3.1 三类数据的定义与来源
| 数据类型 | 定义 | 典型来源 | 关键字段 |
|---|---|---|---|
| 历史行情数据 | 资产在交易所产生的价格与成交量记录 | Wind、Bloomberg、交易所API | open, high, low, close, volume, adj_factor |
| 因子数据 | 基于行情或另类数据计算的特征信号 | 自建计算、第三方因子库 | 动量因子、价值因子、波动率因子等 |
| 标签数据 | 用于监督学习的未来目标变量 | 基于未来行情衍生 | 未来N日收益率、涨跌分类、风险等级 |
实践要点: 历史行情数据通常以日频或分钟频存储;因子数据必须与行情数据在时间戳和资产代码上严格对齐;标签数据应避免未来信息泄露,即标签计算时点必须晚于特征时点。
3.2 数据获取流程
数据获取遵循“先宽后窄、先全后精”的原则。以下为标准化获取步骤:
- 确定时间范围与资产池:设定回测起始日、结束日,筛选流动性充足的股票/期货/加密货币。
- 拉取原始行情:通过API或数据库批量获取OHLCV数据,包含复权因子。
- 计算原始因子:基于行情数据计算基础因子(如过去20日收益率、RSI、成交量变化率)。
- 生成标签:使用未来窗口数据计算标签(如未来5日收益率 > 2% 标记为1,否则为0)。
- 合并数据集:以日期+资产代码为键,左连接行情、因子、标签,形成宽表。
# 示例: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
下图展示了从原始数据到可用训练集的完整清洗流水线:
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流程中,数据版本管理至关重要。下图展示了数据从原始到最终版本的演进路径:
通过以上流程,我们能够构建一个干净、无未来信息、可复现的数据集,为后续 Champion 与 Challenger 模型的公平对比奠定坚实基础。