Bootstrapping Spot Rates from Par Yields and Diagnosing Failures
Summary
The document presents a Python implementation intended to bootstrap spot rates from a sequence of par bond yields. For each maturity, it discounts earlier coupon payments using already computed spot rates, subtracts those payments from the assumed face value, and solves for the rate that prices the remaining cash flows. The author reports that extending the yield input eventually makes this residual negative and produces NaN values.
The example also compares the code’s output with values from an external reference and finds small discrepancies. However, the document does not establish the cause of either issue, and the calculation depends on assumptions that are only partly specified, including par prices, coupon timing, and a regular semiannual schedule. It is best read as a troubleshooting question about recursive bootstrapping and implementation consistency, not as a verified algorithm or a general maturity limit. The supplied yield vector alone does not establish whether the inputs match the code’s cash-flow conventions.
Key ideas
- The code solves each spot rate recursively after discounting earlier coupon payments.
- A negative residual price makes the subsequent rate calculation invalid and leads to NaN values.
- The author observes small differences between the implementation and a referenced set of spot rates.
- Correct results depend on consistent assumptions about par prices, coupon timing, and payment frequency.
- The document raises debugging questions but does not provide a confirmed diagnosis or corrected method.
Tags
Full text
# Is there a limit to the number of Spot rates than can be calculated from Par Yields
# Is there a limit to the number of Spot rates than can be calculated from Par Yields
I am just trying to calculate Spot Rates from Par yields. I find that the code below gives very similar spot rates for the data here, yet if I increase the size of the `yields` array I end up getting NaN values from the Python code as `adding_negatives` turns out to be greater than `face_values[index]` making `term_1` negative. I've basically just directly implemented the steps in the link to get the Spot Rates.
```
#df is a Dataframe that contains the bond information
import numpy as np
face_values = df['FACE_VALUE'].values #ALL 100 as only Par Bonds are taken
yields = (df['coupon'].values) #PAR YIELDS
spot_rate = np.zeros((yields.shape[0]))
#Obtaining Spot Yields
for x, coupon in np.ndenumerate(yields):
index = x[0]
if index == 0:
spot_rate[index] = (yields[index]/face_values[index]) * 100
else:
adding_negatives = 0
if index < spot_rate.shape[0]:
for i in range (0, index, 1):
adding_negatives = adding_negatives + (coupon/2)/np.power((1+(spot_rate[i]/200)),i+1)
term_1 = face_values[index] - adding_negatives
spot_rate[index] = 2*(((np.power(((((face_values[index] + ((coupon/2)))/term_1))),1/(index+1)))-1)) * 100
```
If `yields` is a Vector like this:
`[ 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.75 2. 2. 2. 2.05 2.1 2.5 2.99 4. 4.04 4.1 4.5 4.8 4.875 4.97 5.125 5.25 5.5 5.75 5.75 5.95 5.967 6.15 6.3 6.375]`, some of the Spot Rates turn out to be NaN as `adding_negative` keeps adding up and turns to be greater than 100 (`face_values[index]`) eventually.
Please let me know if more information is required. This code is directly usable, with the `yields` array given above.
Edit: In reference to Gordon's comment:
To get the spot rate for the 2'nd period:
$$\text{Price} = \frac{c/2}{1 + spotrate_{period 0}/200} + \frac{facevalue + c/2}{(1 + spotrate_{period 1}/200)^2} $$
The code basically follows the steps:
$$\text{adding_negatives} = \frac{c/2}{1 + spotrate_{period 0}/200}$$
$$\text{term_1} = \text{Price} - \text{adding_negatives}$$
$$spotrate_{period 1} = 2 * ((((\text{facevalue} + (\text{coupon/2}))/\text{term_1})^{1/periodno} -1))$$
Also, with regards to the spot rates in the Link:
The code gives: `[ 2. 2.4024 2.7669 3.0933 3.4021 3.6721]`
The link gives: `[ 2. 2.4024 2.7669 3.0974 3.3975 3.6701]`, so maybe there actually is an Error here too?
Thank YouShown 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.