Computing a Bond’s Spread to a Par Curve with Curve Shifts
Summary
The document explains two ways to express a bond’s spread relative to a par rate curve. A curve-based z-spread or OAS is found by shifting the discount curve until the bond’s modeled price matches a target price. Because this repricing equation generally has no closed-form solution, the example uses iterative root finding, illustrating a Newton–Raphson step and checking the result by repricing on the shifted curve.
The method depends on how a one-basis-point curve shift is defined: the example applies the shift to the overnight rates implied by the discount curve, rather than to the curve’s calibration instruments. It also describes a simpler yield–yield spread, computed as the bond’s yield to maturity less a par rate. The numerical illustration uses a UK bond and curve setup, so its outputs are specific to those assumptions. The yield comparison is easier to calculate but does not have the same mathematical consistency as the curve-shift measure; the example concerns a bond without optionality.
Key ideas
- A z-spread can be defined as the parallel shift to a discount curve that makes modeled bond value equal a target price.
- Finding the shift generally requires an iterative root solver because the pricing equation may not have a closed-form solution.
- The meaning of a one-basis-point shift depends on how the curve shift is defined.
- A yield–yield spread compares bond yield to maturity with a par rate, but differs conceptually from a curve-based spread.
Tags
Full text
# Calculating spread on a par rate curve given bond’s coupon and yield
# Calculating spread on a par rate curve given bond’s coupon and yield
In Tuckman and Serrat’s Fixed Income Securities, they give an example of a bond and state its coupon and yield.
They also provide an HQM par rate curve and quote the bond’s spread to this curve.
How would the spread to this curve be calculated?
From the coupon and yield, you can calculate the price of the bond. Similarly, from the HQM par rate curve, you can calculate spot rates and forward rates. But it is not clear how one can use all this information to compute a spread to the curve.
A step-by-step breakdown would be helpful.
## Answer by Attack68 (score 3)
https://quant.stackexchange.com/a/79165
### Z-Spread or OA-Spread (with no optionality)
In Python's `rateslib` you can make the following calculation:
Define a `bond` and a `curve`:
```
#PYTHON
from rateslib import FixedRateBond, Curve
bond = FixedRateBond(
effective=dt(2023, 3, 7), termination="10y",
fixed_rate=3.0, spec="ukt"
)
curve = Curve(
nodes={dt(2023, 3, 7): 1.0, dt(2033, 9, 7): 0.68},
calendar="ldn", convention="act365f",
)
```
Use the `curve` to price the bond's current implied mid-market discounted cashflows and derive an OASpread based an assumed price of par (100.0):
```
bond.rate(curves=curve, metric="clean_price")
# 94.14639360179893
bond.oaspread(curves=curve, price=100.00)
# -69.37792098276168
```
You can double check this is valid by `shift`-ing the `curve` and re-pricing the `bond`:
```
shifted_curve = curve.shift(-69.37792098276168)
bond.rate(curves=shifted_curve, metric="clean_price")
# 100.00000000000647
```
So what is actually going on?
Firstly you must define what is a "spread of 1bp to a curve". This is a very important mathematical consideration. Rateslib defines it as adding 1bp to every overnight rate implied by the discount curve. Defining it this way is helpful because it establishes consistent properties of a metric space. Defining it in an alternate way, such as to suggest that the instruments parametrising the Curve, e.g. the 5y swap rate and 10y swap rate are increased by 1bp is very unhelpful because this leads to arbitrary relationships dependent upon those calibrating instruments.
Now you have to derive the actual amount of basis points that the curve needs to shift by in order to re-price the bond at the target price. This requires an iterative root solving algorithm. There is often no closed form solution for this problem.
The price of your bond evaluated at the current curve is:
$$P(C)$$
You are seeking a target price, $P_{tgt}$, which is equal to a price evaluated with a curve shifted by $z$ basis points, i.e.:
$$h(z) = P(C + zC_{1bp}) - P_{tgt} = 0.0 $$
One such possible algorithm is Newton-Raphson. Start with an initial guess e.g. z_0 = 0.0 and apply:
$$ z_{i+1} = z_i - \frac{h(z_i)}{h'(z_i)} $$
I can show you the first iteration that rateslib would perform by evaluating the needed gradient for the algorithm using automatic differentiation:
```
curve._set_ad_order(1)
sensitivity_curve = curve.shift(Dual(0.0, ["z"], []), composite=False)
p = bond.rate(curves=sensitivity_curve, metric="clean_price")
dh_dp = gradient(p, ["z"])
# -0.08162224
```
Thus,
$$ z_1 = 0.0 - (94.14639 - 100.0) / -0.08162224 = -71.715 bps$$
Maybe 3-5 more iterations will get to -69.3779...
### Yield-Yield spread
The yield-yield spread is just the YTM minus the par rate on the curve. Very easy to calculate, not as mathematically consistent as using the OASpread.
In this case the YY spread would be -73.6862.. bps
```
swap = IRS(effective=dt(2023, 3, 7), termination="10y", spec="gbp_irs")
swap.rate(curves=curve)
# 3.736855699417951
bond.ytm(price=100.0, settlement=dt(2023, 3, 8))
# 2.9999929845112567
```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.