Python

Python 中的 QuantLib:與交換/存款 RateHelper 相比,使用 OISRateHelper 的執行時間

  • July 30, 2018

我一直在使用雙曲線引導法對利率掉期進行估值。為此,我使用 OISRateHelper 使用 OIS 費率創建折扣期限結構。下面的整個程式碼:

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))

我計算了創建 OISRateHelpers 實例的執行時間,並將它們與創建 DepositRateHelpers 和 SwapRateHelpers 實例的執行時間進行了比較。

ois helpers on date0: 0:00:00.299389
other helpers on date0: 0:00:00.000999

輸出顯示兩者之間存在重大差異。

此外,在重新計算掉期時,在其他日期(date0 除外),ql.Settings.instance().evaluationDate會佔用更多時間。

Calculations for other dates: 0:00:09.596271
Setting evaluation dates for other dates: 0:00:09.554356

從這個執行緒中,我了解到設置新日期會更改之前聲明的所有實例的參考日期。

我試圖理解:

  1. 為什麼創建 OISRateHelpers 實例比創建 SwapRateHelpers/DepositRateHelpers 實例需要更長的時間?
  2. 事實上,更改評估日期非常耗時,OISRateHelper 是否有可能創建“冗餘”依賴項,這也需要通知評估日期的更改?因此在 ql.Settings 中重置評估日期會很耗時?

謝謝!

我向 quantlib 郵件列表發送了同樣的問題。解決方案是 telescopicValueDates = True為 OISRateHelpers 設置。

OISRateHelper 創建一個包含很多日期的時間表;對於曲線引導而言,這並不是真正必要的,您可以通過將參數 telescopicValueDates 設置為 true 來避免它,這將加快速度(當然不會改變結果曲線)。

引用自:https://quant.stackexchange.com/questions/40939