第1章:远期利率协议(FRA)定价
各位同学,咱们今天聊聊FRA。说实话,我在刚入行那会儿,觉得FRA就是个简单的远期合约,没什么技术含量。直到有一次帮客户做一笔大额FRA报价,因为忽略了几个细节,差点让公司亏了六位数。从那以后,我对FRA的定价公式有了全新的认识。
1.1 FRA合约机制
远期利率协议,说白了就是一份锁定未来某段时间利率的合约。你想想看,如果你是一家公司的财务总监,三个月后要借一笔钱,你担心利率上涨怎么办?签一份FRA,把利率锁死。
FRA的核心要素包括:
- 名义本金:不实际交换,只用于计算利息差额
- 合约期限:比如3×6 FRA,表示3个月后开始的3个月期利率
- 合约利率:双方约定的固定利率
- 参考利率:通常是LIBOR或SHIBOR
- 结算方式:到期时支付净额
关键点:FRA的结算是在合约开始日(即远期起始日)进行的,而不是在贷款到期日。这一点我见过太多新手搞混了。
我曾经帮一家企业做FRA对冲,他们以为FRA和远期利率互换是一回事。其实差别很大——FRA只覆盖一个利率期,而利率互换覆盖多个连续的利率期。
1.2 定价公式推导
FRA的定价逻辑其实很直观。假设我们有一个3×6 FRA,合约利率为K,参考利率为L。在结算日,买方收到的现金流为:
结算金额 = 名义本金 × (L - K) × 期限 / (1 + L × 期限)
为什么要除以(1 + L × 期限)?因为结算是在期初进行的,而利息差额是在期末才产生的,所以需要折现到期初。嗯,这里要注意,不同市场的折现方式略有不同。
定价的核心是找到使FRA价值为零的合约利率K。根据无套利原理,这个K应该等于远期利率。推导过程如下:
- 假设即期利率曲线已知,R(T1)和R(T2)分别对应期限T1和T2的即期利率
- 远期利率F满足:(1 + R(T1)×T1) × (1 + F×(T2-T1)) = 1 + R(T2)×T2
- 解出F = [ (1 + R(T2)×T2) / (1 + R(T1)×T1) - 1 ] / (T2-T1)
这个公式看起来简单,但实际应用中坑不少。我记得有一次,我用连续复利公式算出来的结果和市场上看到的报价差了十几个基点,查了半天才发现是计息天数的问题。
个人经验:在实际项目中,我习惯用ACT/360的计息惯例来计算FRA。因为大多数货币市场产品都采用这个惯例,包括LIBOR。如果你用ACT/365,结果会略有偏差。
1.3 Python实现FRA定价
好了,理论讲完了,咱们直接上代码。我写了一个完整的FRA定价类,包含了从即期利率曲线计算FRA价格的所有逻辑。
import numpy as np
from datetime import datetime, timedelta
class FRAPricer:
"""FRA定价器"""
def __init__(self, spot_rates, spot_tenors):
"""
初始化即期利率曲线
spot_rates: 即期利率数组
spot_tenors: 即期利率对应的期限(年)
"""
self.spot_rates = np.array(spot_rates)
self.spot_tenors = np.array(spot_tenors)
def interpolate_rate(self, tenor):
"""线性插值获取任意期限的即期利率"""
return np.interp(tenor, self.spot_tenors, self.spot_rates)
def forward_rate(self, t1, t2):
"""
计算远期利率
t1: 远期起始时间(年)
t2: 远期结束时间(年)
"""
r1 = self.interpolate_rate(t1)
r2 = self.interpolate_rate(t2)
# 使用ACT/360计息惯例
df1 = 1 / (1 + r1 * t1)
df2 = 1 / (1 + r2 * t2)
forward = (df1 / df2 - 1) / (t2 - t1)
return forward
def price_fra(self, notional, t1, t2, strike_rate, is_payer=True):
"""
定价FRA
notional: 名义本金
t1: 远期起始时间
t2: 远期结束时间
strike_rate: 合约利率
is_payer: True表示支付固定利率,False表示收取固定利率
"""
forward = self.forward_rate(t1, t2)
discount_factor = 1 / (1 + forward * (t2 - t1))
# 结算金额
settlement = notional * (forward - strike_rate) * (t2 - t1) * discount_factor
if not is_payer:
settlement = -settlement
return settlement, forward
# 示例使用
spot_rates = [0.02, 0.025, 0.03, 0.035] # 即期利率
spot_tenors = [0.25, 0.5, 1.0, 2.0] # 对应期限
pricer = FRAPricer(spot_rates, spot_tenors)
# 定价3×6 FRA(3个月后开始的3个月期FRA)
notional = 1000000 # 100万
t1 = 0.25 # 3个月
t2 = 0.5 # 6个月
strike = 0.028 # 2.8%
value, fwd = pricer.price_fra(notional, t1, t2, strike, is_payer=True)
print(f"远期利率: {fwd:.4%}")
print(f"FRA价值: ${value:.2f}")
避坑指南:我曾经在写这个类的时候,忘记考虑计息天数的问题。直接用年份差算,结果和Bloomberg上的报价差了整整5个基点。后来才发现,不同货币的计息惯例不一样——美元用ACT/360,英镑用ACT/365,欧元用30/360。这个细节一定要记住。
1.4 市场惯例与报价
在实际市场中,FRA的报价方式和我们刚才讲的略有不同。市场报价通常采用以下惯例:
| 参数 | 市场惯例 | 说明 |
|---|---|---|
| 报价方式 | Bid/Ask | 做市商报出买卖双边价格 |
| 期限表示 | 3×6, 6×9, 1×4等 | 第一个数字表示远期起始月数,第二个表示结束月数 |
| 计息惯例 | ACT/360(美元) | 不同货币有差异 |
| 结算方式 | 期初净额结算 | 在远期起始日支付 |
| 参考利率 | LIBOR(正在被替代) | SOFR、SONIA等替代利率 |
说到市场惯例,我想起一个有意思的事。以前做FRA交易的时候,报价员报的是"3×6 2.80-2.85",意思是做市商愿意以2.80%买入(支付固定利率),以2.85%卖出(收取固定利率)。新手很容易搞反买卖方向。
重要提示:随着LIBOR退出历史舞台,FRA的参考利率正在向无风险利率(RFR)过渡。比如美元FRA现在可以用SOFR作为参考利率。这个变化对定价模型的影响不小,因为SOFR是隔夜利率,而LIBOR是期限利率,两者的期限结构不同。
另外,FRA的报价通常以基点为单位。比如报价2.80%,实际上会说"280个基点"。这个习惯在交易员之间很常见,但如果你和风控部门沟通,最好还是用百分比,避免误解。
知识体系结构图
这张图把FRA定价的知识体系梳理清楚了。从合约机制出发,分三条线展开:定价公式推导、Python实现、市场惯例。这三条线最终汇聚到同一个目标——实现FRA的准确定价。
我个人觉得,学习FRA定价最好的方法就是动手写代码。光看公式和理论,你永远不知道实际应用中会遇到什么问题。比如我刚才提到的计息天数问题,不写代码根本不会注意到。
实用建议:如果你刚开始学FRA定价,我建议你先用Excel验证一下公式。把即期利率、远期利率、FRA价值都算一遍,确认逻辑正确了再写Python代码。这样能避免很多低级错误。
好了,FRA定价的基础内容就讲到这里。记住,FRA虽然看起来简单,但它是理解更复杂利率衍生品(如利率互换、利率期权)的基础。把FRA吃透了,后面的内容会轻松很多。