Skip to content
All library documents

QuantLib Treasury Curve Bootstrapping: Supply Bond Prices to Helpers

Article Quant Q&A · Author: dbojanin

Summary

The document examines a failed attempt to construct a US Treasury zero-coupon curve in QuantLib using deposit and fixed-rate bond helpers. The key diagnostic is to build the curve from deposit helpers alone: this isolates the source of the reported root-bracketing error to the bond instruments rather than the short-term deposits.

The explanation identifies a mismatch in the bond-helper inputs. A FixedRateBondHelper expects a market price as well as the bond’s coupon rate, but the example feeds the same spot-rate value into both roles. That makes the calibration inconsistent and prevents the solver from finding a root. The answer demonstrates that changing the price input allows execution, but the illustrative prices are hypothetical and do not constitute market data. The document does not provide a complete methodology for selecting Treasury instruments, stripping coupons, or validating the resulting curve, so a usable curve still requires appropriate observed prices and conventions.

Key ideas

  • Build the curve with deposit helpers alone to check whether the error originates in the bond helpers.
  • A fixed-rate bond helper requires a bond price and coupon rate as separate inputs.
  • Using spot-rate values as both prices and coupon rates creates inconsistent calibration inputs.
  • The example’s replacement prices are hypothetical and should not be treated as observed Treasury prices.

Tags

Full text
# How to bootstrap the zero coupon curve for US treasuries


# How to bootstrap the zero coupon curve for US treasuries












Here is my understanding of the process:

- Capture price of most recently sold gov security at each tenor of the curve (reference treasuryDirect)

- For coupon paying securities, (i.e. tenor>2yr) you must strip the interest gained from coupon from the price and recalc the security

- Calculate yield for each tenor in the new curve using zero coupon prices

- Interpolate the curve for 360 nodes (representing months up to 30 years)

What am I doing wrong?

Hello, I need help trying to construct a zero coupon curve using treasury spot rates, I am following the cookbook on quantlib but struggling to understand why I am throwing the error:

```
_QuantLib.YieldTermStructure_zeroRate(self, *args)  RuntimeError: 1st iteration: failed at 4th alive instrument, pillar February 3rd, 2025, maturity February 3rd, 2025, reference date February 2nd, 2024: root not bracketed: f[0.593053,1.59535] -> [-6.243439e+01,-1.681992e+02]
```

Code:

```
    import QuantLib as ql
    # Spot rates provided
    spot_rates = [5.49, 5.51, 5.43, 5.42, 5.22, 4.81, 4.36, 4.14, 3.99, 4.02, 4.03, 4.33, 4.22]  # in percent
    
    # Corresponding tenors in months and years for deposits and bonds
    deposit_tenors_months = [1, 3, 6]
    bond_tenors_years = [1, 2, 3, 5, 7, 10, 20, 30]
    
    # Convert annual spot rates to QuantLib QuoteHandle objects and adjust for percentage
    spot_rate_handles = [ql.QuoteHandle(ql.SimpleQuote(rate / 100.0)) for rate in spot_rates]
    
    # Assuming today's date is the settlement date
    settlement_date = ql.Date(2, 2, 2024)
    ql.Settings.instance().evaluationDate = settlement_date
    
    # Define day count and calendar for deposits
    calendar = ql.UnitedStates(ql.UnitedStates.GovernmentBond)
    day_count_deposit = ql.Actual360()
    
    # Deposit rate helpers
    deposit_helpers = [
     ql.DepositRateHelper(spot_rate_handles[i],
     ql.Period(tenors, ql.Months),
     2,  # fixing days
     calendar,
     ql.ModifiedFollowing,
     False,
     day_count_deposit)
     for i, tenors in enumerate(deposit_tenors_months)
    ]
    
    # Day count for bonds
    day_count_bond = ql.ActualActual(ql.ActualActual.ISDA)
    
    # Bond rate helpers
    bond_helpers = []
    for i, tenors in enumerate(bond_tenors_years, start=3):  # Starting from the 4th element in spot_rates
     maturity_date = settlement_date + ql.Period(tenors, ql.Years)
     schedule = ql.Schedule(settlement_date,
     maturity_date,
     ql.Period(ql.Annual),
     calendar,
     ql.Unadjusted,
     ql.Unadjusted,
     ql.DateGeneration.Backward,
     False)
     bond_helpers.append(
     ql.FixedRateBondHelper(spot_rate_handles[i],
     3,  # settlement days
     100.0,  # face amount
     schedule,
                                   [spot_rates[i] / 100.0],  # coupon rate
     day_count_bond,
     ql.ModifiedFollowing,
     100.0)  # redemption
        )
    
    # Combine deposit and bond helpers
    rate_helpers = deposit_helpers + bond_helpers
    
    # Construct the curve
    curve = ql.PiecewiseLogCubicDiscount(settlement_date,
     rate_helpers,
     ql.ActualActual(ql.ActualActual.ISDA))
    
    # Extract zero rates for months 1-360
    zero_rates = []
    for month in range(1, 361):
     date = settlement_date + ql.Period(month, ql.Months)
     yrs = curve.dayCounter().yearFraction(settlement_date, date)
     zero_rate = curve.zeroRate(yrs, ql.Compounded, ql.Annual).rate()
     zero_rates.append(zero_rate)
    
    # Print some of the zero rates
    print("Zero Rates for the first 12 months:")
    for month, rate in enumerate(zero_rates[:12], start=1):
     print(f"Month {month}: {rate*100:.2f}%")
```

## Answer by Xiarpedia (score 2)

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

By executing the following code:

```
# Construct the curve
curve = ql.PiecewiseLogCubicDiscount(
    settlement_date, deposit_helpers, ql.ActualActual(ql.ActualActual.ISDA)
)

date, rates = zip(*curve.nodes())
```

I concluded that the deposits did not cause this issue. Thus moving onwards with the FixedRateBondHelpers, it is expecting prices and coupon rates as input. However, those two variables are set equally as of now, causing the error as the solver can not find a solution. If we hypothetically set the following price:

```
prices= [
    ql.QuoteHandle(ql.SimpleQuote(rate+90)) for rate in spot_rates
]
```

and in the for loop use:

```
bond_helpers.append(
    ql.FixedRateBondHelper(
        prices[i],
        3,  # settlement days
        100.0,  # face amount
        schedule,
        [spot_rates[i] / 100.0],  # coupon rate
        day_count_bond,
        ql.ModifiedFollowing,
        100.0,
        )  # redemption
    )
```

The code will execute and find a solution. Here are a few links that I found useful when investigating FixedRateBondHelper:

- Documentation

- Example usage of FixedRateBondHelper

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.