4. 数据准备与预处理
均值回归轮动策略的成败,很大程度上在数据进入回测引擎之前就已经决定。策略逻辑本身并不复杂:计算各行业指数相对基准的偏离程度,在偏离过大时做均值回归方向的配置。但一旦原始行情存在复权错误、停牌缺口、异常跳变或日期错位,偏离度就会被污染,回测会给出看似漂亮却无法复现的结论。本节围绕“拿到一份可直接用于计算的行业指数面板数据”这一目标,把数据获取、复权、缺失值、异常值和数据对齐五个环节讲清楚。
一、行业指数数据的获取与口径确认
行业指数的来源主要有三类:交易所或指数公司官方发布的行业指数(如中证行业指数、申万行业指数)、第三方数据接口(如 Tushare、AkShare、Wind、聚宽等),以及自行用成分股合成的行业组合。无论哪种来源,第一步都不是下载,而是确认口径:指数是价格指数还是全收益指数,成分股调整频率如何,基日和基点是什么,行业分类标准是哪一套(申万、中信、证监会或自定义)。口径不一致会导致不同行业之间不可比,例如用价格指数做轮动,高股息行业会因为分红除权被系统性低估。
以 Tushare 为例,获取申万一级行业指数日线的典型调用如下(示例参数,实际以接口文档为准):
import tushare as ts
pro = ts.pro_api("YOUR_TOKEN")
# 示例:获取申万一级行业指数日线,起止日期为假设值
df = pro.sw_daily(ts_code="801010.SI", start_date="20180101", end_date="20231231")
# 返回字段通常包含 trade_date、close、open、high、low、vol、amount 等
df = df.sort_values("trade_date").reset_index(drop=True)
需要强调的是,行业指数本身通常已经是“调整后”的指数,其点位已考虑成分股分红与拆股,因此对指数点位一般不需要再做前复权或后复权。真正需要复权处理的是两种情况:一是用成分股或 ETF 自建行业序列时,个股价格必须复权;二是用行业 ETF 价格代替指数时,ETF 存在分红,需要做复权。这一点常被混淆,务必先判断数据对象是“指数点位”还是“可交易价格”。
二、复权处理:前复权与后复权的选择
复权的本质是把分红、送股、拆股造成的价格跳空还原为连续的价格序列。前复权以当前价格为基准向前调整,历史价格会被改写,适合看图和计算收益率;后复权以上市首日为基准向后调整,历史价格不变,适合做长周期回测和避免未来函数。对于均值回归策略,我们关心的是滚动窗口内的相对偏离,使用前复权序列计算收益率是常见做法,但要注意前复权序列会随最新除权事件变化,若每次回测都重新拉取,历史信号可能发生漂移。
一个稳妥的工程做法是:用后复权价格计算收益率和偏离度,因为后复权序列一旦确定就不再随未来事件改变,可保证回测可复现;展示净值曲线时再转换为前复权口径。若数据源直接提供复权因子,可用“复权价 = 原始价 × 复权因子”统一处理,并保留原始价与因子两列,便于审计。
三、缺失值处理:区分“没交易”和“没数据”
行业指数日线缺失通常有三种原因:非交易日(周末、节假日)、停牌或数据源漏采。非交易日不应填补,正确做法是以交易日历为基准做左连接,只保留真实交易日。停牌造成的缺失,指数点位往往仍有编制(成分股停牌但指数按前收盘计算),若确实缺失,可用前值填充(ffill),但必须限制填充长度,例如连续缺失超过 5 个交易日则标记为可疑并剔除该区间。数据源漏采则应与相邻数据源交叉核对,而不是简单插值。
下面是一段示例处理逻辑,交易日历来自交易所日历接口,缺失值按“前值填充 + 缺失标记”处理:
# 假设 trade_cal 为交易日历,panel 为多行业收盘价宽表
panel = panel.reindex(trade_cal, method=None) # 对齐到交易日
missing_flag = panel.isna()
panel = panel.ffill(limit=5) # 最多连续填充5日
panel = panel.dropna(how="all") # 去掉全空行
# 保留缺失标记,供后续异常检测参考
注意不要用后向填充(bfill),那会把未来信息带入历史,是回测中的典型未来函数。
四、异常值检测:从“跳变”到“不可信”
行业指数的异常值主要来自数据源错误(如小数点错位、单位不一致)、指数编制调整(成分股大比例更换)以及极端行情。检测方法不必复杂,日收益率法是性价比最高的一种:对每个行业计算日对数收益率,若某日收益率绝对值超过阈值(例如 15%,需结合行业波动率设定),则标记为可疑点,再人工核对原始数据。另一种方法是与同源的其他数据(如行业 ETF、成分股加权收益)比对,偏离过大则判定为异常。
异常值的处理要谨慎:真实极端行情不能删除,否则会低估尾部风险;数据错误则应修正或置为缺失后再按缺失值规则处理。建议保留一份“异常标记表”,记录日期、行业、原始值、判定依据和处理方式,作为回测报告的一部分。
五、数据对齐:让所有行业在同一时间轴上可比
多行业面板最常见的错误是日期不对齐:不同行业指数因停牌、数据源更新时点不同,交易日集合存在差异。若直接按行拼接,会出现某行业用 T 日价格、另一行业用 T-1 日价格计算偏离度的情况,信号完全失真。正确做法是以统一交易日历为索引,对所有行业做外连接后按规则填充,并检查每个交易日的有效行业数量,低于阈值(例如少于全部行业的 80%)的日期应整体剔除。
下图展示了本节数据从原始获取到可用于策略计算的完整处理链,以及每个环节的关键判断与输出:
完成上述五步后,得到的应是一张“日期 × 行业”的收盘价矩阵,外加缺失标记表和异常标记表。建议在正式回测前做一次快速体检:检查每个行业的首末交易日是否一致、日收益率分布是否存在明显离群、各行业在同一交易日的有效数量是否稳定。这些检查不需要复杂工具,几行 pandas 代码即可完成,却能提前暴露大部分数据问题。