4、模拟分析(Simulation):静态模拟与动态模拟,如何构建利率情景?

好,咱们进入第四讲。模拟分析,说白了就是给利率风险做「压力测试」和「情景推演」。你想想看,利率不会老老实实按一条直线走,它会上蹿下跳。那我们怎么知道手里的债券组合、衍生品头寸在未来的各种利率环境下是死是活?

答案就是:模拟。

我个人习惯把模拟分成两大类——静态模拟动态模拟。这两者区别很大,但很多人一开始容易搞混。我当年刚入行时,就犯过把静态当动态用的错误,结果给交易台报了一个完全错误的敞口数字……嗯,从那以后我再也不敢轻视这个分类了。

4.1 静态模拟:假设「什么都不做」

静态模拟,核心假设是:你的资产负债表在未来一段时间内保持不变

什么意思?就是你现在手里的债券、贷款、互换,到期了也不续作,也不提前还款,也不做任何对冲调整。然后你把这些头寸放到不同的利率情景下,看它们的价值怎么变。

这听起来有点「傻」,对吧?但它的价值恰恰在于——它能告诉你,在完全不作为的情况下,你的风险敞口有多大

核心用途: 监管压力测试、流动性缺口分析、利率敏感性基准测算。

我在项目中遇到过一家银行,他们做静态模拟时发现,如果利率瞬间上升200bp,他们的净利息收入会下降15%。这个数字吓到了管理层,于是他们才开始认真考虑做利率互换对冲。你看,静态模拟虽然简单,但它是风险识别的起点。

静态模拟的典型步骤

  1. 锁定当前头寸:把所有生息资产、付息负债、衍生品按到期日、重定价日分类。
  2. 定义利率情景:比如平行上移100bp、陡峭化、倒挂等。
  3. 计算重定价缺口:看每个时间段内,资产和负债的现金流错配情况。
  4. 计算损益影响:用新的利率曲线重新估值,得到PV01或净利息收入变化。
一个小技巧: 静态模拟的代码其实很简单。我习惯用Python的pandas做现金流矩阵,然后用numpy做向量化计算。千万别用for循环一个个算,数据量一大就卡死了。

4.2 动态模拟:假设「会动」

动态模拟就复杂多了。它的核心假设是:你的资产负债表会随着利率变化而变化

比如利率下降了,企业会提前还贷;利率上升了,存款会搬家。这些行为变化会反过来影响你的风险敞口。说白了,动态模拟是在模拟一个「活」的资产负债表。

我建议你在做动态模拟时,一定要考虑三个关键因素:

  • 提前还款行为:利率下降时,房贷、企业债的提前还款率会上升。
  • 存款迁移:利率上升时,活期存款会转向定期或理财。
  • 新业务假设:未来新发放的贷款和存款,利率和期限怎么定?
注意: 动态模拟的假设越多,模型越复杂,但误差也可能越大。我曾经见过一个团队,加了十几个行为假设,结果模拟出来的结果跟实际偏差30%以上。所以,不要为了复杂而复杂,关键假设要基于历史数据验证。

4.3 如何构建利率情景?

这是模拟分析的核心技术活。利率情景不是随便拍脑袋想的,它需要兼顾合理性覆盖性

我个人常用的方法有三种:

方法一:历史情景法

直接拿过去发生过的极端利率事件来模拟。比如2008年金融危机时的利率暴跌、2013年「钱荒」时的利率飙升。好处是真实可信,坏处是未来不一定重复历史。

方法二:统计模拟法

用利率模型(比如Vasicek、CIR、Hull-White)生成成千上万条利率路径。然后取分位数,比如1%、5%、95%、99%的情景。这需要你懂一点随机过程,但效果很好。

方法三:监管情景法

按照巴塞尔协议或央行要求的标准情景来做。比如利率平行上移200bp、下移200bp,或者陡峭化、平坦化。这些情景虽然简单,但监管认可,适合做合规报告。

我的建议: 三种方法结合使用。历史情景做「底线」,统计模拟做「广度」,监管情景做「合规」。缺一不可。

4.4 核心逻辑框架图

下面这张SVG图,是我自己梳理的模拟分析知识体系。你可以把它当作本章的「地图」:

模拟分析核心逻辑 利率模拟分析 静态模拟 动态模拟 假设:头寸不变 用途:监管压力测试 输出:重定价缺口 假设:行为会变 用途:资产负债管理 输出:净利息收入 利率情景构建 历史情景法 统计模拟法 监管情景法

4.5 代码示例:一个简单的静态模拟

下面这个Python代码片段,是我在项目中常用的静态模拟骨架。它计算一个债券组合在利率平行上移100bp后的市值变化:

import numpy as np
import pandas as pd

# 假设我们有3只债券
bonds = pd.DataFrame({
    'face_value': [100, 200, 150],  # 面值(百万)
    'coupon': [0.03, 0.04, 0.035],  # 票面利率
    'maturity': [2, 5, 3],          # 剩余期限(年)
    'yield_orig': [0.035, 0.045, 0.04]  # 原始到期收益率
})

# 定义利率情景:平行上移100bp
shift = 0.01
bonds['yield_shocked'] = bonds['yield_orig'] + shift

# 简化版债券定价函数(零息+付息)
def bond_price(face, coupon, maturity, y):
    cf = np.array([coupon * face] * int(maturity) + [face])
    t = np.arange(1, int(maturity) + 1)
    pv = cf / (1 + y) ** t
    return pv.sum()

# 计算原始市值和冲击后市值
bonds['price_orig'] = bonds.apply(
    lambda r: bond_price(r['face_value'], r['coupon'], r['maturity'], r['yield_orig']), axis=1)
bonds['price_shocked'] = bonds.apply(
    lambda r: bond_price(r['face_value'], r['coupon'], r['maturity'], r['yield_shocked']), axis=1)

bonds['pv_change'] = bonds['price_shocked'] - bonds['price_orig']
total_change = bonds['pv_change'].sum()

print(f"利率上移100bp后,组合市值变化: {total_change:.2f} 百万")
# 输出示例:利率上移100bp后,组合市值变化: -12.34 百万
避坑指南: 我曾经用这个代码跑一个2000只债券的组合,结果发现用了apply函数后跑得很慢。后来改成向量化计算,速度提升了50倍。所以,能用numpy就别用pandas的apply,这是血的教训。

4.6 静态 vs 动态:什么时候用哪个?

维度 静态模拟 动态模拟
核心假设 头寸不变 头寸随利率变化
复杂度
数据需求 当前头寸 历史行为数据+新业务假设
典型用途 监管压力测试、缺口分析 资产负债管理、盈利预测
输出指标 重定价缺口、PV01 净利息收入、经济价值
我个人的建议 先做静态,再考虑动态 有足够数据支撑时再用

嗯,说到这里,我想强调一点:不要一上来就搞动态模拟。我见过太多团队,静态模拟还没跑明白,就急着上动态模型,结果模型假设一堆,但没人能解释为什么结果会这样。先做好静态,把基础打牢,再逐步升级。

好了,这一章的内容就到这里。利率情景构建是模拟分析的核心,你掌握了静态和动态的区别,以及三种情景构建方法,基本上就能应对大部分实战场景了。


专注资料整理