I have been working on valuation of interest rate swaps using dual curve bootstrapping. And for this I use OISRateHelper to create a discount term structure using OIS rates. The entire code below :
import QuantLib as ql
ql.IndexManager.instance().clearHistories()
import math
import numpy as np
import pandas as pd
import datetime as dt
#### INPUTS
date0 = dt.datetime.strptime("2012-07-05", "%Y-%m-%d")
date1 = dt.datetime.strptime("2012-07-06", "%Y-%m-%d")
calculation_dates = [ql.Date(date0.day,date0.month,date0.year), ql.Date(date1.day,date1.month,date1.year)]
fwd_start = 0
ois_mat = [ql.Period(x) for x in ['1D', '2W', '1M', '2M', '3M', '4M', '5M', '6M',
'7M', '8M', '9M', '10M', '11M', '12M', '18M', '2Y',
'30M', '3Y', '4Y', '5Y', '6Y', '7Y', '8Y', '9Y', '10Y',
'11Y', '12Y', '15Y', '20Y', '25Y', '30Y', '35Y', '40Y',
'50Y']]
ois_rates0 = [0.331, 0.162, 0.1525, 0.138, 0.136, 0.134, 0.133, 0.132, 0.135, 0.133, 0.134, 0.133, 0.134, 0.135, 0.146, 0.168, 0.197, 0.263, 0.419, 0.622, 0.8364, 1.006, 1.1625, 1.302, 1.429, 1.544, 1.64, 1.839, 1.93, 1.964, 1.999, 2.0465, 2.097, 2.1675]
ois_rates1 = [0.329, 0.145, 0.134, 0.129, 0.13, 0.1235, 0.125, 0.145, 0.126, 0.12, 0.127, 0.122, 0.123, 0.125, 0.141, 0.165, 0.192, 0.253, 0.402, 0.595, 0.795, 0.976, 1.131, 1.27, 1.4049, 1.517, 1.611, 1.811, 1.901, 1.94, 1.963, 2.0265, 2.091, 2.173]
ois_dict0 = dict(zip(ois_mat, [x/100 for x in ois_rates0]))
ois_dict1 = dict(zip(ois_mat, [x/100 for x in ois_rates1]))
list_ois_rates_dict = [ois_dict0, ois_dict1]
deposit_mat = [ql.Period(1, ql.Months), ql.Period(3, ql.Months), ql.Period(6, ql.Months)]#, ql.Period(12, ql.Months)]
deposit_rates0 = [0.00362, 0.00641, 0.0092]
deposit_rates1 = [0.00255, 0.00549, 0.00831]
deposit_dict0 = dict(zip(deposit_mat, deposit_rates0))
deposit_dict1 = dict(zip(deposit_mat, deposit_rates1))
list_deposit_rates_dict = [deposit_dict0, deposit_dict1]
swap_mats = [ql.Period(x, ql.Years) for x in [1,2,3,4,5,6,7,8,9,10,12,15,20,25,30]]
swap_rates0 = [0.00515, 0.00806, 0.00883, 0.01029, 0.01213, 0.0139, 0.01544, 0.01677, 0.01793, 0.01897, 0.02073, 0.02232, 0.02279, 0.02293, 0.02307]
swap_rates1 = [0.00465, 0.00744, 0.00802, 0.00931, 0.01104, 0.01288, 0.0145, 0.01591, 0.01713, 0.01824, 0.02006, 0.0217, 0.02229, 0.02246, 0.02263]
swap_dict0 = dict(zip(swap_mats, swap_rates0))
swap_dict1 = dict(zip(swap_mats, swap_rates1))
list_swap_curve_rates_dict = [swap_dict0, swap_dict1]
libor_rates_dict = deposit_dict0.copy()
swap_rates_dict = swap_dict0.copy()
swap_libor_tenors = [ql.Period(x, ql.Months) for x in [6,6,6,6,6,6,6,6,6,6,6,6,6,6,6]]
settlement_days = 0
face_value = 100
fixed_day_count = ql.Thirty360()
float_day_count = ql.Actual360()
ois_day_count = ql.Actual360()
calendar = ql.TARGET()
list_fixed_coupon_frequency = [ql.Annual for x in range(len(swap_libor_tenors))]
currency = ql.EURCurrency()
spread = 0
business_convention = ql.ModifiedFollowing
date_generation = ql.DateGeneration.Forward
end_of_month = False
### CALCULATIONS
fixed_npv = []
float_npv = []
swap_npv = []
swap_fair_rate = []
ql.IndexManager.instance().clearHistories()
date_t0 = calculation_dates[0]
ql.Settings.instance().evaluationDate = date_t0
effective_start_date = calendar.advance(date_t0, settlement_days + fwd_start, ql.Days)
discount_term_structure = ql.RelinkableYieldTermStructureHandle()
forecast_term_structure = ql.RelinkableYieldTermStructureHandle()
oindex = ql.OvernightIndex("", settlement_days, currency, calendar, ois_day_count, discount_term_structure)
######################
t5 = dt.datetime.now()
######################
ois_quote_map = {}
ois_helpers_t0 = []
for r,m in zip(list_ois_rates_dict[0].values(), list_ois_rates_dict[0].keys()):
quote = ql.SimpleQuote(r)
helper= ql.OISRateHelper(settlement_days, m, ql.QuoteHandle(quote), oindex)
ois_helpers_t0.append(helper)
ois_quote_map[m]=quote
######################
t6 = dt.datetime.now()
######################
deposit_quote_map = {}
deposit_helpers_t0 = []
for r,m in zip(list_deposit_rates_dict[0].values(), list_deposit_rates_dict[0].keys()):
quote = ql.SimpleQuote(r)
helper = ql.DepositRateHelper(ql.QuoteHandle(quote),m,settlement_days,calendar,
business_convention,end_of_month,float_day_count)
deposit_helpers_t0.append(helper)
deposit_quote_map[m] = quote
swap_ibor_indices = [ ql.IborIndex("", x, settlement_days, currency, calendar, business_convention, False, float_day_count,
forecast_term_structure) for x in swap_libor_tenors]
for sib in swap_ibor_indices:
sib.addFixing(date_t0, libor_rates_dict[sib.tenor()])
swap_quote_map = {}
swap_helpers_t0 = []
for r,m, ibor_index,f in zip(list_swap_curve_rates_dict[0].values(), list_swap_curve_rates_dict[0].keys(),
swap_ibor_indices, list_fixed_coupon_frequency):
quote = ql.SimpleQuote(r)
helper = ql.SwapRateHelper(ql.QuoteHandle(quote),m,calendar,f,business_convention,fixed_day_count, ibor_index,
ql.QuoteHandle(ql.SimpleQuote(0)),ql.Period(0, ql.Days), discount_term_structure,
settlement_days)
swap_helpers_t0.append(helper)
swap_quote_map[m] = quote
######################
t10 = dt.datetime.now()
######################
helpers_t0 = deposit_helpers_t0 + swap_helpers_t0
swap_curve_t0 = ql.PiecewiseLogCubicDiscount(settlement_days, calendar, helpers_t0, fixed_day_count)
discount_curve_t0 = ql.PiecewiseLogCubicDiscount(settlement_days, calendar, ois_helpers_t0, fixed_day_count)
swap_curve_t0.enableExtrapolation()
discount_curve_t0.enableExtrapolation()
discount_term_structure.linkTo(discount_curve_t0)
forecast_term_structure.linkTo(swap_curve_t0)
swap_engine = ql.DiscountingSwapEngine(discount_term_structure)
list_irs = []
for i in range(len(swap_mats)):
swap_rate = list(swap_rates_dict.values())[i]
fixed_tenor = ql.Period(list_fixed_coupon_frequency[i])
float_tenor = swap_ibor_indices[i].tenor()
fixed_schedule = ql.Schedule(effective_start_date, calendar.advance(effective_start_date, list(swap_rates_dict.keys())[i]),
fixed_tenor, calendar,
business_convention, business_convention,
date_generation, end_of_month)
float_schedule = ql.Schedule(effective_start_date, calendar.advance(effective_start_date, list(swap_rates_dict.keys())[i]),
float_tenor, calendar,
business_convention, business_convention,
date_generation, end_of_month)
irs_temp = ql.VanillaSwap(ql.VanillaSwap.Receiver, face_value, fixed_schedule, swap_rate, fixed_day_count,
float_schedule, swap_ibor_indices[i], spread, float_day_count)
irs_temp.setPricingEngine(swap_engine)
if fwd_start > 0:
fair_swap_rate = irs_temp.fairRate()
irs = ql.VanillaSwap(ql.VanillaSwap.Receiver, face_value, fixed_schedule, fair_swap_rate, fixed_day_count,
float_schedule, swap_ibor_indices[i], spread, float_day_count)
irs.setPricingEngine(swap_engine)
list_irs.append(irs)
else:
list_irs.append(irs_temp)
fixed_npv.append([x.fixedLegNPV() for x in list_irs])
float_npv.append([x.floatingLegNPV() for x in list_irs])
swap_npv.append([x.NPV() for x in list_irs])
swap_fair_rate.append([x.fairRate() for x in list_irs])
print("ois helpers on date0: " + str((t6 - t5)))
print("other helpers on date0: " + str((t10 - t6)))
######################
t19 = dt.datetime.now()
######################
tdelta = dt.timedelta()
if len(calculation_dates) > 1:
for i in range(1, len(calculation_dates)):
######################
t19_1 = dt.datetime.now()
######################
date_ti = calculation_dates[i]
ql.Settings.instance().evaluationDate = date_ti
######################
t19_2 = dt.datetime.now()
######################
for k in list_ois_rates_dict[i].keys():
ois_quote_map[k].setValue(list_ois_rates_dict[i][k])
for k in list_deposit_rates_dict[i].keys():
deposit_quote_map[k].setValue(list_deposit_rates_dict[i][k])
for k in list_swap_curve_rates_dict[i].keys():
swap_quote_map[k].setValue(list_swap_curve_rates_dict[i][k])
fixed_npv.append([x.fixedLegNPV() for x in list_irs])
float_npv.append([x.floatingLegNPV() for x in list_irs])
swap_npv.append([x.NPV() for x in list_irs])
swap_fair_rate.append([x.fairRate() for x in list_irs])
######################
tdelta = tdelta + (t19_2 - t19_1)
######################
######################
t20 = dt.datetime.now()
######################
print("Calculations for other dates: " + str((t20 - t19)))
print("Setting evaluation dates for other dates: " + str(tdelta))
I have calculated execution time for creating an instance of OISRateHelpers and compared them to execution time for creating instances of DepositRateHelpers and SwapRateHelpers.
ois helpers on date0: 0:00:00.299389
other helpers on date0: 0:00:00.000999
The output shows that there is a major difference between.
Also, when recalculating the swaps, on other dates (other than date0), it's the ql.Settings.instance().evaluationDate which takes up more time.
Calculations for other dates: 0:00:09.596271
Setting evaluation dates for other dates: 0:00:09.554356
From this thread, I have come to understand that setting new dates changes reference dates for all the instances declared before.
I am trying to understand :
Why creating instances of OISRateHelpers takes longer than creating instances of SwapRateHelpers/DepositRateHelpers?
The fact that changing evaluation dates is time consuming, is it possible that OISRateHelper is creating 'redundant' dependencies which require notifications for changes in evaluation dates as well? Thus making it time consuming when resetting evaluation dates in ql.Settings?
Thank you!