Wednesday, 23 September 2015

Importing a yield curve into QuantLib with quantlib-python

The purpose of the exercise was to explore importing a predefined curve into QuantLib, and then to observe the output and check if we get what we expect. We use the ZeroCurve class (which the reference documentation says is "Term structure based on linear interpolation of zero yields class"). To import a zero yield curve into python one needs to create two aligned lists of dates and rates. By default the class will accept continuous rates, so anything different must be specified. In our case we are importing annual yields, so we set compoundingFrequency = ql.Annual




import pandas as pd
import numpy as np
import QuantLib as ql
import datetime as dt


calendar = ql.SouthAfrica()
bussiness_convention = ql.Following
day_count = ql.Actual365Fixed()
interpolation = ql.Linear()
compounding = ql.Compounded
compoundingFrequency = ql.Annual
calc_date = ql.Date(18, 9, 2015) 
curve_date = dt.datetime(2015,9,18)
today = calc_date#ql.Date(8,ql.October, 2014)
#ql.Settings.instance().evaluationDate = calc_date

def to_datetime(d):
    return dt.datetime(d.year(),d.month(), d.dayOfMonth())
#the data from predefined curve

curve_data=pd.DataFrame(np.array([(0L, 1442534400000000000L, 1L, 0.05968943361108825, 'DAY', 1L),
       (1L, 1442534400000000000L, 3L, 0.06008360555190584, 'DAY', 3L),
       (2L, 1442534400000000000L, 7L, 0.061003962665558964, 'WEEK', 1L),
       (3L, 1442534400000000000L, 14L, 0.061629138095260494, 'WEEK', 2L),
       (4L, 1442534400000000000L, 21L, 0.06246539834717324, 'WEEK', 3L),
       (5L, 1442534400000000000L, 30L, 0.063080497435273, 'MONTH', 1L),
       (6L, 1442534400000000000L, 61L, 0.0640837249655406, 'MONTH', 2L),
       (7L, 1442534400000000000L, 91L, 0.06458932758091618, 'MONTH', 3L),
       (8L, 1442534400000000000L, 122L, 0.06460602877745236, 'MONTH', 4L),
       (9L, 1442534400000000000L, 153L, 0.06490097928752325, 'MONTH', 5L),
       (10L, 1442534400000000000L, 182L, 0.06562196294577327, 'MONTH', 6L),
       (11L, 1442534400000000000L, 213L, 0.06559317254771013, 'MONTH', 7L),
       (12L, 1442534400000000000L, 243L, 0.06615484763684432, 'MONTH', 8L),
       (13L, 1442534400000000000L, 274L, 0.06687832021278495, 'MONTH', 9L),
       (14L, 1442534400000000000L, 304L, 0.06683739890814766, 'MONTH', 10L),
       (15L, 1442534400000000000L, 335L, 0.06722706850014037, 'MONTH', 11L),
       (16L, 1442534400000000000L, 366L, 0.06799498188697228, 'MONTH', 12L),
       (17L, 1442534400000000000L, 457L, 0.06903328142506937, 'MONTH', 15L),
       (18L, 1442534400000000000L, 547L, 0.07012859614243028, 'MONTH', 18L),
       (19L, 1442534400000000000L, 639L, 0.0711421402251855, 'MONTH', 21L),
       (20L, 1442534400000000000L, 731L, 0.07215610320058774, 'MONTH', 24L),
       (21L, 1442534400000000000L, 822L, 0.07306913034489537, 'MONTH', 27L),
       (22L, 1442534400000000000L, 912L, 0.07390974864236854, 'MONTH', 30L),
       (23L, 1442534400000000000L, 1004L, 0.0746912071699184, 'MONTH', 33L),
       (24L, 1442534400000000000L, 1096L, 0.07543012070847732, 'MONTH', 36L),
       (25L, 1442534400000000000L, 1187L, 0.07612842034647893, 'MONTH', 39L),
       (26L, 1442534400000000000L, 1277L, 0.07679447188377297, 'MONTH', 42L),
       (27L, 1442534400000000000L, 1369L, 0.07744248361696626, 'MONTH', 45L),
       (28L, 1442534400000000000L, 1461L, 0.07805149575772585, 'MONTH', 48L),
       (29L, 1442534400000000000L, 1552L, 0.07862397558955214, 'MONTH', 51L),
       (30L, 1442534400000000000L, 1643L, 0.07917584728234361, 'MONTH', 54L),
       (31L, 1442534400000000000L, 1735L, 0.079709603821418, 'MONTH', 57L),
       (32L, 1442534400000000000L, 1827L, 0.08021643040257698, 'MONTH', 60L),
       (33L, 1442534400000000000L, 1918L, 0.08069118300778322, 'MONTH', 63L),
       (34L, 1442534400000000000L, 2008L, 0.08114184287539494, 'MONTH', 66L),
       (35L, 1442534400000000000L, 2100L, 0.08159085035229752, 'MONTH', 69L),
       (36L, 1442534400000000000L, 2192L, 0.08204424485907724, 'MONTH', 72L),
       (37L, 1442534400000000000L, 2283L, 0.08249064630693725, 'MONTH', 75L),
       (38L, 1442534400000000000L, 2373L, 0.08292334366174559, 'MONTH', 78L),
       (39L, 1442534400000000000L, 2465L, 0.08336529417751093, 'MONTH', 81L),
       (40L, 1442534400000000000L, 2557L, 0.0837551643848149, 'MONTH', 84L),
       (41L, 1442534400000000000L, 2648L, 0.08409983640404173, 'MONTH', 87L),
       (42L, 1442534400000000000L, 2738L, 0.08441573153982995, 'MONTH', 90L),
       (43L, 1442534400000000000L, 2830L, 0.08472115832909055, 'MONTH', 93L),
       (44L, 1442534400000000000L, 2922L, 0.08503216237171274, 'MONTH', 96L),
       (45L, 1442534400000000000L, 3013L, 0.08535856504021955, 'MONTH', 99L),
       (46L, 1442534400000000000L, 3104L, 0.08568946114081943, 'MONTH', 102L),
       (47L, 1442534400000000000L, 3196L, 0.08601312798025784, 'MONTH', 105L),
       (48L, 1442534400000000000L, 3288L, 0.08631201206897576, 'MONTH', 108L),
       (49L, 1442534400000000000L, 3379L, 0.08657921856932438, 'MONTH', 111L),
       (50L, 1442534400000000000L, 3469L, 0.08682568429113857, 'MONTH', 114L)], 
      dtype=[('index', '<i8'), ('valDate', '<M8[ns]'), ('tDays', '<i8'), ('SWAP', '<f8'), ('Period', 'O'), ('Periods', '<i8')]))

#We must make QuantLib date objects
dicPeriod={'DAY':ql.Days,'WEEK':ql.Weeks,'MONTH':ql.Months,'YEAR':ql.Years}
curve_data['qlvalDate']= curve_data.apply(lambda row :ql.DateParser.parseISO(pd.to_datetime(row['valDate']).strftime('%Y-%m-%d')),axis=1)
curve_data['qlQuote']= curve_data.apply(lambda row :ql.SimpleQuote(row['SWAP']),axis=1)
curve_data['qlDate']=curve_data.apply(lambda row :row['qlvalDate'] + ql.Period(int(row['Periods']),dicPeriod[row['Period']]),axis=1)
curve_data['Date']=curve_data.apply(lambda row :to_datetime(row['qlDate']),axis=1)

#Create Curve as a QuantLib object
zc = ql.ZeroCurve(curve_data.qlDate.values,curve_data.SWAP.values,ql.Actual365Fixed(),calendar, interpolation,compounding, compoundingFrequency)
# write back into the dataframe
curve_data['qlRates']=zc.zeroRates()
curve_data.set_index('Date')[['SWAP','qlRates']].plot()

The Plot

We can see that the QuantLib curve is below our input curve as it is a continuous rate. We can check this by running the following code, and we now see that they are the same.

curve_data['qlRates']=(np.exp(curve_data.qlRates)-1)
curve_data.set_index('Date')[['SWAP','qlRates']].plot()

Final Plot