Skip to content
All library documents

Why Jamshidian Decomposition Can Fail with Black-76 Bond Options

Article Quant Q&A · Author: Sentinel

Summary

The document presents a numerical attempt to apply Jamshidian’s trick to a European put on a coupon bond. It compares the bond option’s Black-76 value with the sum of Black-76 values for options on the bond’s individual cash flows. In the example, intrinsic values agree to numerical precision, while the volatility-based prices differ.

That discrepancy illustrates a key modeling requirement: Jamshidian decomposition relies on a one-factor setup in which the bond’s cash flows move with a common underlying state, so the component option strikes must be derived consistently from that state. Pricing each discounted cash flow as a separate Black-76 option with the same quoted volatility does not generally preserve the distribution of the aggregate bond price. The document supplies code and a specific example, but no accepted correction or general derivation; its numerical output is best treated as a question about model assumptions and implementation rather than a validated result.

Key ideas

  • The example compares a coupon-bond put with a sum of options on its cash flows.
  • The intrinsic values match, but the Black-76 prices differ when the same volatility is assigned to each component.
  • Jamshidian decomposition requires a consistent common factor linking the cash flows and the bond price.
  • The document offers an implementation example but does not resolve the modeling issue.

Tags

Full text
# Jamshidian trick in Black-76 for bond options


# Jamshidian trick in Black-76 for bond options












I am struggling with application of Jamshidian's trick to the Black-76 model for bond options. I made a simple example to demonstrate my problem.

I value the european put bond option at 95 on the 2Y semi-annual bond with coupon = 5 and redemption at 100. Volatility of F is set to 0.04. Then I compare value of option on the coupon bond with sum of values of options on coupons.

Intrinsic values [max(sum)-sum(max)] match exactly, but Black-76 values differ.

What am I doing wrong? – My python code is below.

```
# -*- coding: utf-8 -*-
"""
Created on Mon Jan  6 14:13:34 2025

Attept at applying Jamshidian trick to the bond option, priced by Black-76
"""
import numpy as np
import scipy.stats as st
import scipy.optimize as opt

def black76(CP, F, K, sigma, t, r):
    d1 = (np.log(F/K)+0.5*t*sigma**2)/(np.sqrt(t)*sigma)
    d2 = d1-np.sqrt(t)*sigma
    if CP == 1: # call
        return np.exp(-r*t)*(st.norm.cdf(d1)*F-st.norm.cdf(d2)*K)
    else:
        return np.exp(-r*t)*(st.norm.cdf(-d2)*K-st.norm.cdf(-d1)*F)

def intrinsic(CP, F, K, tex, r):
    if CP == 1:
        return max(F-K,0)*np.exp(-r*tex)
    else:
        return max(K-F,0)*np.exp(-r*tex)

def jam(bondF, K, r, tex):
    func = lambda r: np.sum(bondF[:,1]*np.exp(-r*(bondF[:,0]-tex)))-K
    return opt.newton(func, r, tol=1e-15)
    
# =============================================================================
# PARAMETERS
# =============================================================================

r = 0.17
bond = np.array([[0.5, 5],[1, 5],[1.5, 5],[2, 105]]) # t, CF
tex = 1 # option expiration
CP = -1
K = 95
sigma = 0.04

# =============================================================================
# MAIN
# =============================================================================

P0T = np.sum(bond[:,1]*np.exp(-r*bond[:,0]))
bondF = bond[bond[:,0]>tex]
F = np.sum(bondF[:,1]*np.exp(-r*(bondF[:,0]-tex)))

Pin = intrinsic(CP, F, K, tex, r)
Err = Pin-black76(CP, F, K, 1e-9, tex, r) 
P = black76(CP, F, K, sigma, tex, r)

rj = jam(bondF, K, r, tex) # Jamshidian rj
Kj = bondF[:,1]*np.exp(-rj*(bondF[:,0]-tex)) # Jamshidian strikes
Fj = bondF[:,1]*np.exp(-r*(bondF[:,0]-tex)) # F of strips

Pinj = np.zeros(Kj.shape[0]) # intrinsic value of options on coupons
for j, k in enumerate(Kj):
    Pinj[j] = intrinsic(CP, Fj[j], k, tex, r)

print('Bond option put intrinsic value difference \nmax(sum)-sum(max)', Pin-np.sum(Pinj))

Pj = np.zeros(Kj.shape[0]) # black value of options on coupons
for j, k in enumerate(Kj):
    Pj[j] = black76(CP, Fj[j], k, sigma, tex, r)

print('\nBond option put black value difference\nmax(sum)-sum(max)', P-np.sum(Pj))
```

Bond option put intrinsic value difference max(sum)-sum(max) -8.881784197001252e-16

Bond option put black value difference max(sum)-sum(max) -0.0016714330263845056

Shown in full with attribution under the source's licence. Licence: CC BY-SA 4.0 (Stack Exchange)

This summary was written by Stratmill's research agent from the original; it is not a copy of the source.