|
From: Charles A. <ch...@al...> - 2024-08-27 10:47:06
|
@Luigi Ballabio <lui...@gm...> posted this very informative piece, https://www.implementingquantlib.com/2024/05/inflation-curves.html on Inflation indexes and curves which has been of great help. What I am struggling with, is that when I price a CPI bond, I would expect the value of the bond priced off the real curve to equal the price of the CPI bond priced off the nominal curve with coupons increased by the forecasted inflation rate as defined by implied inflation being the difference between the nominal and real zero curves. I have investigated this here by adapting the Python code from https://www.implementingquantlib.com/2024/05/inflation-curves.html, by defining a Nominal and Real curve, with implied inflation being the geometric diff of the two curves. The problem is that I was expecting ql.CPI.laggedFixing(hicp, some_date, observation_lag, ql.CPI.Linear))/ ValueBase to be very close to 1.0/ implied_infl_curve.discount(some_date)) however this does not seem to be the case: node nom real imp_inf base_val inf diff May 11th, 2024 1.0000000 1.0000000 1.0000000 124.6732258 1.0000000 - May 12th, 2025 1.0620111 1.0298161 1.0312628 124.6732258 1.0308708 -0.0003802 May 11th, 2026 1.1274969 1.0607752 1.0628989 124.6732258 1.0620177 -0.0008291 May 11th, 2027 1.1972174 1.0930260 1.0953238 124.6732258 1.0939309 -0.0012716 May 11th, 2028 1.2714581 1.1266872 1.1284926 124.6732258 1.1264751 -0.0017878 May 11th, 2029 1.3500807 1.1619297 1.1619297 124.6732258 1.1594619 -0.0021240 May 12th, 2031 1.5224620 1.2390781 1.2287054 124.6732258 1.2251594 -0.0028860 May 11th, 2034 1.8227180 1.3739282 1.3266471 124.6732258 1.3215592 -0.0038352 May 12th, 2036 2.0557845 1.4762652 1.3925577 124.6732258 1.3863294 -0.0044726 May 11th, 2039 2.4608164 1.6392202 1.5012116 124.6732258 1.4932337 -0.0053143 May 11th, 2044 3.3228469 1.9504444 1.7036358 124.6732258 1.6918066 -0.0069435 May 11th, 2049 4.4861115 2.3106996 1.9414516 124.6732258 1.9248788 -0.0085363 May 11th, 2054 6.0566127 2.7320222 2.2168973 124.6732258 2.1941767 -0.0102488 May 12th, 2064 11.0431267 3.7591653 2.9376540 124.6732258 2.8960907 -0.0141484 May 11th, 2074 20.1251968 5.0281959 4.0024687 124.6732258 3.9279265 -0.0186241 To replicate please see the Python below: import QuantLib as ql import pandas as pd today = ql.Date(11, ql.May, 2024) ql.Settings.instance().evaluationDate = today index = ql.EUHICP() inflation_fixings = [ ((2022, ql.January), 110.70), ((2022, ql.February), 111.74), ((2022, ql.March), 114.46), ((2022, ql.April), 115.11), ((2022, ql.May), 116.07), ((2022, ql.June), 117.01), ((2022, ql.July), 117.14), ((2022, ql.August), 117.85), ((2022, ql.September), 119.26), ((2022, ql.October), 121.03), ((2022, ql.November), 120.95), ((2022, ql.December), 120.52), ((2023, ql.January), 120.27), ((2023, ql.February), 121.24), ((2023, ql.March), 122.34), ((2023, ql.April), 123.12), ((2023, ql.May), 123.15), ((2023, ql.June), 123.47), ((2023, ql.July), 123.36), ((2023, ql.August), 124.03), ((2023, ql.September), 124.43), ((2023, ql.October), 124.54), ((2023, ql.November), 123.85), ((2023, ql.December), 124.05), ((2024, ql.January), 123.60), ((2024, ql.February), 124.37), ((2024, ql.March), 125.31), ((2024, ql.April), 126.05), ] for (year, month), fixing in inflation_fixings: index.addFixing(ql.Date(1, month, year), fixing) index.fixing(ql.Date(15, ql.March, 2024)) observation_lag = ql.Period(3, ql.Months) ql.CPI.laggedFixing( index, ql.Date(15, ql.May, 2024), observation_lag, ql.CPI.Linear ) real_quotes = [ (ql.Period(1, ql.Years), 2.93), (ql.Period(2, ql.Years), 2.95), (ql.Period(3, ql.Years), 2.965), (ql.Period(4, ql.Years), 2.98), (ql.Period(5, ql.Years), 3.0), (ql.Period(7, ql.Years), 3.06), (ql.Period(10, ql.Years), 3.175), (ql.Period(12, ql.Years), 3.243), (ql.Period(15, ql.Years), 3.293), (ql.Period(20, ql.Years), 3.338), (ql.Period(25, ql.Years), 3.348), (ql.Period(30, ql.Years), 3.348), (ql.Period(40, ql.Years), 3.308), (ql.Period(50, ql.Years), 3.228), ] calendar = ql.TARGET() observation_lag = ql.Period(3, ql.Months) day_counter = ql.Actual365Fixed() interpolation = ql.CPI.Linear nominal_curve = ql.YieldTermStructureHandle( ql.FlatForward(today, 0.06, ql.Actual365Fixed()) # Continuous, Annual ) rates,dates = [],[] for tenor, quote in real_quotes: maturity = calendar.advance(today, tenor) rates.append(quote/100.0) dates.append(maturity) real_curve = ql.ZeroCurve([today] + dates,[0.0] + rates,day_counter) # Continuous, Annual implied_infl_curve = ql.DiscountCurve([today] + dates,[1.0]+[nominal_curve. discount(d)/real_curve.discount(d) for d in dates],ql.Actual365Fixed()) helpers = [] for tenor, quote in real_quotes: maturity = calendar.advance(today, tenor) helpers.append( ql.ZeroCouponInflationSwapHelper( ql.makeQuoteHandle(implied_infl_curve.zeroRate(maturity,ql. Actual365Fixed(),ql.Continuous).rate()), # Continuous, Annual observation_lag, maturity, calendar, ql.Following, day_counter, index, interpolation, nominal_curve, ) ) ql.Date(1,1,2024) fixing_frequency = ql.Monthly inflation_curve = ql.PiecewiseZeroInflation( today,index.lastFixingDate(), fixing_frequency, ql.Actual365Fixed(), helpers ) hicp = ql.EUHICP(ql.ZeroInflationTermStructureHandle(inflation_curve)) hicp.fixing(ql.Date(2, ql.February, 2074)) hicp.fixing(ql.Date(11, ql.May, 2024)) pd.DataFrame(inflation_curve.nodes(), columns=["node", "rate"]).style.format ( {"rate": "{:.4%}"} ) df = pd.DataFrame(real_curve.nodes(), columns=["node", "yreal"]) df['nom'] = df.node.map(lambda x: 1.0/nominal_curve.discount(x)) df['real'] = df.node.map(lambda x: 1.0/real_curve.discount(x)) df['imp_inf'] = df.node.map(lambda x: 1.0/implied_infl_curve.discount(x)) df['base_val'] = df.node.map(lambda x: ql.CPI.laggedFixing(index, df.loc[0, 'node'], observation_lag, ql.CPI.Linear)) df['inf'] = df.node.map(lambda x: ql.CPI.laggedFixing(hicp, x, observation_lag, ql.CPI.Linear)) df['inf'] = df.inf/df.base_val df['diff'] = df.inf/df.imp_inf-1 df Am I on the right track or am I missing something? Any feedback would be much appreciated. |