3. 数据清洗与预处理:债券数据获取、缺失值处理、异常值检测、现金流计算

做收益率曲线构建,最怕什么?

不是模型不够复杂,而是数据本身就有问题。我见过太多人,上来就套用各种高级模型,结果曲线怎么调都不对。最后发现,是原始数据里混了一只「垃圾债」。

数据清洗,说白了就是给数据「洗澡」。把脏的、错的、不合理的统统去掉。这一步做不好,后面全是白费功夫。

核心原则:垃圾进,垃圾出(Garbage In, Garbage Out)。数据质量决定了曲线质量的底线。

3.1 债券数据获取:从哪里来,拿什么

我个人习惯,先把数据源分清楚。不同市场、不同品种,获取方式天差地别。

  • 交易所市场:上交所、深交所的固定收益平台。数据相对规范,但流动性差。
  • 银行间市场:中国外汇交易中心(CFETS)。这是主力市场,数据量大,但需要接口或数据商。
  • 数据服务商:Wind、Bloomberg、路透。最省心,但贵。
  • 公开数据:中债登、中证登的估值数据。免费,但更新频率低。

我在项目中遇到过一个问题:从Wind拉下来的数据,有些字段是空的。为什么?因为那是一只老券,已经停牌了。所以,获取数据时,一定要带上「数据状态」字段。

获取的核心字段,我列了个表:

字段 说明 重要性
债券代码 唯一标识 必填
债券名称 辅助识别 必填
发行日 起息日 必填
到期日 终止日 必填
票面利率 固定或浮动 必填
付息频率 年/半年/季 必填
中债估值 净价/全价 推荐
久期/凸性 风险指标 可选

小技巧:我建议把数据获取做成一个自动化脚本。每天定时跑一遍,把新券加进来,把到期券移出去。省得手工操作,容易出错。

3.2 缺失值处理:别瞎填,先问为什么

数据拿回来,一看,哎,好几个字段是空的。怎么办?

很多人第一反应是「填充」。用均值、用中位数、用前值。嗯,这其实很危险。

为什么会缺失?我总结了三种情况:

  1. 数据本身不存在:比如一只贴现债,就没有票面利率。这不是缺失,是合理空值。
  2. 数据获取失败:接口超时、网络断了。这种可以重试或补拉。
  3. 数据被剔除:比如某只债被交易所暂停交易,估值数据就没了。

处理方式,我一般这么干:

  • 合理空值:保留原样,在计算时做特殊判断。
  • 获取失败:重试3次,还不行就标记为「待人工核查」。
  • 被剔除:直接删除该记录。因为这只债已经不在交易池里了。

注意:千万不要用「0」去填充缺失的利率或价格。0在金融里是有意义的,你填个0,模型会以为这只债是零收益,曲线直接崩掉。

我曾经犯过一个错:用前一天的中债估值去填充今天的缺失值。结果那天正好是除息日,价格跳空了。我这一填充,把除息效应给抹掉了,曲线变得平滑得不像话。后来查了半天才发现问题。

3.3 异常值检测:揪出那些「捣乱分子」

异常值,就是那些明显不合理的数据点。比如一只10年期国债,收益率突然飙到10%。这要么是数据录入错误,要么是发生了极端事件。

怎么检测?我常用的方法有几种:

  • 3σ原则:假设数据服从正态分布,超出均值±3倍标准差的就是异常。简单粗暴,但有效。
  • 箱线图法:用四分位数(Q1、Q3)和四分位距(IQR)来界定。超出Q1-1.5*IQR或Q3+1.5*IQR的,算异常。
  • 业务规则法:这个最靠谱。比如,同期限国债收益率不可能比国开债高太多。如果出现这种情况,大概率是数据错了。

我个人偏爱业务规则法。为什么?因为金融数据有很强的逻辑约束。你想想看,一只AAA级企业债,收益率比同期限国债还低,这合理吗?不合理。那它就是异常。

下面是一个简单的异常检测代码示例:

# 基于业务规则的异常检测
def detect_outliers_by_rule(df):
    # 规则1:收益率不能为负(除非是负利率环境)
    if df['yield'].min() < 0:
        print("发现负收益率,请确认市场环境")
    
    # 规则2:同期限国债 vs 国开债利差
    spread = df[df['type']=='国债']['yield'] - df[df['type']=='国开债']['yield']
    if spread.mean() > 0.5:  # 利差超过50bp
        print("国债收益率高于国开债,可能存在异常")
    
    # 规则3:价格不能为0
    if (df['price'] == 0).any():
        print("存在价格为0的记录,请核查")
    
    return df

避坑指南:我曾经遇到过一个案例:一只债的收益率突然跳升,看起来像异常。但后来发现,是因为那天有大量抛盘,流动性枯竭导致的真实价格。所以,异常值不一定是错误值。要结合市场背景判断,别一刀切。

3.4 现金流计算:把未来每一笔钱算清楚

收益率曲线构建的核心,是债券的现金流。说白了,就是这只债在未来什么时候、给我多少钱。

计算现金流,需要知道几个关键参数:

  • 本金:面值,通常是100元。
  • 票面利率:每年付多少利息。
  • 付息频率:一年付几次。
  • 起息日、到期日:确定时间轴。

举个例子:一只3年期国债,票面利率3%,每年付息一次。那么它的现金流就是:

时间(年) 现金流(元)
1 3
2 3
3 103

看起来简单吧?但实际中,有很多坑。

比如,付息日遇到节假日怎么办?按惯例,顺延到下一个工作日。但不同市场规则不一样。中国银行间市场是「顺延」,交易所市场是「提前」还是「顺延」?得查规则。

再比如,半年付息的债券,现金流怎么算?每半年付一次,每次付一半的年利息。但要注意,有些债券是「实际/实际」计息,有些是「30/360」。计息规则不同,现金流金额会有微小差异。

下面是一个现金流计算的Python函数:

import pandas as pd
from datetime import datetime

def calculate_cashflows(issue_date, maturity_date, coupon_rate, frequency, face_value=100):
    """
    计算债券现金流
    :param issue_date: 起息日
    :param maturity_date: 到期日
    :param coupon_rate: 票面利率(小数形式,如0.03)
    :param frequency: 付息频率(1=年付,2=半年付,4=季付)
    :param face_value: 面值
    :return: DataFrame,包含日期和现金流
    """
    cashflows = []
    current_date = issue_date
    
    # 计算每期利息
    coupon_per_period = face_value * coupon_rate / frequency
    
    while current_date < maturity_date:
        # 计算下一个付息日
        if frequency == 1:
            next_date = current_date.replace(year=current_date.year + 1)
        elif frequency == 2:
            # 半年付,简单处理:加6个月
            month = current_date.month + 6
            year = current_date.year + (month - 1) // 12
            month = (month - 1) % 12 + 1
            next_date = current_date.replace(year=year, month=month)
        else:
            # 其他频率类似处理
            pass
        
        # 如果是到期日,加上本金
        if next_date == maturity_date:
            cashflows.append({'date': next_date, 'amount': coupon_per_period + face_value})
        else:
            cashflows.append({'date': next_date, 'amount': coupon_per_period})
        
        current_date = next_date
    
    return pd.DataFrame(cashflows)

注意:上面的代码是简化版。实际中要考虑节假日调整、闰年、计息基准(ACT/365, 30/360等)。我建议直接用现成的金融库,比如QuantLib,别自己造轮子。

现金流计算完成后,下一步就是把这些现金流「折现」到现在,算出债券的理论价格。然后跟市场价格对比,就能反推出收益率曲线了。

嗯,这部分内容比较多,我们后面会详细讲。今天先把数据清洗和现金流的基础打牢。


好了,数据清洗和预处理就讲到这里。记住,数据是根,根不正,苗必歪。花80%的时间在数据上,20%的时间建模,这是值得的。

本章核心要点:

  • 数据获取要明确来源和字段,自动化脚本是王道。
  • 缺失值先问原因,再决定是填充、删除还是保留。
  • 异常值检测用业务规则最靠谱,别迷信统计方法。
  • 现金流计算要关注节假日、计息规则等细节。

专注资料整理