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