由平价利差、标准 coupon 与 risky PV01 计算 CDS upfront 费
CDS Upfront Fee from Par Spread, Standard Coupon, and Risky PV01
开始编码实现 solution(par_spread_bps: float, standard_coupon_bps: float, risky_pv01_per_million: float, notional_million: float) -> float。某信贷交易台每天跑一个脚本,给在册的单名 CDS 合约定价 upfront 现金腿,把结果写进信用交易看板的「upfront 费」那一列。给定市场反推出的平价利差 par_spread_bps、合约固定标准 coupon standard_coupon_bps(北美 ISDA 惯例下 IG 名义通常 100bp、HY 通常 500bp)、premium 腿 risky PV01 risky_pv01_per_million(CDS 生命周期内、条件于存活、每年 1bp coupon 的现值,按 $1MM notional 标准化)以及合约 notional notional_million(百万美元),返回带符号的 upfront 费(美元)。
公式只有一行:
upfront_fee = (par_spread_bps - standard_coupon_bps) * risky_pv01_per_million * notional_million符号承载信息:正值表示保护买方付卖方(市场 par 利差宽于标准 coupon,买方需要补差额给卖方);负值表示卖方付买方(市场 par 利差紧于标准 coupon,卖方需要补差额给买方)。
示例
solution(150.0, 100.0, 4500.0, 10.0) 返回 2250000.0。逐步:差额 150 - 100 = 50 bp;risky PV01 表示 1bp 年金在 4500(这是 desk 从信用曲线 bootstrap 得到的存活加权年金因子);在 $10MM notional 头寸下,upfront = 50 * 4500 * 10 = 2,250,000 美元。看板读数直觉:IG 惯例的 100bp coupon 比 150bp 的真实风险低估了 50bp/年;在合约寿命内,这 50bp 差额按 PV 折现是 2.25MM,由买方在交易开始时一次性付给卖方。
五个易错点
第一,对差额取绝对值。输出带符号。当 par_spread_bps < standard_coupon_bps(例如 IG 名义在 80bp 但合约 coupon 100bp),upfront 为负 —— 卖方在交易起始时付买方,因为买方被锁进了高于真实利差的 coupon 流。abs(par - coupon) 会丢掉 desk 需要的支付方向。校验:solution(80.0, 100.0, 4500.0, 10.0) 必须返回 -900000.0,不是 +900000.0。
第二,单位换算。bps 与 per-bp 相消、MM 与 per-MM 相消。不要除以 10000(把 bps 当十进制)也不要除以 100(把 bps 当百分比)。bps * (USD / bp / MM) * MM 直接是 USD。多除一个 /10000 会得到 1 万倍小的结果。
第三,risky_pv01_per_million 的语义。它是合约生命周期内、条件于存活的 1bp 年金现值,按 per-MM 标准化。它已经把暗含的违约概率折现进去了。不要再乘存活概率或额外折现因子。对扭曲名义来说,desk 的 risky PV01 较小(期望存活时间短);直接代入即可。
第四,把 par_spread_bps 与 standard_coupon_bps 当成同一个东西。它们是不同输入。市场反推出的 par 利差是做市商屏上的;标准 coupon 是合约固定 coupon(北美惯例 100bp IG、500bp HY)。差额才驱动 upfront;把 par 同时填入两处的作者永远得到零 upfront —— 这只是退化的 at-par 情形。
第五,非线性陷阱。公式对四个输入都是线性。notional 加倍 -> upfront 加倍;利差差额加倍 -> upfront 加倍。引入幂、平方根、对数或任何非线性变换都是错的。一致性校验:solution(150,100,4500,1) * 10 == solution(150,100,4500,10)。
边界情形
par_spread_bps == standard_coupon_bps 时差额为零,upfront 也为零(合约「at par」交易)。notional_million == 0.0 时 upfront 为零,与其他输入无关。两者都为零或四个输入都为零时 upfront 都是零。线性公式天然处理,不需要额外分支。
risky_pv01_per_million == 0.0(退化年金)时 upfront 为零,与差额、notional 无关。
在约束封顶下输出可以是 [-1e11, +1e11] 区间内的任意带符号浮点(最坏情形 10000 * 10000 * 1000 = 1e11)。
stubs/stub.py 提供函数骨架。
实践背景
2009 年 ISDA「大爆炸」之后的北美 CDS 惯例规定:所有单名合约用一组固定的标准 coupon(IG 通常 100bp、HY 通常 500bp)。当市场反推出的真实 par 利差与标准 coupon 不同(几乎总是这样),交易在起始时用 upfront 现金支付来补偿那一方拿到了「不公平」的 coupon。Desk 每天从盯市曲线计算这笔 upfront:从曲线读 par 利差、查合约的标准 coupon、乘以 risky 年金因子(信用曲线 bootstrap 得到的 PV01)、再按 notional 缩放。结果就是这笔美元 upfront —— par > coupon 时为正(买方付卖方),par < coupon 时为负(卖方付买方)。同样的计算每晚跑一遍簿上每一笔活动头寸的 upfront 现金腿盯市;本题的姊妹题覆盖 spread-to-PD 与曲线 bootstrap 两个方向,分别产生本题消费的 par 利差与 risky PV01 输入。
约束条件
- 0.0 <= par_spread_bps <= 10000.0(基点;10000 bp = 100% 利差,扭曲名义区间)
- 0.0 <= standard_coupon_bps <= 10000.0(北美惯例下 IG=100bp、HY=500bp)
- 0.0 <= risky_pv01_per_million <= 10000.0(每 1bp coupon 在 $1MM notional 上的存活加权 PV,单位 USD)
- 0.0 <= notional_million <= 1000.0(合约 notional,单位 $MM)
- 输出:带符号 float(美元;正 = 买方付卖方,负 = 卖方付买方);rel_tol=1e-9、abs_tol=1e-9
样例
Case 1 · statement-example: IG name par=150bp coupon=100bp pv01=4500 nl=10MM
输入: [150,100,4500,10]
期望: 2250000
(150-100) * 4500 * 10 = 50 * 4500 * 10 = 2,250,000 美元,买方付卖方。
Case 2 · typical: IG par=150 coupon=100 pv01=4500 nl=5MM (half notional)
输入: [150,100,4500,5]
期望: 1125000
在线性关系下,notional 减半结果减半:(50)*(4500)*(5) = 1,125,000。
Case 3 · typical: IG par=250 coupon=100 pv01=4500 nl=10MM (wider IG)
输入: [250,100,4500,10]
期望: 6750000
(250-100)*4500*10 = 150*4500*10 = 6,750,000。
Case 4 · typical: HY name par=600 coupon=500 pv01=3000 nl=10MM
输入: [600,500,3000,10]
期望: 3000000
HY 标准 coupon=500bp。(600-500)*3000*10 = 100*3000*10 = 3,000,000。
Case 5 · boundary: par == coupon -> upfront = 0 (CDS at par)
输入: [100,100,4500,10]
期望: 0
par 与标准 coupon 相等时无差额,upfront = 0(合约以 par 交易)。
Case 6 · boundary: notional = 0 -> upfront = 0
输入: [150,100,4500,0]
期望: 0
notional=0 -> upfront=0,与 spread/coupon/pv01 无关。
Case 7 · adversarial: signed output — par<coupon must be NEGATIVE, not abs
输入: [80,100,4500,10]
期望: -900000
par=80<coupon=100:作者若用 abs(par-coupon) 会得到 +900,000,正确为 -900,000(卖方付买方)。
最近提交
还没有提交记录。
编码区
实现 solution(...)。本地运行当前支持 Python 可见样例;服务端提交会运行可见样例和隐藏测试。
默认展示公开样例。点击「运行样例」后会在这里显示实际输出;点击「提交评测」会进入隐藏测试。
Case 1 · statement-example: IG name par=150bp coupon=100bp pv01=4500 nl=10MM
输入: [150,100,4500,10]
期望: 2250000
(150-100) * 4500 * 10 = 50 * 4500 * 10 = 2,250,000 美元,买方付卖方。
Case 2 · typical: IG par=150 coupon=100 pv01=4500 nl=5MM (half notional)
输入: [150,100,4500,5]
期望: 1125000
在线性关系下,notional 减半结果减半:(50)*(4500)*(5) = 1,125,000。
Case 3 · typical: IG par=250 coupon=100 pv01=4500 nl=10MM (wider IG)
输入: [250,100,4500,10]
期望: 6750000
(250-100)*4500*10 = 150*4500*10 = 6,750,000。
Case 4 · typical: HY name par=600 coupon=500 pv01=3000 nl=10MM
输入: [600,500,3000,10]
期望: 3000000
HY 标准 coupon=500bp。(600-500)*3000*10 = 100*3000*10 = 3,000,000。
Case 5 · boundary: par == coupon -> upfront = 0 (CDS at par)
输入: [100,100,4500,10]
期望: 0
par 与标准 coupon 相等时无差额,upfront = 0(合约以 par 交易)。
Case 6 · boundary: notional = 0 -> upfront = 0
输入: [150,100,4500,0]
期望: 0
notional=0 -> upfront=0,与 spread/coupon/pv01 无关。
Case 7 · adversarial: signed output — par<coupon must be NEGATIVE, not abs
输入: [80,100,4500,10]
期望: -900000
par=80<coupon=100:作者若用 abs(par-coupon) 会得到 +900,000,正确为 -900,000(卖方付买方)。