Bootstrap
The bootstrap script contains functions from bootstrapping a term structure from a set of data points.Curve Types
Extedning a Curve
Full Script
import QuantLib as ql
from datetime import date, datetime
from dateutil.relativedelta import relativedelta
from enum import StrEnum
'''
values = [('2025-01-01', 0.01),]
'''
def add_spreads(curve, values, zeros=True):
base_curve_handle = ql.YieldTermStructureHandle(curve)
# 2. Define non-constant spread nodes (Dates and Spreads)
# In this example, the spread starts at 50 bps and increases to 100 bps over 5 years
spread_dates = [
ql.Date(int(date.split('-')[2]), int(date.split('-')[1]),int(date.split('-')[0]))
for date,_ in values
]
spread_quotes = [
ql.SimpleQuote(x)
for _,x in values
]
spread_handles = [ql.QuoteHandle(q) for q in spread_quotes]
spreaded_curve = None
#or use ql.SpreadedForwardInterpolatedTermStructure
# 3. Create the spreaded yield term structure
if zeros == True : spreaded_curve = ql.SpreadedLinearZeroInterpolatedTermStructure(
base_curve_handle,
spread_handles,
spread_dates
)
else: spreaded_curve = ql.SpreadedForwardInterpolatedTermStructure(
base_curve_handle,
spread_handles,
spread_dates
)
return spreaded_curve
def add_static_spread(curve, value):
base_curve_handle = ql.YieldTermStructureHandle(curve)
# 3. Define the spread (e.g., 50 basis points)
#spread_quote = ql.SimpleQuote(0.0050)
spread_quote = ql.SimpleQuote(value)
spread_handle = ql.QuoteHandle(spread_quote)
# 4. Create the spreaded term structure
spread_curve = ql.ZeroSpreadedTermStructure(base_curve_handle, spread_handle)
return spread_curve
class OvernightIndex(StrEnum):
SOFR="SOFR" #ql.DateGeneration.Backward
EONIA="EONIA"
def ois(market_data=[], depo_data=[], index = OvernightIndex.SOFR, today=None):
if today == None: today = date.today().isoformat()
if len(market_data)>0 and isinstance(market_data[0], list):
market_data = [(x[0],x[1]) for x in market_data]
if len(depo_data)>0 and isinstance(depo_data[0], list):
depo_data = [(x[0],x[1]) for x in depo_data]
split = today.split('-')
today = ql.Date(int(split[2]), int(split[1]), int(split[0]))
# 2. Define the index (e.g., SOFR or EONIA)
# If using a customized index, pass a blank Handle for bootstrapping
overnight_index = ql.Eonia()
if index == 'SOFR': overnight_index = ql.Sofr()
# 4. Construct Rate Helpers
# Map market rates into QuantLib Quote handles and instantiate OIS helpers
settlement_days = 2
rate_helpers = []
for tenor_str, rate in market_data:
tenor = ql.Period(tenor_str)
# QuantLib expects rates as decimals (e.g., 0.0525 for 5.25%)
quote_handle = ql.QuoteHandle(ql.SimpleQuote(rate / 100.0))
helper = ql.OISRateHelper(
settlement_days,
tenor,
quote_handle,
overnight_index,
paymentFrequency=ql.Annual,
# telescopicValueDates speed up bootstrap calculation loops significantly
telescopicValueDates=True
)
rate_helpers.append(helper)
# 5. Build the Piecewise Bootstrapped Curve
# Combine traits (e.g., Discount) and interpolation methods (e.g., LogLinear)
day_count = ql.Actual360()
ois_curve = ql.PiecewiseLogLinearDiscount(
today,
rate_helpers,
day_count
)
# Optional: Enable extrapolation past the furthest market node
ois_curve.enableExtrapolation()
return ois_curve
def continuous_zero_curve(data, today=None, day_counter = ql.Actual365Fixed(), calendar = ql.TARGET()):
if today == None: today = date.today().isoformat()
if len(data)>0 and isinstance(data[0], list):
data = [(x[0],x[1]) for x in data]
calculation_date = ql.Date(26, 6, 2026)
dates = [
ql.Date(int(date.split('-')[2]),int(date.split('-')[1]),int(date.split('-')[0]))
for date,_ in data
]
rates = [
rate
for date,rate in data
]
'''# 2. Set dates and continuously compounded rates
dates = [calculation_date,
calculation_date + ql.Period(1, ql.Years),
calculation_date + ql.Period(10, ql.Years)]
# Continuously compounded zero rates
rates = [0.04, 0.04, 0.04] '''
# 3. Build the curve using Continuous compounding
continuous_curve = ql.ZeroCurve(
dates,
rates,
day_counter,
calendar,
ql.Linear(),
ql.Continuous,
ql.Annual # Frequency is ignored for continuous compounding
)
return continuous_curve
'''
data is in the format [('2025-01-01', 0.01),]
'''
def treasury(data, today=None):
if len(data)>0 and isinstance(data[0], list):
data = [(x[0],x[1]) for x in data]
if today == None: today = date.today().isoformat()
today_date = datetime.fromisoformat(today)
tsplit = today.split('-')
calc_date = ql.Date(int(tsplit[2]), int(tsplit[1]), int(tsplit[0]))
# Define common settings
calendar = ql.UnitedStates(ql.UnitedStates.GovernmentBond)
day_count = ql.ActualActual(ql.ActualActual.ISDA)
business_convention = ql.Following
face_amount = 100
settlement_days = 0
'''
In QuantLib, you use DepositRateHelper to model Treasury Bills (T-Bills) at the short end of a yield curve
because both instruments are fundamentally discount securities.
T-Bills do not pay periodic coupons. Instead, they are sold at a discount to face value and mature at par,
meaning their yield calculation maps perfectly to the simple compounding and actual/360 day-counting behavior built into DepositRateHelper.
'''
# Helpers for a T-bills
bill_helpers = []
#for key in [x for x in data if ('date' not in x and 'Y' not in x and '.' not in x)]:
for item in [x for x in data if ('M' in x[0] and '.' not in x[0])]:
#period = key[:-1]
period = item[0]
bill_quote = ql.QuoteHandle(ql.SimpleQuote(item[1]))
bill_helper = ql.DepositRateHelper(
bill_quote,
ql.Period(int(period[:-1]), ql.Months),
settlement_days,
calendar,
business_convention,
True,
day_count
)
bill_helpers.append(bill_helper)
pass
# Helpers for coupon bonds (Example: 2-year and 5-year Treasury notes)
# Assumes you have their par yields
bond_helpers = []
#for key in [x for x in data if ('date' not in x and 'M' not in x)]:
for item in [x for x in data if ('Y' in x[0])]:
issue_date = calc_date
period = item[0]
maturity = today_date + relativedelta(years=int(period[:-1]))
maturity_date = ql.Date.from_date(maturity)
schedule = ql.Schedule(
issue_date, maturity_date, ql.Period(ql.Semiannual), calendar,
business_convention, business_convention,
ql.DateGeneration.Backward, False
)
quote = ql.SimpleQuote(100.0)
bond_helper = ql.FixedRateBondHelper(
ql.QuoteHandle(quote),
settlement_days,
face_amount,
schedule,
[float(item[1])],
day_count,
business_convention
)
bond_helpers.append(bond_helper)
helpers = bill_helpers + bond_helpers
# Combine into a bootstrapped yield curve
yield_curve = ql.PiecewiseLogLinearDiscount(
calc_date,
helpers,
day_count
)
# Enable daily recalculation
yield_curve.enableExtrapolation()
return yield_curve