3
$\begingroup$

I am wondering whether it is possible somehow to speed up my script using ratehelpers/bootstrapping. I am creating for every days a new ratehelper. Is there a way building up just once the ratehelper and then bootstrap day by day? If yes how would an example look like?

Update:

My Data:

fact_date product_type    fra_start_period    maturity    tenor   quote   currency
2015-02-09    Deposit 0D  1D      0.081   JPY
2015-02-09    OIS     1W      0.0713  JPY
2015-02-09    OIS     3W      0.0663  JPY
2015-02-09    OIS     1M      0.0656  JPY
2015-02-09    OIS     2M      0.0613  JPY
2015-02-09    OIS     3M      0.06    JPY
2015-02-09    OIS     10M     0.05    JPY
2015-02-09    OIS     12M     0.0494  JPY
2015-02-09    OIS     5Y      0.1563  JPY
2015-02-09    OIS     6Y      0.2025  JPY
2015-02-09    OIS     7Y      0.2481  JPY
2015-02-09    OIS     10Y     0.3806  JPY
2015-02-09    OIS     15Y     0.6888  JPY
2015-02-09    OIS     20Y     0.965   JPY
2015-02-09    OIS     25Y     1.1081  JPY
2015-02-09    OIS     30Y     1.1831  JPY
2015-02-10    Deposit 0D  1D      0.074   JPY
2015-02-10    OIS     1W      0.0725  JPY
2015-02-10    OIS     3W      0.0688  JPY
2015-02-10    OIS     1M      0.0681  JPY
2015-02-10    OIS     2M      0.0625  JPY
2015-02-10    OIS     3M      0.0606  JPY
2015-02-10    OIS     10M     0.0531  JPY
2015-02-10    OIS     12M     0.0525  JPY
2015-02-10    OIS     5Y      0.1719  JPY
2015-02-10    OIS     6Y      0.2244  JPY
2015-02-10    OIS     7Y      0.2744  JPY
2015-02-10    OIS     10Y     0.4169  JPY
2015-02-10    OIS     15Y     0.7269  JPY
2015-02-10    OIS     20Y     1.0044  JPY
2015-02-10    OIS     25Y     1.1475  JPY
2015-02-10    OIS     30Y     1.2225  JPY

My Code:

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

def Convert(Period):
    unit =[]
    if Period[-1:] == 'D':
        unit = ql.Days
    elif Period[-1:] == 'M':
        unit = ql.Months
    elif Period[-1:] == 'W':
        unit = ql.Weeks
    elif Period[-1:] == 'Y':
        unit = ql.Years
    period_object = ql.Period(int(Period[:-1]), unit)
    return period_object

def qlStr2periodNumber(Period):
    if Period[-1:] == "D":
        period_unit = int(Period[:1])
    elif Period[-1:] == 'M':
        period_unit = int(Period[:1])
    elif Period[-1:] == 'W':
        period_unit = int(Period[:1])
    elif Period[-1:] == 'Y':
        period_unit = int(Period[:1])
    elif Period == '':
        period_unit = int(0)
    else:
        raise Exception('(qlStr2periodNumber) Period'+ Period + 'not recognized!')
    return period_unit

def Datetime2ql(date):
    dates = ql.DateParser.parseFormatted(date,'%Y-%m-%d')
    return dates

def ql2Datetime(date):
    dates = dt.datetime(date.year(), date.month(), date.dayOfMonth())
    return dates

Index_OIS = ql.OvernightIndex("Tonar", 2, ql.JPYCurrency(), ql.Japan(), ql.Actual365Fixed())

data = pd.read_csv('C:/Book1.csv').fillna('')

quote_map = {}
helpers = []

for product_type, fra_start_period, maturity, quote in zip(data.product_type, data.fra_start_period, data.maturity, data.quote): 
    quotes = ql.SimpleQuote(quote/100)
    if product_type == 'Deposit':
        helper = ql.DepositRateHelper(ql.QuoteHandle(quotes),
                                      ql.Period(2,ql.Days),
                                      qlStr2periodNumber(fra_start_period),
                                      ql.Japan(),
                                      ql.ModifiedFollowing,
                                      False,
                                      ql.Actual365Fixed()                                      
                                      )
    elif product_type == 'OIS':
        helper = ql.OISRateHelper(2,
                                  Convert(maturity),
                                  ql.QuoteHandle(quotes),
                                  Index_OIS)

    helpers.append(helper)        

    quote_map[(product_type,fra_start_period,maturity)] = quotes

curve = ql.PiecewiseCubicZero(0, ql.Japan(), helpers, ql.Actual365Fixed())

for current_date in zip(data.fact_date):
    time = current_date[0]
    ql.Settings.instance().evaluationDate = Datetime2ql(str(time))
    for row in data:
        quote_map[(product_type, fra_start_period, maturity, quote)].setValue(quote)
$\endgroup$

1 Answer 1

5
$\begingroup$

Yes, it's possible to reduce the number of objects you'll create; whether this will speed up your calculations depend on how much time is taken by their creation and how much is taken by the actual bootstrapping. In any case:

  1. When you create your rate helpers, make sure you're passing quote objects and not simple numbers; that is, something like

    q1 = SimpleQuote(0.0125)
    h1 = DepositRateHelper(QuoteHandle(q1), index)
    

    and not

    h1 = DepositRateHelper(0.0125, index)
    
  2. when you create your curve, don't specify its reference date explicitly; instead, specify it as a number of days (possibly 0) from the global evaluation date; that is, something like

    curve = PiecewiseFlatForward(0, UnitedStates(), helpers, day_counter)
    

    if you want the reference date to equal the evaluation date, or

    curve = PiecewiseFlatForward(2, UnitedStates(), helpers, day_counter)
    

    if you want it to start spot. This way, the reference date of the curve will move when the evaluation date changes.

  3. Now you're setup. When you want to change to a new date, you'll write

    Settings.instance().evaluationDate = new_today
    

    and for each quote,

    q1.setValue(new_value)
    

    the curve will detect the changes and recalculate accordingly.

In pseudocode, the idea would be to have a setup like:

quote_map = {}
helpers = []
for type, fra_start_date, maturity in ...  # extract unique helper data
    quote = SimpleQuote(...)
    if type == 'Deposit':
        helper = ...
    else:
        ...
    helpers.append(helper)
    quote_map[(type,fra_start_date,maturity)] = quote
curve = PiecewiseCubicZero(2, Japan(), helpers, day_counter)

and then the loop over the dates:

for current_date in ...
    # Don't build stuff, just change date and quotes
    Settings.instance().evaluationDate = current_date
    for row in ...
        quote_map[(type,fra_start_date,maturity)].setValue(value)
    # now the curve is updated and you can use it
$\endgroup$
10
  • $\begingroup$ Thanks a lot for the hint. Somehow I am still struggeling concerning the helper. I updated my question with an example since not just the quotes but also the maturity is my problem. How can I handle this because having a quote object how do I link the proper maturity to it? Maybe you can show it based on my example above? Thanks. $\endgroup$
    – MCM
    Mar 8, 2018 at 9:56
  • $\begingroup$ I don't think that's a problem. You're passing a tenor (2 years, for instance), not a hard-coded maturity. When the evaluation date changes, the helper recalculates its maturity date as well. Thus, you can create your objects outside the loop (one helper per quoted tenor, and a curve that doesn't take a hard-coded date; see my point 2 above) and then loop over the dates in your data and set evaluation date and quotes. $\endgroup$ Mar 8, 2018 at 10:21
  • $\begingroup$ Thanks for the answer. I tried the way and updated the code above. Somehow it fails: 'NotImplementedError: Wrong number or type of arguments for overloaded function 'new_QuoteHandle'. Possible C/C++ prototypes are: Handle< Quote >::Handle(boost::shared_ptr< Quote > const &) Handle< Quote >::Handle()' $\endgroup$
    – MCM
    Mar 8, 2018 at 10:54
  • $\begingroup$ You're passing to QuoteHandle the whole list of quotes, not just the quote you last created. $\endgroup$ Mar 8, 2018 at 11:49
  • $\begingroup$ Now I got it. Hope the updated code above is the one you mean how to handle it. $\endgroup$
    – MCM
    Mar 8, 2018 at 14:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.