# Convexity for historical bond data

I'm trying to write a program to calculate the convexity of a bond. The bigger idea is, that if I have access to the actual price for each point in time, I should be able to calculate various features of the bond given that date. The convexity program is written as follows:

""" Calculate convexity of a bond """
def bond_convexity(price, par, T, coup, freq, dy=0.0001):
ytm = bond_ytm(price, par, T, coup, freq)

ytm_minus = ytm - dy
price_minus = bond_price(par, T, ytm_minus, coup, freq)

ytm_plus = ytm + dy
price_plus = bond_price(par, T, ytm_plus, coup, freq)

convexity = (price_minus+price_plus-2*price)/(price*dy**2)
return convexity


Where the yield to maturity and bond price functions are as follows:

"""Interpolate yield to maturity"""
def bond_ytm(price, par, coupdates, coup, freq=2, guess=0.05):
freq = float(freq)
timedif = list() #init
periods = list() #init
timedif.append( round((MAT - coupdates[i]).days/365,1) )     #collection of time to maturity - coupon date
periods.append(timedif[i]*freq)
coupon = coup/100.*par/freq
dt = [(x)/freq for x in periods]
ytm_func = ytm_func = lambda y : \
sum([coupon/(1+y/freq)**(freq*t) for t in dt]) + par/(1+y/freq)**\
(freq*max(dt)) - price

return optimize.newton(ytm_func, guess)

"""Price bond"""
def bond_price(par, coupdates, ytm, coup, freq=2):
freq = float(freq)
timedif = list() #init
periods = list() #init
timedif.append( round((MAT - coupdates[i]).days/365,1) ) #collection of time to maturity - coupon date
periods.append(timedif[i]*freq)
coupon = coup/100.*par/freq
dt = [(x)/freq for x in periods]
price = sum([coupon/(1+ytm/freq)**(freq*t) for t in dt]) + par/(1+ytm/freq)**(freq*max(dt))
return price


Now, the following data shows the upcoming coupon dates for the bond and today's date at the top. I've saved this variable as cdd:

cdd
Out[28]:
[datetime.datetime(2017, 10, 31, 15, 20, 0, 480212),
datetime.datetime(2018, 2, 1, 0, 0),
datetime.datetime(2018, 8, 1, 0, 0),
datetime.datetime(2019, 2, 1, 0, 0),
datetime.datetime(2019, 8, 1, 0, 0),
datetime.datetime(2020, 2, 1, 0, 0),
datetime.datetime(2020, 8, 1, 0, 0),
datetime.datetime(2021, 2, 1, 0, 0),
datetime.datetime(2021, 8, 1, 0, 0),
datetime.datetime(2022, 2, 1, 0, 0),
datetime.datetime(2022, 8, 1, 0, 0),
datetime.datetime(2023, 2, 1, 0, 0),
datetime.datetime(2023, 8, 1, 0, 0),
datetime.datetime(2024, 2, 1, 0, 0),
datetime.datetime(2024, 8, 1, 0, 0),
datetime.datetime(2025, 2, 1, 0, 0),
datetime.datetime(2025, 8, 1, 0, 0),
datetime.datetime(2026, 2, 1, 0, 0),
datetime.datetime(2026, 8, 1, 0, 0),
datetime.datetime(2027, 2, 1, 0, 0),
datetime.datetime(2027, 8, 1, 0, 0),
datetime.datetime(2028, 2, 1, 0, 0),
datetime.datetime(2028, 8, 1, 0, 0),
datetime.datetime(2029, 2, 1, 0, 0),
datetime.datetime(2029, 8, 1, 0, 0),
datetime.datetime(2030, 2, 1, 0, 0),
datetime.datetime(2030, 8, 1, 0, 0),
datetime.datetime(2031, 2, 1, 0, 0),
datetime.datetime(2031, 8, 1, 0, 0),
datetime.datetime(2032, 2, 1, 0, 0),
datetime.datetime(2032, 8, 1, 0, 0),
datetime.datetime(2033, 2, 1, 0, 0),
datetime.datetime(2033, 8, 1, 0, 0),
datetime.datetime(2034, 2, 1, 0, 0),
datetime.datetime(2034, 8, 1, 0, 0),
datetime.datetime(2035, 2, 1, 0, 0),
datetime.datetime(2035, 8, 1, 0, 0),
datetime.datetime(2036, 2, 1, 0, 0),
datetime.datetime(2036, 8, 1, 0, 0),
datetime.datetime(2037, 2, 1, 0, 0),
datetime.datetime(2037, 8, 1, 0, 0),
datetime.datetime(2038, 2, 1, 0, 0),
datetime.datetime(2038, 8, 1, 0, 0),
datetime.datetime(2039, 2, 1, 0, 0),
datetime.datetime(2039, 8, 1, 0, 0),
datetime.datetime(2040, 2, 1, 0, 0),
datetime.datetime(2040, 8, 1, 0, 0),
datetime.datetime(2041, 2, 1, 0, 0),
datetime.datetime(2041, 8, 1, 0, 0),
datetime.datetime(2042, 2, 1, 0, 0),
datetime.datetime(2042, 8, 1, 0, 0),
datetime.datetime(2043, 2, 1, 0, 0),
datetime.datetime(2043, 8, 1, 0, 0),
datetime.datetime(2044, 2, 1, 0, 0),
datetime.datetime(2044, 8, 1, 0, 0),
datetime.datetime(2045, 2, 1, 0, 0),
datetime.datetime(2045, 8, 1, 0, 0)]


However, if I run the program to calculate convexity for a bond priced at 112.057, par = 100, dates as cdd, coupon of 4.9, and semi annual frequency, I get the following solution:

bond_convexity(112.057, 100, cdd, 4.9, 2)
Out[32]: 351.98162487756656


Bloomberg is showing that the convexity should be approximately 3.467. What could I possibly be doing wrong? Any help is appreciated.

1. There are different ways to express convexity, depending how you express yields. An example might help: If yields are expressed in percent (e.g., 5 for 5%), then the convexity of a zero coupon bond with a duration of 15 is roughly $15\times15/100 = 2.25$. Alternatively, if you expressed yields in decimals (0.05 for 5%), then the corresponding convexity is expressed as $15\times 15 = 225$. The first approach is more common and is used by BBG. All you have to do is to divide your number by 100 to be consistent.