第27章 曲面数据源与清洗:从Bloomberg、Reuters获取数据,异常值处理

做波动率曲面,最头疼的其实不是模型本身。而是——数据。

我见过太多团队,模型跑得飞起,结果一上实盘就崩。为什么?数据源没搞干净。说白了,你喂进去的是垃圾,出来的只能是更垃圾的预测。今天这章,我就把我在Bloomberg和Reuters上踩过的坑,一个一个说清楚。

27.1 数据源的选择:BBG vs. Reuters

先说说两个主流数据源。我个人习惯是:能用BBG就用BBG。为什么?

  • Bloomberg(BBG):数据覆盖全,更新快,但贵。一个终端一年几万美金。适合机构。
  • Reuters(Refinitiv):便宜一些,但某些小众品种的数据质量参差不齐。我曾在Reuters上抓到过某只港股期权数据,连续三天都是同一个数——明显是没更新。

你想想看,如果数据源本身就有问题,后面的清洗再努力也白搭。所以第一步,选对源。

27.2 数据字段:你到底需要什么?

很多人一上来就拉一堆字段,什么Delta、Gamma、Vega全要。其实没必要。做曲面,核心字段就这几个:

字段 说明 来源
Strike 行权价 BBG/Reuters
Expiry 到期日 BBG/Reuters
Bid/Ask 买卖报价 BBG/Reuters
Implied Vol 隐含波动率 BBG/Reuters
Volume/Open Interest 成交量/持仓量 BBG/Reuters

嗯,这里要注意:隐含波动率这个字段,不同数据源的计算方式可能不一样。BBG用的是标准BS模型,Reuters有时候会用自己调整过的模型。所以如果你同时用两个源,一定要做交叉验证。

27.3 数据清洗:异常值处理

数据拉下来之后,第一件事不是建模,而是清洗。我遇到过最离谱的一次:某只股票的期权数据里,居然有一个行权价是负数。你想想看,行权价怎么可能为负?明显是数据录入错误。

异常值处理,我一般分三步走:

27.3.1 基础过滤

先把明显不合理的数据干掉:

  • 行权价 ≤ 0
  • 隐含波动率 ≤ 0 或 ≥ 200%
  • 买卖价差过大(比如Bid/Ask ratio > 5)
  • 成交量 = 0 且 持仓量 = 0

这些规则看起来简单,但能过滤掉至少80%的脏数据。

27.3.2 统计方法

基础过滤之后,再用统计方法揪出那些「看起来正常但实际异常」的数据。我常用的是Z-scoreIQR

# 伪代码示例:Z-score异常检测
import numpy as np

def detect_outliers_zscore(data, threshold=3):
    z_scores = np.abs((data - np.mean(data)) / np.std(data))
    return data[z_scores > threshold]

# 对隐含波动率做Z-score检测
iv_outliers = detect_outliers_zscore(iv_data)
print(f"发现 {len(iv_outliers)} 个异常波动率数据")

我曾经用这个方法,在Reuters的日元期权数据里抓到了一批「幽灵数据」——那些数据点明明在统计上正常,但实际是某个交易员手动输入的测试数据。Z-score一跑,全现形了。

27.3.3 业务逻辑校验

这一步最容易被忽略。统计上正常的数据,业务上不一定合理。举个例子:

  • 深度虚值期权的隐含波动率突然飙升——可能是流动性不足导致的报价失真
  • 临近到期的期权,隐含波动率异常平稳——可能是数据源没更新
  • 同一到期日、不同行权价的波动率出现「锯齿」状——可能是数据抓取时间不一致

对于这些情况,我一般会加一个平滑性检查

# 检查同一到期日的波动率是否平滑
def check_smoothness(vol_surface, tolerance=0.1):
    for expiry in vol_surface.keys():
        strikes = sorted(vol_surface[expiry].keys())
        vols = [vol_surface[expiry][k] for k in strikes]
        for i in range(1, len(vols)-1):
            # 检查相邻点变化是否超过容忍度
            if abs(vols[i] - vols[i-1]) > tolerance:
                print(f"警告:到期日{expiry},行权价{strikes[i]}附近波动率异常")

27.4 数据对齐:时间戳与到期日

数据清洗完了,还有一个坑:时间对齐

BBG和Reuters的数据更新时间不一样。BBG是实时推送,Reuters是每5秒刷新一次。如果你同时用两个源,一定要把时间戳对齐到同一个时间点。否则你拿到的曲面,左边是10:00:00的数据,右边是10:00:05的数据,拼在一起就是错的。

我个人的做法是:统一用BBG的时间戳,然后把Reuters的数据做线性插值对齐到BBG的时间点上。

27.5 数据存储:别用Excel

最后说一句:数据存哪里?千万别用Excel。我见过有人把几万条期权数据存在一个Excel文件里,打开一次要5分钟,还经常崩溃。

建议用HDF5或者Parquet格式。这两种格式对时间序列数据支持好,读写快,而且支持压缩。我自己用的是HDF5,配合Pandas的to_hdf()方法,几百万条数据秒级读写。

核心要点总结:

  • 数据源选BBG优先,Reuters做补充
  • 清洗三步走:基础过滤 → 统计检测 → 业务校验
  • 时间对齐是曲面构建的关键,别忽略
  • 存储用HDF5或Parquet,别用Excel

我的小技巧:每次拉完数据,先画一张散点图看看。肉眼扫一遍,很多异常值一眼就能看出来。别完全依赖代码,人的直觉有时候比算法管用。

警告:千万不要在数据清洗阶段「过度拟合」。我见过有人把异常值全删了,结果曲面变得异常平滑,但实际交易时完全对不上。保留一些「合理」的异常,反而能反映真实市场情况。

波动率曲面数据清洗流程 Bloomberg 实时推送 Reuters 5秒刷新 数据合并 时间戳对齐 步骤1:基础过滤 步骤2:统计检测 步骤3:业务校验 清洗后的波动率曲面数据 存储为 HDF5 / Parquet 格式 异常值处理 Z-score / IQR

公众号:蓝海资料掘金营,微信deep3321