Skip to content
All library documents

Bootstrapping Discount Factors from Government Bond Prices

Article Quant Q&A · Author: Jojo

Summary

The document describes an attempt to bootstrap discount factors from US government bond coupon, face value, and closing price data. The proposed approach builds a cash flow matrix for securities of different maturities, then solves a linear system by applying the inverse matrix to the observed prices. The author reports that the resulting discount factor vector includes negative values and asks whether the matrix construction or omission of an error term explains the problem.

The example includes zero-coupon short maturities and coupon-paying bonds, with payment frequency used to place cash flows into matrix periods. It gives code and sample rows but no confirmed diagnosis, corrected implementation, or validation against known yields. The setup raises issues worth checking, including maturity alignment, coupon conventions, matrix conditioning, and whether the cash flows and prices are expressed consistently. The document should be read as a debugging question, not as evidence that this particular bootstrap procedure is reliable.

Key ideas

  • The approach represents bond cash flows in a matrix and solves for discount factors using observed prices.
  • Negative estimated discount factors prompt the author to question the cash flow matrix and model setup.
  • Payment frequency and months to maturity determine the placement of coupons and principal in the matrix.
  • The document offers no final diagnosis or validated discount curve.

Tags

Full text
# Issue with Naive Bootstrapping (US Government Bonds)


# Issue with Naive Bootstrapping (US Government Bonds)












I am using the formula here to determine the discount factor function. I get the data for Coupons, Face Values and Closing Prices from Thomson Reuters, which I insert into the Dataframe `df`. However, I am getting that `dicount_factors` contains negative values as a result of the inverse of `matrix_of_coupons_and_facevalues`. I am trying to decipher if there is a flaw in my code below.

```
df:

   months_to_maturity                      orig_iss_dt  \
1                   6                    2015-06-25 00:00:00.0   
2                  12                    2015-06-25 00:00:00.0   
3                  18                    2015-06-30 00:00:00.0   
4                  24                    2015-06-15 00:00:00.0   
5                  30                    2015-06-30 00:00:00.0   

             maturity_dt  pay_freq_cd  coupon  closing_price  FACE_VALUE  
1  2015-12-24 00:00:00.0          NaN   0.000      99.960889         100  
2  2016-06-23 00:00:00.0          NaN   0.000      99.741444         100  
3  2017-06-30 00:00:00.0            2   0.625      99.968750         100  
4  2018-06-15 00:00:00.0            2   1.125     100.390625         100  
5  2020-06-30 00:00:00.0            2   1.625      99.984375         100
```

The code:

```
#Determining the dimensions of the matrix of coupons and face values
#pay_freq_cd is number of coupon payments per year
max_freq_payments = df['pay_freq_cd'].max()
no_of_months_between_payments_for_matrix = 12/max_freq_payments
number_of_columns_and_rows = df['months_to_maturity'].max()/no_of_months_between_payments_for_matrix

df['FACE_VALUE'] = 100
df = df.sort(['months_to_maturity'], ascending=[True])
df = df.reset_index()

for (i,row) in df.iterrows():
    no_of_coupons_per_year_2 = row['pay_freq_cd']
    no_of_months_between_payments_2 = 12/no_of_coupons_per_year_2               
    if math.isnan(no_of_months_between_payments_2):

        matrix_of_coupons_and_facevalues[i,(row.months_to_maturity/no_of_months_between_payments_for_matrix)-1] = (row['FACE_VALUE']) 
    else:
        matrix_of_coupons_and_facevalues[i,0:((row.months_to_maturity/no_of_months_between_payments_2)-1)] = row['coupon']
        matrix_of_coupons_and_facevalues[i,(row.months_to_maturity/no_of_months_between_payments_2)-1] = (row['FACE_VALUE']) + row['coupon']

price_array = df['closing_price'].values
price_array = np.where(np.isnan(price_array), 100, price_array)
inv_matrix_of_coupons_and_facevalues = inv(matrix_of_coupons_and_facevalues)
dicount_factors = np.dot(inv_matrix_of_coupons_and_facevalues, price_array)
```

Edit: I am not accounting for $epsilon$ , as stated here, in the equation $AP = F + epsilon$ and can hence directly solve $AP = F$ as a system of Equations. However, I am wondering could this be where my problem stems from?

Thank You

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.