4. 数据准备与行业分类标准

波动率调整后的行业强度排名,本质上是一个“先算行业收益、再除以行业风险、最后横向排序”的过程。而这一链条的起点,是干净、口径一致、时间对齐的行业指数数据。如果行业分类体系混用、成分股调整未处理、停牌或退市造成的缺失值被简单填零,那么后续无论用多精细的波动率模型,排名结果都会失真。本节聚焦三件事:行业指数数据从哪里取、怎么清洗;申万、中信、GICS 三套分类体系如何选择与映射;缺失值与异常值如何识别和处理。

一、行业指数数据的获取与字段结构

行业强度排名的常用数据源包括:Wind、Choice、同花顺 iFinD 等金融终端,中证指数公司与申万宏源研究所官网,以及聚宽、Tushare、AkShare 等开源数据接口。无论来源如何,最终需要落到一张“日期 × 行业”的面板表上。最小可用字段通常包括:交易日期、行业代码、行业名称、所属分类体系、收盘点位、日涨跌幅、成分股数量、成交额。若要做波动率调整,还需要至少 60 个交易日以上的日收益率序列,因此取数区间应比排名观察窗口向前多取一段“预热期”。

以申万一级行业为例,假设我们研究 2023 年全年、按月调仓的行业强度排名,则实际取数区间应设为 2022 年 10 月初至 2023 年 12 月底,前三个月用于计算滚动波动率,不参与排名。取数时建议一次性拉取全部 31 个申万一级行业的日收盘点位,形成宽表,避免逐行业循环取数造成日期错位。

# 示例:用 AkShare 获取申万一级行业日行情(示例代码,接口字段以实际版本为准)
import akshare as ak
import pandas as pd

# 行业列表
sw_list = ak.sw_index_first_info()
codes = sw_list["行业代码"].str[:6].tolist()

frames = []
for code in codes:
    df = ak.index_hist_sw(symbol=code, period="day")
    df["行业代码"] = code
    frames.append(df)

panel = pd.concat(frames, ignore_index=True)
panel["日期"] = pd.to_datetime(panel["日期"])
panel = panel.pivot(index="日期", columns="行业代码", values="收盘")
panel = panel.sort_index().loc["2022-10-01":"2023-12-31"]
print(panel.shape)   # 期望形如 (约300, 31)

二、三套分类体系的选择与映射

申万、中信、GICS 是国内行业研究中最常见的三套体系,它们的划分逻辑并不相同。申万分类贴近 A 股本土产业结构,一级行业数量约 31 个,历史上经过 2014、2021 两次较大修订,成分调整较频繁;中信分类同样面向 A 股,一级行业约 30 个,行业边界与申万存在差异,例如对“电力设备及新能源”“有色金属”的归并方式不同;GICS 由标普与 MSCI 维护,全球通用,一级行业 11 个,颗粒度更粗,适合跨市场比较,但对 A 股细分产业的刻画不如申万细腻。

选择标准应服务于研究目的:若排名只在 A 股内部进行、需要较细的产业区分,优先申万一级;若需要与中信研报、卖方行业配置观点对齐,则用中信;若要做 A 股与港股、美股的行业横向对比,必须统一到 GICS。关键原则是全样本期内只用一套体系,不能前半段用申万、后半段换中信,否则行业收益序列会出现口径断裂。

当研究需要跨体系对照时,应建立映射表而非直接拼接。映射表至少包含:源体系代码、源行业名称、目标体系代码、目标行业名称、映射方式(一对一、一对多、多对一)、生效起止日期。一对多情形(如申万某行业对应 GICS 两个行业)需要按成分股市值加权拆分,不能简单取平均。

行业指数数据准备与分类映射流程 数据源 Wind / Choice / iFinD AkShare / Tushare 原始面板 日期 × 行业代码 收盘点位 / 成交额 清洗与对齐 交易日历对齐 缺失值 / 异常值处理 分类体系 申万 / 中信 GICS 行业映射表(关键字段) 源代码 → 目标代码 | 映射方式:一对一 / 一对多 / 多对一 生效起止日期 | 一对多按成分股市值加权拆分 质量检查点 缺失率 < 1% 单日涨跌 > ±11% 复核 点位连续、无跳空 输出:对齐后的行业日收益率面板 供波动率计算与行业强度排名使用 异常值处理 核查成分调整公告

三、缺失值的识别与处理

行业指数层面的缺失,通常来自三种原因:一是交易日历不一致,例如港股与 A 股节假日不同,跨市场数据合并时出现空值;二是行业指数发布滞后或接口偶发失败;三是行业分类调整导致旧代码停用、新代码从某日起才有数据。处理方式必须区分原因,不能一律前向填充。

对于交易日历不一致造成的缺失,正确做法是先对齐到研究基准日历(如 A 股交易日历),再对非交易日做前向填充,因为非交易日指数点位本就不变。对于接口偶发失败,应重新拉取或与相邻交易日比对,确认是数据缺失而非真实停牌。对于分类调整造成的代码切换,应在映射表中记录切换日期,切换前用旧代码、切换后用新代码,并在切换日检查收益率是否出现异常跳变。若某行业在观察窗口内缺失率超过 5%,建议直接剔除该行业并记录原因,而不是强行插值。

不要用 0 填充行业指数收益率。0 收益会被误判为“低波动”,从而在波动率调整后人为抬高该行业的强度得分,这是行业排名类研究中最常见的隐性偏差来源。

四、异常值的判定与修正

行业指数是成分股加权结果,单日涨跌幅通常比个股平缓。以申万一级行业为例,正常交易日涨跌幅多在 ±3% 以内,极端行情下可能达到 ±8% 至 ±10%。因此可设定两条检查规则:其一,单日涨跌幅绝对值超过 11% 时标记为可疑,需回查当日是否有重大成分调整、指数编制规则变更或数据源错误;其二,连续多日点位完全不变(除节假日外)超过 5 个交易日,需确认是否为接口停更。

确认是数据错误后,修正方式优先采用“重新取数覆盖”,其次用相邻交易日线性插值,并在数据日志中记录修正日期、原值、新值和修正依据。若确认是真实的极端行情(如政策冲击导致的行业单日大跌),则应保留原值,仅在波动率计算时考虑使用稳健估计(如对收益率做 1% 缩尾),而不是删除该日数据。

五、一份可直接执行的数据准备检查清单

把上述要点固化为检查清单,可以在每次更新数据时快速过一遍,避免口径问题流入排名环节。

检查项 判定标准 不通过时的处理
分类体系唯一性 全样本期仅一套体系 重建映射表,禁止混用
日期对齐 与基准交易日历完全一致 重采样并对非交易日前向填充
缺失率 单行业缺失率 ≤ 5% 超限行业剔除并记录
极端涨跌 无未解释的 |涨跌幅| > 11% 回查公告或重新取数
预热期长度 ≥ 60 个交易日 向前延长取数区间
代码切换记录 切换日期与新旧代码可追溯 补录映射表生效区间

完成以上步骤后,得到的应是一张行索引为交易日、列索引为行业代码、数值为收盘点位的干净宽表,且每个行业都有足够长度的连续收益率序列。此时再计算日收益率 r_t = P_t / P_{t-1} - 1,并进入滚动波动率与强度得分的计算,口径才是可靠的。数据准备看似琐碎,但它决定了后续排名是“反映行业真实强弱”,还是“反映数据缺陷”。