Skip to content
All library documents

Reusing QuantLib Rate Helpers for Daily Curve Bootstrapping

Article Quant Q&A · Author: MCM

Summary

The document explains how to reuse QuantLib rate helpers and a term-structure curve when bootstrapping curves for successive dates. Rather than recreating all helper objects for every observation date, create quote objects and pass handles to them when constructing deposit and overnight-index swap helpers. Build the curve using a settlement-day offset from the global evaluation date so its reference date can move as that date changes.

For each new market date, update QuantLib’s evaluation date and set each quote object to the day’s market rate; the curve then detects the updates and recalculates as needed. This can reduce object creation, although the answer cautions that speed gains depend on whether construction or bootstrapping dominates runtime. The source presents a workflow and pseudocode rather than a complete corrected script. Correct mapping of market data to quotes and appropriate helper conventions remain necessary for reliable curves.

Key ideas

  • Rate helpers can reference mutable quote objects through quote handles.
  • A curve defined by a day offset from the evaluation date can roll forward as the date changes.
  • Updating the evaluation date and quote values allows the curve to recalculate without rebuilding every object.
  • Reusing helpers may improve runtime, but the gain depends on the relative cost of setup and bootstrapping.
  • Market data must be mapped to the appropriate quote objects and instrument conventions.

Tags

Full text
# Using RateHelper (bootstrapping) and Speed up in Quantlib Python


# Using RateHelper (bootstrapping) and Speed up in Quantlib Python












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 `

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

## Answer by Luigi Ballabio (score 5, accepted)

https://quant.stackexchange.com/a/38667

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:

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

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

```
h1 = DepositRateHelper(0.0125, index)
```

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

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

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

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

```
Settings.instance().evaluationDate = new_today
```

```
q1.setValue(new_value)
```

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

Shown in full with attribution under the source's licence. Licence: CC BY-SA 4.0 (Stack Exchange)

This summary was written by Stratmill's research agent from the original; it is not a copy of the source.