3、数据获取与预处理:金融数据源介绍、数据清洗、缺失值处理、时间序列对齐
在量化回测与灰度发布流程中,数据质量直接决定模型的有效性与策略的稳健性。本章系统讲解金融数据的获取来源、清洗规范、缺失值处理策略以及多源时间序列对齐方法,为后续 LightGBM 特征工程与回测奠定可靠的数据基础。
3.1 金融数据源介绍
量化策略开发通常依赖以下三类数据源,每类数据源在时效性、覆盖范围与成本上存在显著差异:
| 数据源类型 | 典型提供商 | 覆盖范围 | 更新频率 | 成本 | 适用场景 |
|---|---|---|---|---|---|
| 交易所直连 | 上交所、深交所、CME | Level-2 逐笔成交、订单簿 | 实时 / 逐笔 | 极高 | 高频策略、做市 |
| 专业金融终端 | Wind、Bloomberg、同花顺 | 股票、期货、期权、宏观 | 日频 / 分钟频 | 中高 | 中低频策略、研究 |
| 开源/免费数据 | Yahoo Finance、Quandl、Tushare | 主要市场日线、基本面 | 日频 / 周频 | 低 | 原型验证、教学 |
灰度发布数据选型建议: 灰度阶段建议优先使用专业终端或交易所直连数据,确保数据质量与回测结果的可复现性。开源数据仅用于快速原型验证,正式灰度前必须与权威数据源交叉校验。
3.2 数据清洗规范
原始金融数据通常包含以下问题,需按固定流程清洗:
- 重复值去除:同一时间戳下出现多条相同记录,保留第一条或最后一条。
- 异常值检测:价格、成交量出现明显偏离(如涨跌幅超过 20%、成交量突变为 0 或异常放大),需标记或剔除。
- 停牌/退市处理:停牌期间价格不变、成交量为 0,应保留但标记状态,避免参与模型训练。
- 复权处理:股票分红、送股、配股后需进行前复权或后复权,保证价格序列连续性。
# 数据清洗示例(Python 伪代码)
import pandas as pd
def clean_market_data(df: pd.DataFrame) -> pd.DataFrame:
# 1. 去除重复行
df = df.drop_duplicates(subset=['ts_code', 'trade_date'])
# 2. 剔除异常价格(涨跌幅超过 20%)
df = df[df['pct_chg'].between(-20, 20)]
# 3. 标记停牌日
df['is_suspended'] = (df['volume'] == 0) & (df['close'] == df['pre_close'])
# 4. 前复权处理(使用复权因子)
df['adj_close'] = df['close'] * df['adj_factor']
return df
注意: 复权方式的选择会影响回测结果。前复权适合回测时模拟历史真实收益,后复权适合观察实际价格走势。灰度发布阶段建议统一使用前复权,并在回测报告中注明复权方式。
3.3 缺失值处理策略
金融时间序列的缺失值通常由停牌、数据源中断、节假日差异等原因造成。不同缺失模式需采用不同处理策略:
| 缺失模式 | 典型场景 | 推荐处理方法 | 注意事项 |
|---|---|---|---|
| 单点缺失 | 个别交易日数据缺失 | 前向填充(ffill) | 仅适用于价格类,成交量不可填充 |
| 连续缺失 | 停牌 5 个交易日 | 标记为缺失,不填充 | 模型训练时需设置缺失值处理参数 |
| 跨品种缺失 | A 股有数据,B 股无数据 | 对齐后删除该时间点 | 避免引入未来信息 |
| 节假日差异 | 中美市场节假日不同 | 按各自交易日独立对齐 | 回测时需使用统一日历 |
# 缺失值处理示例
# 前向填充价格,成交量不填充
price_cols = ['open', 'high', 'low', 'close', 'adj_close']
df[price_cols] = df[price_cols].fillna(method='ffill')
# 成交量缺失直接置为 0 或 NaN
df['volume'] = df['volume'].fillna(0)
# LightGBM 原生支持缺失值,无需额外插值
LightGBM 缺失值优势: LightGBM 原生支持缺失值处理,在训练时自动将缺失值分配到增益最大的分支,无需手动插值。但回测场景中,缺失值仍建议按业务逻辑处理,避免引入数据泄露。
3.4 时间序列对齐
多源数据(股票、指数、期货、宏观指标)通常具有不同的时间频率与交易日历,对齐是特征工程前的必要步骤。
3.4.1 对齐流程
- 统一交易日历:确定回测区间内所有品种的共同交易日。
- 频率统一:将分钟数据聚合为日线,或日线扩展为分钟(需谨慎)。
- 索引对齐:以主品种(如沪深 300 指数)的交易日为基准,左连接其他数据。
- 缺失标记:对齐后仍缺失的数据按 3.3 节策略处理。
# 时间序列对齐示例
import pandas as pd
# 假设 stock_df 为股票日线,index_df 为指数日线
# 以指数交易日为基准对齐
aligned_df = index_df[['trade_date', 'index_close']].merge(
stock_df[['trade_date', 'close', 'volume']],
on='trade_date',
how='left'
)
# 对齐后检查缺失
print(aligned_df.isnull().sum())
3.4.2 对齐前后对比
下图展示了对齐前后数据结构的差异:
3.5 数据预处理完整流程
将上述步骤整合为可复用的数据预处理管线:
def data_preprocessing_pipeline(raw_data_dir: str, output_dir: str):
# 1. 加载原始数据
stock_df = load_stock_data(raw_data_dir + '/stock/')
index_df = load_index_data(raw_data_dir + '/index/')
macro_df = load_macro_data(raw_data_dir + '/macro/')
# 2. 清洗
stock_df = clean_market_data(stock_df)
index_df = clean_market_data(index_df)
# 3. 缺失值处理
stock_df = fill_missing_prices(stock_df)
index_df = fill_missing_prices(index_df)
# 4. 时间序列对齐
aligned_df = align_by_trade_calendar(stock_df, index_df, macro_df)
# 5. 保存预处理结果
aligned_df.to_parquet(output_dir + '/aligned_data.parquet')
print(f'预处理完成,共 {len(aligned_df)} 条记录')
灰度发布数据校验要点: 在灰度发布前,需对预处理后的数据做以下校验:
• 时间戳是否严格递增且无重复
• 价格序列是否连续(停牌日已标记)
• 对齐后缺失比例是否低于 5%
• 复权因子是否与官方数据一致
• 时间戳是否严格递增且无重复
• 价格序列是否连续(停牌日已标记)
• 对齐后缺失比例是否低于 5%
• 复权因子是否与官方数据一致
3.6 数据质量监控指标
建立数据质量监控看板,在灰度发布期间持续跟踪:
| 监控指标 | 计算方式 | 告警阈值 | 处理动作 |
|---|---|---|---|
| 缺失率 | 缺失值数量 / 总记录数 | > 3% | 检查数据源连接 |
| 异常价格占比 | 涨跌幅 > 20% 的记录数 / 总记录数 | > 0.5% | 标记并人工复核 |
| 对齐偏差 | 对齐后交易日数量与基准日历差异 | > 2 天 | 检查交易日历配置 |
| 复权因子变化率 | 当日复权因子 / 前日复权因子 - 1 | > 10% | 确认分红送股事件 |
通过以上数据获取与预处理流程,可以为 LightGBM 回测灰度发布提供干净、对齐、可追溯的金融数据集,有效降低数据偏差带来的策略误判风险。