4. 板块指数与个股数据的整合
跨板块量价共振回测的第一道工程门槛,不是策略逻辑,而是数据对齐。板块指数(如申万行业指数、中证主题指数或自建等权指数)与成分股行情来自不同数据源,交易日历、复权方式、频率、时间戳口径往往不一致。如果直接按行号拼接或按日期字符串简单合并,回测结果会出现"未来函数"或"错位共振"——指数在某日放量上涨,而个股数据实际对应的是前一交易日,共振信号被人为制造出来。本节要解决的就是:如何把板块指数与成分股数据在时间轴和频率上严格对齐,形成可直接进入回测引擎的统一面板。
先明确三个对齐维度
整合工作可以拆成三个必须同时满足的维度。第一是标的维度:板块指数是一个序列,成分股是多个序列,需要确定成分股名单的生效区间,避免用今天的成分股回测三年前(幸存者偏差)。第二是时间维度:两边的交易日历必须取交集或按统一日历重采样,停牌日、节假日、半天交易都要显式处理。第三是频率维度:指数可能是日频,个股若为分钟频,需要先聚合到日频再对齐,或统一降采样到相同周期。三个维度中任何一个错位,量价共振条件就失去意义。
交易日历与频率的统一处理
实际数据中,指数与个股的日期列经常出现"看起来一样、实际对不上"的情况。稳妥做法是:先构造一个主日历,取指数交易日与个股交易日的并集,再按回测需要决定是取交集(只保留双方都有数据的日期)还是保留并集并对缺失值做前向填充。对于量价共振策略,成交量缺失不能简单填零,否则会误判为"缩量";更合理的做法是标记为停牌,并在计算共振条件时跳过该日。
频率聚合要遵循"先聚合、后对齐"的顺序。以个股分钟数据聚合为日频为例,收盘价取当日最后一根分钟K线的收盘,成交量取当日所有分钟成交量之和,最高/最低取当日极值。示例参数:某成分股在 2023-06-15 有 240 根分钟线,成交量合计 1,860,000 股,则日频成交量记为 1,860,000;若当日 11:00 起停牌,则只聚合停牌前的分钟线,并在停牌标记列记 1。聚合完成后再与指数日频序列按日期合并,才能保证"同一天"指的是同一个交易日的完整信息。
复权与成分股区间匹配
指数通常是不复权的点位序列,而个股存在除权除息。若个股用不复权价格与指数比较涨跌幅,除权日会出现虚假大跌。建议统一使用后复权价格计算收益率,或直接用"涨跌幅"字段对齐,避免价格量纲差异。成分股名单则要按生效日期做区间匹配:假设某板块在 2023-01-01 至 2023-06-30 的成分股为 A、B、C,2023-07-01 起调整为 A、B、D,则回测在 7 月之前只能使用 A、B、C 的数据计算板块内个股共振,不能提前引入 D。
一个可执行的对齐示例
下面给出一个简化的 Python 处理骨架,演示"聚合—对齐—校验"的关键步骤(示例数据为假设值,仅用于说明流程):
import pandas as pd
# 假设 idx_daily: 指数日频,index 为日期
# stock_min: 个股分钟频,含 code、datetime、close、volume
stock_min['date'] = stock_min['datetime'].dt.normalize()
stock_daily = stock_min.groupby(['code', 'date']).agg(
close=('close', 'last'),
volume=('volume', 'sum')
).reset_index()
# 统一日历:取指数日期与个股日期的交集
calendar = pd.Index(sorted(set(idx_daily.index) & set(stock_daily['date'])))
panel = idx_daily.reindex(calendar).rename(columns={'close': 'idx_close'})
for code, g in stock_daily.groupby('code'):
s = g.set_index('date')['close'].reindex(calendar)
panel[f'{code}_close'] = s
# 校验:行数、缺失率、同日方向一致性
assert len(panel) == len(calendar)
print(panel.isna().mean())
运行后重点观察两件事:一是 panel.isna().mean() 是否集中在少数停牌日,若某列缺失率异常高,说明该成分股数据源覆盖不足;二是抽查若干交易日,确认指数涨跌方向与多数成分股方向一致,若出现系统性反向,往往是复权口径或日期错位导致。完成这一步,板块指数与个股数据才真正具备进入共振条件回测的资格。