Reusing QuantLib Rate Helpers for Daily Curve Bootstrapping
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.