1
$\begingroup$

Has anyone used QuantLib to create term structure (i.e bootstrapping process to produce spots) in python? I have been using the below example http://gouthamanbalaraman.com/blog/quantlib-term-structure-bootstrap-yield-curve.html

Now looks like the assumption is that coupon bonds are trading at par (i.e. price of 100) ?

'The rest of the points are coupon bonds. We assume that the YTM given for the bonds are all par rates. So we have bonds with coupon rate same as the YTM.'

But what if they aren't bonds we use are not trading at par? i.e. YTM and coupon rates are different?

Any help is greatly appreciated.

thanks,

Updated codes added here

import matplotlib
matplotlib.use('macosx')
import matplotlib.pyplot as plt
import QuantLib as ql
import pandas as pd

# Deposit rates
depo_maturities = [ql.Period(1,ql.Months), ql.Period(2,ql.Months),ql.Period(3,ql.Months),ql.Period(6,ql.Months),
                   ql.Period(9,ql.Months), ql.Period(12, ql.Months)]
depo_cpn = [.08,.24,.40,.68,.34,.52] #yields they are trading at


# Coupon Bonds
bond_maturities = [ql.Period(i, ql.Years) for i in range(2,11)]
bond_cpn = [.5,.75,.1,.625,1.5,1.25,1.625,.875,4.75]
bond_rates = [.114,.151,.187,.252,.214,.272,.311,.4089,4.74]
bond_quotes = [100.896,101.987,103.301,101.926,108.078,107.088,111.111,104.374,144.568]


bond_long_maturities = [ql.Period(12,ql.Years),ql.Period(15,ql.Years),ql.Period(20,ql.Years),ql.Period(25,ql.Years),
                        ql.Period(30,ql.Years),ql.Period(40,ql.Years),ql.Period(50,ql.Years)]
bond_long_cpn = [4.25,4.5,4.25,3.25,1.75,1.75,1.625] #coupons
bond_long_rates = [.593,.667,.767,.858,.848,.669,.543] #yields
bond_long_quotes = [142.974,152.719,162.806,151.432,123.016,135.634,148.58,]



'''####### Depo Helpers #########'''

calc_date = ql.Date(24, 3, 2020)
ql.Settings.instance().evaluationDate = calc_date

calendar = ql.UnitedKingdom()
business_convention = ql.Unadjusted
day_count = ql.Thirty360()
end_of_month = True
settlement_days = 0
face_amount = 100
coupon_frequency = ql.Period(ql.Annual)


#Create depo bondhelps
depo_helpers = [ql.DepositRateHelper(ql.QuoteHandle(ql.SimpleQuote(r/100.0)),
                                     m,
                                     settlement_days,
                                     calendar,
                                     business_convention,
                                     end_of_month,
                                     day_count )
                for r, m in zip(depo_cpn, depo_maturities)]





'''####### Bonds Helpers #########'''

day_count = ql.Thirty360()
end_of_month = True
settlement_days = 2


# create fixed rate bond helpers from fixed rate bonds
bond_cpn += bond_long_cpn
bond_maturities += bond_long_maturities
bond_quotes += bond_long_quotes
bond_rates += bond_long_rates

bond_helpers = []
for r, m, q in zip(bond_cpn, bond_maturities,bond_quotes):
    termination_date = calc_date + m
    quote = ql.QuoteHandle(ql.SimpleQuote(q))
    schedule = ql.MakeSchedule(calc_date,termination_date,m)

    helper = ql.FixedRateBondHelper(quote,settlement_days,face_amount,schedule,[r/100.0],day_count,business_convention)
    bond_helpers.append(helper)


#The yield curve is constructed by putting the two helpers together.

rate_helpers = depo_helpers + bond_helpers
yieldcurve = ql.PiecewiseLogCubicDiscount(calc_date,rate_helpers, day_count)


#The spot cpn is obtined from yieldcurve object using the zeroRate method.
spots = []
tenors = []
for d in yieldcurve.dates():
    yrs = day_count.yearFraction(calc_date, d)
    compounding = ql.Compounded
    freq = ql.Annual
    zero_rate = yieldcurve.zeroRate(yrs, compounding, freq)
    tenors.append(yrs)
    eq_rate = zero_rate.equivalentRate(day_count,
                                       compounding,
                                       freq,
                                       calc_date,
                                       d).rate()
    spots.append(100*eq_rate)


spotcurve = pd.DataFrame(dict(tenors=tenors,spots=spots))
spotcurve.set_index('tenors',inplace=True)

print('\n')
spotcurve = spotcurve.iloc[1:]
pars = depo_cpn+bond_rates
spotcurve['pars'] = pars
spotcurve['spots'] = round(spotcurve['spots'],3)
print(spotcurve)

plt.figure(figsize=(7,4))
plt.plot(spotcurve)#,'b',lw=1.5)
plt.plot(spotcurve,'ro')
plt.grid(True)
plt.xlabel('Tenor')
plt.ylabel('Curves')
plt.show()
```
$\endgroup$
1
$\begingroup$

You should look at the inputs for the helpers. I believe the example you pointed to is an aproximation to build the curve with the yields, but the inputs are actually in prices. Check this example that is hopefully self explanatory.

quote = ql.QuoteHandle(ql.SimpleQuote(115.5))
settlementDays = 2
faceAmount = 100
schedule = ql.MakeSchedule(ql.Date(15,6,2020), ql.Date(15,6,2021), ql.Period('1y'))
coupons = [0.0195]
dayCounter = ql.ActualActual()
ql.FixedRateBondHelper(quote, settlementDays, faceAmount, schedule, coupons, dayCounter)
| improve this answer | |
$\endgroup$
  • $\begingroup$ great thanks @David I think am getting it now. As you can see I have just started using QL probably will have to try some more examples to get my head around it properly. $\endgroup$ – Prasad Kamath Mar 24 at 15:46
  • $\begingroup$ btw what's the difference between ql.Schedule and ql.MakeSchedule? $\endgroup$ – Prasad Kamath Mar 24 at 23:49
  • $\begingroup$ Schedule is the class and MakeSchedule is a helper to create an instance of the class $\endgroup$ – David Duarte Mar 24 at 23:56
  • $\begingroup$ thanks again David $\endgroup$ – Prasad Kamath Mar 25 at 0:08
  • $\begingroup$ hi David, my bootstrapped curve is really all over the place. any chance you could have a look at the codes (added above) and advise where am going wrong? Also, wanted to confirm for 'bond_cpn' I am using the coupon rate (not the yield). appreciate your help again. The spot curve is really different from the curve I see on my Bloomberg screen. $\endgroup$ – Prasad Kamath Mar 25 at 0:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.