4. 数据获取与清洗:银行间市场数据源、Wind/QE接口、数据预处理

做银行间市场流动性分析,第一步就是搞定数据。说实话,这一步卡住了不少人。我见过太多分析师,模型建得漂漂亮亮,结果数据源没选对,或者清洗不到位,最后结论全是错的。今天咱们就把这块硬骨头啃下来。

4.1 银行间市场核心数据源

银行间市场的数据,说白了就三大类:行情数据、交易数据、衍生品数据。每一类都有它特定的获取渠道。

数据类别 典型指标 主要数据源 更新频率
货币市场 DR001、DR007、Shibor、R001 Wind、QE、中国货币网 日频
债券市场 国债收益率、信用利差、成交量 Wind、QE、中债登 日频/周频
衍生品市场 IRS利率、国债期货、利率互换 Wind、QE、CFETS 日频/分钟频
央行操作 OMO、MLF、TMLF、SLF 央行官网、Wind 事件驱动

我个人习惯,把数据源分成「官方源」和「商业源」。官方源比如中国货币网、中债登、上清所,数据最权威,但接口往往不太好用。商业源像Wind、QE,封装得好,但价格不菲。嗯,这里要注意:做量化回测时,尽量用商业源,因为数据连续性更好。

4.2 Wind接口实战

Wind在固收领域,几乎是标配。我用Python调Wind接口已经五六年了,踩过的坑能写本书。先看个最基础的例子:

import pandas as pd
from WindPy import w

# 启动Wind服务
w.start()

# 获取DR007历史数据
dr007 = w.wsd("DR007.IB", "close", "2024-01-01", "2024-12-31", "")
# 返回的是Wind特有的格式,需要转换
df_dr007 = pd.DataFrame(dr007.Data, index=dr007.Fields, columns=dr007.Times).T

# 获取Shibor 3M数据
shibor_3m = w.wsd("SHIBOR3M.IR", "close", "2024-01-01", "2024-12-31", "")
df_shibor = pd.DataFrame(shibor_3m.Data, index=shibor_3m.Fields, columns=shibor_3m.Times).T

print(df_dr007.head())
我的经验:Wind的wsd函数返回的数据结构有点反人类。我建议每次都先转成DataFrame,不然后面处理起来很痛苦。另外,记得先调w.start(),不然接口会报错——这个坑我刚开始时踩了不下十次。

为什么会这样?因为Wind底层是用C++写的,Python接口只是封装。所以数据格式跟pandas原生习惯不太一样。你想想看,如果每次都要手动转格式,多麻烦。所以我写了个小工具函数:

def wind_to_df(data):
    """将Wind数据转换为标准DataFrame"""
    if data.ErrorCode != 0:
        print(f"Error: {data.ErrorCode}")
        return None
    df = pd.DataFrame(data.Data, index=data.Fields, columns=data.Times).T
    df.index = pd.to_datetime(df.index)
    return df

# 使用示例
df_dr007 = wind_to_df(w.wsd("DR007.IB", "close", "2024-01-01", "2024-12-31", ""))

4.3 QE接口实战

QE是后来崛起的金融数据平台,在债券数据方面有独特优势。尤其是信用债的估值数据,QE比Wind更全。我记得有一次做信用利差分析,Wind上缺了不少AA+级城投债的数据,换成QE就全了。

import qe

# 初始化QE客户端
qe_client = qe.QEClient()

# 获取国债收益率曲线
treasury_yields = qe_client.get_yield_curve(
    bond_type="treasury",
    start_date="2024-01-01",
    end_date="2024-12-31",
    tenors=["1Y", "3Y", "5Y", "10Y"]
)

# 获取IRS利率
irs_data = qe_client.get_irs_rate(
    currency="CNY",
    start_date="2024-01-01",
    end_date="2024-12-31",
    tenors=["1Y", "5Y"]
)

print(treasury_yields.head())
注意:QE的API设计跟Wind完全不同。它更偏向RESTful风格,返回的是JSON格式。如果你同时用两个平台,建议统一封装一层,避免代码里到处是if-else判断数据源。

4.4 数据预处理——避坑指南

数据拿到手,别急着分析。预处理这步,我吃过太多亏了。曾经有一次,因为没处理节假日缺失值,导致流动性指标计算出了大问题,被领导叫去喝茶。从那以后,我总结了一套标准流程:

4.4.1 缺失值处理

银行间市场的数据,节假日必然缺失。还有的时候,某些债券当天没成交,也会缺失。处理方法要分情况:

  • 利率类数据(DR、Shibor):用前值填充。因为利率有粘性,不会突然跳变。
  • 成交量类数据:用0填充。没成交就是0,别自作聪明插值。
  • 收益率曲线数据:用插值法。中债登每天都会发布曲线,但如果你自己算,可以用线性插值。
# 缺失值处理示例
def fill_missing_data(df, method="ffill"):
    """
    根据数据类型选择填充方法
    method: 'ffill' 前向填充, 'zero' 零填充, 'interpolate' 插值
    """
    if method == "ffill":
        return df.fillna(method='ffill')
    elif method == "zero":
        return df.fillna(0)
    elif method == "interpolate":
        return df.interpolate(method='linear')
    else:
        raise ValueError("不支持的填充方法")

# 利率数据用前向填充
df_rates_filled = fill_missing_data(df_dr007, "ffill")
# 成交量用零填充
df_volume_filled = fill_missing_data(df_volume, "zero")

4.4.2 异常值检测

银行间市场偶尔会有「乌龙指」或者数据录入错误。比如DR007突然飙到10%,明显不合理。我一般用两种方法:

  1. 3-sigma法则:超过均值±3倍标准差的数据,标记为异常。
  2. IQR方法:超过Q1-1.5*IQR或Q3+1.5*IQR的数据,标记为异常。
def detect_outliers_iqr(data, column):
    """用IQR方法检测异常值"""
    Q1 = data[column].quantile(0.25)
    Q3 = data[column].quantile(0.75)
    IQR = Q3 - Q1
    
    lower_bound = Q1 - 1.5 * IQR
    upper_bound = Q3 + 1.5 * IQR
    
    outliers = data[(data[column] < lower_bound) | (data[column] > upper_bound)]
    return outliers

# 检测DR007异常值
outliers = detect_outliers_iqr(df_dr007, "close")
print(f"发现 {len(outliers)} 个异常值")
核心原则:异常值不要直接删除。先确认是不是数据错误,如果是市场真实波动(比如钱荒时期),保留并标记。我一般会在数据框里加一列"is_outlier",而不是直接删行。

4.4.3 日期对齐与重采样

不同数据源的日期可能不一致。Wind用交易日历,QE用自然日,央行操作又是事件驱动。做分析前,必须统一时间轴。

# 统一到交易日历
def align_to_trading_calendar(df, calendar="CHINA_IB"):
    """
    对齐到银行间市场交易日历
    calendar: 'CHINA_IB' 银行间, 'CHINA_STOCK' 交易所
    """
    from pandas.tseries.offsets import CustomBusinessDay
    from pandas.tseries.holiday import AbstractHolidayCalendar
    
    # 这里简化处理,实际需要加载完整的交易日历
    trading_days = pd.date_range(start=df.index.min(), 
                                 end=df.index.max(), 
                                 freq='B')  # 仅做示例
    df_aligned = df.reindex(trading_days)
    return df_aligned

# 重采样到周频
df_weekly = df_dr007.resample('W').mean()

4.5 数据质量检查清单

我每次做完数据清洗,都会过一遍这个清单。你可以直接拿去用:

检查项 检查方法 常见问题
缺失率 df.isnull().sum() / len(df) 节假日缺失、数据源中断
重复值 df.duplicated().sum() 接口重复调用、数据合并错误
边界值 df.describe() 查看min/max 负利率、异常高值
时间连续性 检查日期是否连续 跳空、停牌日未处理
跨源一致性 对比Wind和QE同指标 计算口径不同、四舍五入差异
一个小技巧:我习惯在清洗完数据后,画一张简单的分布图。肉眼扫一眼,比任何统计指标都直观。如果发现某个指标分布形状很奇怪,八成是数据有问题。

4.6 本章知识体系

下面这张图,是我自己梳理的数据获取与清洗流程。你可以把它当作操作手册:

数据获取与清洗流程 数据源层 Wind接口 QE接口 官方源(货币网/中债登) CSV/Excel 数据清洗层 缺失值处理 前向填充 / 零填充 / 插值 异常值检测 3-sigma / IQR方法 日期对齐 交易日历 / 重采样 质量检查层 缺失率检查 重复值检查 边界值检查 跨源一致性 清洗后的标准数据集

这张图把整个流程分成了三层。数据源层解决「从哪拿」的问题,清洗层解决「怎么处理」的问题,质量检查层解决「对不对」的问题。我建议你每次做数据准备时,都按这个流程走一遍,能省掉很多返工的麻烦。

好了,数据获取和清洗这块,核心内容就这些。记住一句话:垃圾进,垃圾出。数据质量不过关,后面再漂亮的模型都是白搭。下一章咱们会用到今天清洗好的数据,来做流动性指标的构建和可视化。


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