4. 折溢价数据获取与清洗

折溢价率本身只是一个减法:二级市场成交价减去基金份额参考净值(IOPV),再除以IOPV。但真正决定后续轮动信号是否可信的,不是这个公式,而是喂进公式的两条数据序列是否干净、是否在同一时间点上对齐。本节围绕“取数—对齐—补缺—识异常”四个环节展开,目标是让读者能够独立产出一张可用于折溢价计算的ETF日频(或分钟频)数据表。

一、需要取哪些字段,从哪里取

计算折溢价至少需要四类字段:成交价(收盘价或指定时点最新价)、IOPV(交易所或行情商发布的盘中参考净值,通常为每秒或每三秒更新一次)、成交额(用于过滤流动性不足的样本)、以及基金规模或份额(用于判断套利容量)。此外建议一并取涨跌幅、成交量、买卖盘口一档,它们在异常值识别阶段非常有用。

数据来源大致分三档:交易所官方行情文件(如沪深交易所的行情快照与IOPV发布文件)、券商或Wind/Choice等终端接口、以及公开财经数据接口。三者的差异主要在IOPV的更新频率与历史可得性——很多免费接口只提供日频IOPV收盘值,而盘中折溢价监控必须使用分钟级IOPV。选源时要先明确研究频率:日频轮动用日收盘价与日终IOPV即可;日内轮动则必须确认IOPV是否为实时序列,否则折溢价曲线会严重失真。

IOPV是“参考”净值,由基金管理人按申赎清单估算,并非官方净值。它与当日真实净值可能存在偏差,尤其在成分股停牌、汇率波动或申赎清单调整时。因此折溢价率的绝对值应理解为“相对参考净值的偏离”,而不是精确的套利空间。

二、数据对齐:时间戳是第一道关

价格与IOPV来自不同发布通道,时间戳往往不一致。常见问题有三类:一是价格是逐笔或快照时间,IOPV是固定间隔推送,两者无法一一对应;二是收盘价的时间戳为15:00:00,而当日最后一个IOPV可能停在14:59:57;三是不同数据源使用的时区或交易日历不一致,导致节假日错位。

工程上的处理原则是“先统一频率,再对齐时点”。日频研究时,取每日最后一个有效IOPV与收盘价配对;分钟频研究时,将价格重采样到整分钟(取该分钟内最后成交价),IOPV取该分钟内最后一个推送值,然后按分钟时间戳做内连接。若某分钟内IOPV缺失,不要用前值无限填充,应记录缺失标记,后续单独处理。

# 示例:分钟级对齐(pandas,参数为教学假设)
px = px.set_index("ts").resample("1min").last()          # 价格取分钟内最后成交
iopv = iopv.set_index("ts").resample("1min").last()      # IOPV取分钟内最后推送
df = px.join(iopv, how="inner", lsuffix="_px", rsuffix="_iopv")
df["premium"] = df["close"] / df["iopv"] - 1
df["valid"] = df["iopv"].notna() & (df["iopv"] > 0)

注意 how="inner" 会丢弃任一侧缺失的分钟,这在开盘集合竞价阶段和尾盘可能损失样本。更稳妥的做法是先用 outer 合并,再对缺失情况分类统计,确认缺失集中在哪些时段,而不是直接静默丢弃。

三、缺失值处理:区分“没数据”和“不该有数据”

缺失值不能一刀切填充。要先把缺失分成三类:结构性缺失(停牌、非交易日、ETF尚未上市)、传输性缺失(接口超时、字段为空)、业务性缺失(该时段本就没有IOPV推送)。结构性缺失应保留为空并打标签,绝不能插值;传输性缺失可尝试重新拉取;业务性缺失在日频下可用当日最后有效值,在分钟频下应谨慎。

对折溢价计算而言,最危险的是用前值填充跨越了停牌或大幅波动区间。一个可操作的规则是:连续缺失不超过2个采样周期时允许前向填充,超过则标记为无效样本并从轮动信号中剔除。同时保留一列 fill_flag,记录每个点是否被填充过,便于事后审计。

四、异常值识别:折溢价率的“物理边界”

折溢价率出现异常,通常来自三类原因:价格错位(如把前一日收盘价配到今日IOPV)、IOPV异常(成分股停牌导致估值滞后)、以及流动性枯竭导致的瞬时偏离。识别时不要只看折溢价率本身,要结合成交额与盘口。

可用的判断条件包括:折溢价率绝对值超过阈值(例如±3%,需按ETF类型调整,跨境ETF因时差可放宽);成交额低于阈值(如日成交额小于500万元,说明价格不具代表性);以及折溢价率在相邻采样点间跳变超过设定幅度(如单分钟变化超过1%)。满足任一条件即标记为可疑,再人工或规则复核。

折溢价数据链路:从行情源到可用样本 行情源 交易所快照 券商/终端接口 公开数据接口 关键字段 成交价 / 收盘价 IOPV(参考净值) 成交额 / 成交量 / 盘口 时间对齐 统一频率(日/分钟) 取区间内最后有效值 按时间戳内连接 缺失与异常处理 结构性缺失→打标签 短缺口→前向填充+标记 超阈值→剔除并复核 折溢价率计算 premium = 价格 / IOPV − 1 异常样本 |折溢价| > 3% 成交额 < 500万 有效样本 进入轮动信号计算 保留 fill_flag 审计列 复核动作 核对停牌/汇率/清单 必要时回源重取

五、一个可复用的清洗检查表

把上述规则固化成检查表,每次更新数据后逐项过一遍,能显著降低“脏数据进模型”的概率。下表字段与阈值均为教学示例,实际使用时应按ETF类型(宽基、行业、跨境、商品)分别设定。

检查项判断条件(示例)处理动作
IOPV 是否为正IOPV ≤ 0 或为空标记无效,不参与计算
价格与IOPV日期一致两者交易日不同整行剔除,回源核对
折溢价率越界|premium| > 3%标记可疑,结合成交额复核
流动性不足日成交额 < 500 万元降权或剔除出轮动池
相邻点跳变单分钟变化 > 1%检查是否停牌或数据错位
连续缺失缺失 > 2 个采样周期不填充,标记为无效段

六、结果如何看:先看覆盖率,再看分布

清洗完成后,不要急着算轮动收益。先看两个指标:有效样本覆盖率(有效样本数 / 原始样本数)和折溢价率分布。覆盖率过低(例如低于90%)说明数据源或对齐逻辑有问题,应回到对齐环节排查;分布若出现明显双峰或长尾,往往意味着混入了不同频率或不同来源的数据,需要分段核查。只有在覆盖率与分布都合理之后,折溢价率才适合作为轮动信号的输入。

不要用“删除所有异常值”作为默认策略。部分异常值恰恰反映了真实的套利受限情形(如跨境ETF因时差出现的持续溢价),全部删除会让轮动信号系统性低估这类ETF的折溢价水平。正确做法是标记、分类,再决定是剔除、降权还是保留。