利率互换(IRS)定价:从条款到Python实现
利率互换,圈里人常叫它IRS。说白了,就是两方约定好,在未来一段时间内交换利息现金流。一方按固定利率付,另一方按浮动利率付。本金?不交换,只是个名义本金。
我在2015年刚接触IRS时,觉得这东西不就是个对赌嘛。后来真正上手做定价才发现,里面的弯弯绕绕真不少。今天咱们就把IRS定价的底裤扒干净。
IRS标准条款:你得知道的几个关键点
一份标准的IRS合约,通常包含这些要素:
- 名义本金(Notional):计算利息的基础,但不交换
- 固定利率(Fixed Rate):支付方按这个利率付钱
- 浮动利率指数(Floating Index):比如3个月Shibor、6个月Libor
- 支付频率(Payment Frequency):每季度、每半年等
- 计息基准(Day Count Convention):比如Act/360、Act/365
- 起息日与到期日:整个互换的生命周期
核心要点:IRS的现金流是净额结算的。也就是说,到期日只付差额,不付全额。这大大降低了信用风险。
举个例子。假设你签了一个5年期IRS,名义本金1亿,你收固定利率3.5%,付3个月Shibor。每季度交换一次利息。那么每个季度,你都会收到固定端利息,同时付出浮动端利息。净额就是两者之差。
固定端估值:说白了就是求现值
固定端的现金流是确定的。每期支付的利息 = 名义本金 × 固定利率 × 计息期限。然后把这些现金流用折现因子折现到今天。
公式长这样:
PV_fixed = Σ (N × R_fixed × τ_i × DF_i)
其中:
- N = 名义本金
- R_fixed = 固定利率
- τ_i = 第i期的计息期限(年化)
- DF_i = 第i期的折现因子
折现因子从哪来?从零息曲线上来。我习惯用Bootstrapping方法从市场报价中提取零息曲线。嗯,这块后面讲曲线构建时会细说。
浮动端估值:没那么简单
浮动端的现金流是不确定的,因为它挂钩市场利率。但有个巧妙的方法:在重置日,浮动端的现值等于名义本金。
为什么会这样?你想想看,在重置日,浮动利率被设定为当前市场利率,那么未来所有浮动利息的现值,恰好等于名义本金。这是无套利原理的体现。
所以浮动端的估值公式是:
PV_float = N × (DF_start - DF_end)
其中DF_start是起息日的折现因子,DF_end是到期日的折现因子。这个公式简洁得让人拍大腿。
我的经验:在实际项目中,浮动端估值最容易出错的地方是计息基准和支付日期的对齐。我曾经因为Act/360和Act/365没搞对,导致估值差了十几万。嗯,从那以后我再也不敢忽略Day Count Convention了。
互换定价公式:固定端减浮动端
IRS的价值,就是固定端现值减去浮动端现值:
V_IRS = PV_fixed - PV_float
如果V_IRS > 0,说明你收固定利率的头寸是赚钱的。反之则亏钱。
那公平的互换利率怎么定?就是让V_IRS = 0的那个固定利率。解方程:
Σ (N × R_swap × τ_i × DF_i) = N × (DF_start - DF_end)
化简得:
R_swap = (DF_start - DF_end) / Σ (τ_i × DF_i)
这个R_swap就是市场公允的互换利率。你拿它跟市场报价一比,就知道交易划不划算了。
Python实现IRS定价
光说不练假把式。咱们直接上代码。
import numpy as np
from scipy.interpolate import interp1d
class IRS_Pricer:
def __init__(self, notional, fixed_rate, floating_index,
start_date, end_date, pay_freq, day_count='Act/360'):
self.notional = notional
self.fixed_rate = fixed_rate
self.floating_index = floating_index
self.start_date = start_date
self.end_date = end_date
self.pay_freq = pay_freq
self.day_count = day_count
def generate_payment_dates(self):
"""生成支付日期列表"""
# 简化实现:假设按季度支付
dates = []
current = self.start_date
while current < self.end_date:
current = current + pd.DateOffset(months=3)
dates.append(current)
return dates
def get_discount_factors(self, dates, curve):
"""从曲线上获取折现因子"""
# curve是(期限, 折现因子)的插值函数
t = [(d - self.start_date).days / 360 for d in dates]
return curve(t)
def price_fixed_leg(self, curve):
"""定价固定端"""
dates = self.generate_payment_dates()
dfs = self.get_discount_factors(dates, curve)
pv = 0
for i, (d, df) in enumerate(zip(dates, dfs)):
# 计息期限
if i == 0:
tau = (d - self.start_date).days / 360
else:
tau = (d - dates[i-1]).days / 360
pv += self.notional * self.fixed_rate * tau * df
return pv
def price_float_leg(self, curve):
"""定价浮动端"""
# 使用简化公式:N * (DF_start - DF_end)
t_start = 0
t_end = (self.end_date - self.start_date).days / 360
df_start = curve([t_start])[0]
df_end = curve([t_end])[0]
return self.notional * (df_start - df_end)
def price(self, curve):
"""计算IRS价值"""
pv_fixed = self.price_fixed_leg(curve)
pv_float = self.price_float_leg(curve)
return pv_fixed - pv_float
def fair_swap_rate(self, curve):
"""计算公平互换利率"""
dates = self.generate_payment_dates()
dfs = self.get_discount_factors(dates, curve)
# 计算年金因子
annuity = 0
for i, (d, df) in enumerate(zip(dates, dfs)):
if i == 0:
tau = (d - self.start_date).days / 360
else:
tau = (d - dates[i-1]).days / 360
annuity += tau * df
t_end = (self.end_date - self.start_date).days / 360
df_start = curve([0])[0]
df_end = curve([t_end])[0]
return (df_start - df_end) / annuity
# 使用示例
if __name__ == "__main__":
# 构造一条简单的零息曲线
tenors = np.array([0, 0.25, 0.5, 1, 2, 3, 4, 5])
rates = np.array([0.02, 0.022, 0.024, 0.025, 0.027, 0.028, 0.029, 0.03])
dfs = np.exp(-rates * tenors) # 连续复利折现
curve = interp1d(tenors, dfs, kind='linear', fill_value='extrapolate')
# 定价一个5年期IRS
pricer = IRS_Pricer(
notional=100_000_000,
fixed_rate=0.035,
floating_index='3M_Shibor',
start_date=pd.Timestamp('2024-01-01'),
end_date=pd.Timestamp('2029-01-01'),
pay_freq='quarterly'
)
value = pricer.price(curve)
fair_rate = pricer.fair_swap_rate(curve)
print(f"IRS价值: {value:,.2f}")
print(f"公平互换利率: {fair_rate:.4%}")
避坑指南:
- 折现曲线必须与浮动利率指数一致。比如用Shibor定价,就别用国债曲线。
- 支付日期调整要考虑节假日。我见过因为忽略节假日调整,导致现金流错配的案例。
- 计息基准一定要对齐。固定端和浮动端可能用不同的Day Count。
知识体系总览
下面这张图,把IRS定价的核心逻辑串起来了。建议你保存下来,以后做定价时对照着看。
这张图把IRS定价拆成了三个层次:输入层(市场数据+合约参数)、计算层(现金流生成+折现)、输出层(固定端/浮动端现值+公平利率)。每次做定价时,按这个流程走,基本不会漏东西。
我的习惯:在实际项目中,我会把曲线构建和IRS定价拆成两个独立的模块。曲线构建负责从市场报价生成折现因子,IRS定价模块只管用这些因子算现值。这样测试和维护都方便。
好了,IRS定价的核心内容就这些。从标准条款到固定端浮动端估值,再到Python实现,咱们都过了一遍。记住那个核心公式:V_IRS = PV_fixed - PV_float。剩下的就是细节了。
公众号:蓝海资料掘金营,微信deep3321