|
From: Peter C. <pca...@gm...> - 2018-07-30 19:32:53
|
Hi Sonderak,
it avoids building parts of the value dates schedule that is not used for valuation, see ql/cashflows/overnightindexedcoupon.cpp, line 138 (notice the caveat does not apply to rate helpers, since they are rebuilt when the evaluation date changes)
/* For the coupon's valuation only the first and last future valuation
dates matter, therefore we can avoid to construct the whole series
of valuation dates, a front and back stub will do. However notice
that if the global evaluation date moves forward it might run past
the front stub of valuation dates we build here (which incorporates
a grace period of 7 business after the evluation date). This will
lead to false coupon projections (see the warning the class header). */
if (telescopicValueDates) {
// build optimised value dates schedule: front stub goes
// from start date to max(evalDate,startDate) + 7bd
Date evalDate = Settings::instance().evaluationDate();
tmpEndDate = overnightIndex->fixingCalendar().advance(
std::max(startDate, evalDate), 7, Days, Following);
tmpEndDate = std::min(tmpEndDate, endDate);
}
Schedule sch =
MakeSchedule()
.from(startDate)
// .to(endDate)
.to(tmpEndDate)
.withTenor(1 * Days)
.withCalendar(overnightIndex->fixingCalendar())
.withConvention(overnightIndex->businessDayConvention())
.backwards();
valueDates_ = sch.dates();
if (telescopicValueDates) {
// build optimised value dates schedule: back stub
// contains at least two dates
Date tmp = overnightIndex->fixingCalendar().advance(
endDate, -1, Days, Preceding);
if (tmp != valueDates_.back())
valueDates_.push_back(tmp);
tmp = overnightIndex->fixingCalendar().adjust(
endDate, overnightIndex->businessDayConvention());
if (tmp != valueDates_.back())
valueDates_.push_back(tmp);
}
Best Regards
Peter
> On 30 Jul 2018, at 09:48, Abhishek KANNUR <kan...@gm...> wrote:
>
> HI Peter,
>
> Thank you for your response.
> I set telescopicValueDates to true, which sped up things considerably :) Thank you again.
>
> Could you let me know what telescopicValueDates does?
>
> Best,
> sonderak
>
> On Fri, Jul 27, 2018 at 9:35 PM, Peter Caspers <pca...@gm... <mailto:pca...@gm...>> wrote:
> Hi Sonderak,
>
> 1. that’s because it creates a schedule with lots of dates; this is not really necessary for the purpose of curve bootstrapping and you can avoid it by setting the parameter telescopicValueDates to true which will speed up things (without changing the resulting curve of course)
>
> 2. I don’t think so; the rate helper will be rebuild the next time the curve building is triggered and that will be time consuming again for the reason given in 1. - however this is not what you are observing, right, so I don’t know; does that observation persist with telescopicValueDates set to true?
>
> Best Regards
> Peter
>
>> On 27 Jul 2018, at 11:17, Abhishek KANNUR <kan...@gm... <mailto:kan...@gm...>> wrote:
>>
>> Hello,
>> 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 <https://quant.stackexchange.com/questions/39671/why-we-should-specify-the-evaluation-date-when-using-quantlib-yield-curve-and-w/40158>, I have come to understand that setting new dates changes reference dates for all the instances declared before.
>>
>> I am trying to understand :
>>
>> 1. Why creating instances of OISRateHelpers takes longer than creating instances of SwapRateHelpers/DepositRateHelpers?
>>
>> 2. 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!
>>
>> PS: I have posted this question on Quant.StackExchange.com <https://quant.stackexchange.com/questions/40939/quantlib-in-python-execution-time-with-oisratehelper-compared-to-swap-deposit> <http:/> as well.
>>
>>
>> Best,
>> Sonderak
>>
>> ------------------------------------------------------------------------------
>> Check out the vibrant tech community on one of the world's most
>> engaging tech sites, Slashdot.org <http://slashdot.org/>! http://sdm.link/slashdot_______________________________________________ <http://sdm.link/slashdot_______________________________________________>
>> QuantLib-users mailing list
>> Qua...@li... <mailto:Qua...@li...>
>> https://lists.sourceforge.net/lists/listinfo/quantlib-users <https://lists.sourceforge.net/lists/listinfo/quantlib-users>
>
>
|