四、数据对齐与重采样:将逐笔数据重采样为不同频率(秒级、分钟级)的K线,以及复权处理
逐笔成交数据是市场最原始的交易记录,包含时间戳、价格、成交量、买卖方向等字段。然而,逐笔数据量巨大(单日沪深两市可达数亿条),直接用于策略回测或因子计算既不经济也不必要。将逐笔数据重采样为秒级、分钟级K线,是特征工程的第一步,也是连接微观结构与宏观形态的桥梁。本章将系统讲解重采样的核心原理、操作流程、复权处理及常见误区。
4.1 核心原理:从逐笔到K线的映射逻辑
重采样的本质是将不规则时间间隔的逐笔成交记录,映射到规则时间网格上。这一过程涉及三个关键决策:时间分桶方式、聚合函数选择和边界条件定义。
- 时间分桶:秒级K线以自然秒为桶宽(如09:30:00~09:30:01),分钟级K线以自然分钟为桶宽。桶的起点通常采用左闭右开区间,即包含起点时刻的成交,不包含终点时刻的成交。
- 聚合函数:开盘价取桶内第一笔成交价,收盘价取最后一笔,最高价取最大值,最低价取最小值,成交量累加,成交额累加,笔数计数。
- 边界处理:集合竞价时段(09:15~09:25)的成交通常归入第一根分钟K线;午间休市(11:30~13:00)不产生K线;停牌时段无数据,需填充NaN或前值。
4.2 操作流程:标准重采样五步法
以下流程适用于Python pandas、Polars或任何数据处理框架,核心逻辑一致。
- 数据清洗:去除时间戳重复、价格为负、数量为零的异常记录;检查时间戳是否严格递增。
- 时间标准化:将时间戳统一为北京时间(UTC+8),并转换为datetime类型;注意处理夏令时(国内无)和时区偏移。
- 设置时间索引:将时间戳列设为DataFrame的索引,便于重采样。
- 执行重采样:使用
resample('1S')或resample('1T')进行分桶,然后对每列应用聚合函数。 - 后处理:填充缺失K线(如停牌时段),计算复权因子,生成最终K线DataFrame。
import pandas as pd
# 假设 df 为逐笔数据,包含列:time, price, volume, amount, direction
df['time'] = pd.to_datetime(df['time'], utc=True).dt.tz_convert('Asia/Shanghai')
df = df.set_index('time').sort_index()
# 1分钟K线重采样
kline_1min = df.resample('1T', label='left', closed='left').agg({
'price': 'ohlc', # 生成 open, high, low, close
'volume': 'sum',
'amount': 'sum',
'direction': 'count' # 笔数
})
kline_1min.columns = ['open', 'high', 'low', 'close', 'volume', 'amount', 'count']
kline_1min = kline_1min.dropna(subset=['open']) # 去除无成交时段
label='left' 和 closed='left',确保时间戳对齐到桶的起点。对于分钟级K线,若需与交易所官方K线对齐,应使用 closed='left' 且时间戳标记为分钟起点(如09:31表示09:31:00~09:31:59)。
4.3 复权处理:价格连续性的关键
复权是为了消除分红、送股、配股等公司行为对价格序列造成的"跳空",使历史价格具有可比性。复权分为前复权和后复权两种方式。
| 复权类型 | 定义 | 适用场景 | 特点 |
|---|---|---|---|
| 前复权 | 以当前价格为基准,调整历史价格 | 技术分析、形态识别 | 最新价格与市场一致,但历史价格会随每次除权而变动 |
| 后复权 | 以上市首日价格为基准,调整后续价格 | 长期收益计算、因子回测 | 历史价格固定不变,但最新价格与市场不一致 |
| 不复权 | 使用原始成交价格 | 日内高频策略、盘口分析 | 保留真实成交价,但存在除权跳空 |
复权因子的计算公式为:
复权因子 = (除权前收盘价 - 现金红利 + 配股价 × 配股比例) / (除权前收盘价 + 送股比例 × 除权前收盘价 + 配股比例 × 配股价)
实际应用中,通常使用第三方数据源(如Wind、Tushare、AkShare)提供的复权因子,避免自行计算带来的误差。对于逐笔数据重采样后的K线,建议先重采样,后复权,因为复权因子是基于日线计算的,直接应用于逐笔价格会引入偏差。
4.4 案例拆解:分钟K线重采样与复权实战
以下案例基于某股票2024年3月15日的逐笔数据(约12万条),目标生成1分钟K线并处理后复权价格。
背景与约束:该股票当日发生除权(10送2派1元),除权前收盘价为20.00元,除权参考价为16.58元。逐笔数据包含原始成交价,未复权。
操作步骤:
- 读取逐笔数据,清洗异常记录(删除时间戳重复的3条、价格为0的1条)。
- 按上述代码重采样为1分钟K线,得到240根K线(09:30~11:30共121根,13:00~15:00共119根)。
- 从数据源获取复权因子:当日复权因子为0.829(=16.58/20.00)。
- 计算后复权价格:将每根K线的OHLC乘以复权因子,成交量不变,成交额除以复权因子(保持金额一致)。
- 验证:后复权收盘价序列的收益率与原始价格序列的日收益率一致。
| 时间 | 原始收盘价 | 后复权收盘价 | 成交量(手) | 后复权成交额(元) |
|---|---|---|---|---|
| 09:31 | 16.60 | 13.76 | 1,250 | 1,720,000 |
| 09:32 | 16.58 | 13.74 | 980 | 1,346,000 |
| ... | ... | ... | ... | ... |
| 15:00 | 17.20 | 14.26 | 2,100 | 2,994,000 |
结果对比:未复权的分钟K线在除权日会出现明显的价格跳空(从20元直接跳到16.58元),而后复权K线则呈现连续的价格序列,便于技术指标计算。但需注意,后复权价格并非真实成交价,在计算涨跌停幅度、盘口深度等场景时,必须使用原始价格。
4.5 常见误区与总结复盘
在数据对齐与重采样过程中,以下误区最为常见:
- 误区一:忽略集合竞价时段。集合竞价的成交量应归入第一根K线,否则会低估开盘时的交易活跃度。
- 误区二:时间戳精度不一致。部分交易所提供微秒级时间戳,部分仅到秒级,混用会导致排序错误。
- 误区三:复权因子使用错误。将前复权因子应用于后复权计算,或反之,导致价格序列扭曲。
- 误区四:未处理停牌时段。停牌期间不应生成K线,但需保留时间索引以便后续对齐。
- 误区五:成交量单位混淆。逐笔数据可能以"股"为单位,而K线通常以"手"为单位(1手=100股),需统一换算。
总结而言,数据对齐与重采样是量化分析的基础工程,其质量直接决定后续特征提取和模型训练的有效性。掌握时间分桶、聚合计算、复权处理三大核心技能,并建立严格的数据校验流程,是每一位量化工程师的必修课。