Diagnosing QuantLib Swap Curve Bootstrap Failures from Rate Units
Summary
This exchange explains why adding a five-year euro swap quote causes a QuantLib yield curve bootstrap to fail. The key issue is quote units: rates expressed as percentages must be converted to decimal form before being supplied to the helpers. The example’s swap quotes were entered as values such as 0.2331 for 23.31%, although the intended rate was 0.2331%. The resulting implausibly high long-dated rates prevent the solver from finding a bracketed root.
The accepted answer recommends correcting the units for both deposits and swaps, after which the curve should bootstrap as expected. This is a focused debugging lesson rather than a general treatment of curve construction. It does not examine other possible causes of bootstrap failures or provide independent validation across market data sets.
Key ideas
- Interest rates supplied as percentages must be represented as decimal fractions when the library expects decimals.
- The sample quotes mistakenly interpret percentage values as whole rates, making long-term rates extreme.
- A failed root search can result from inconsistent or incorrectly scaled market quotes.
- The same unit check should be applied to deposit quotes as well as swap quotes.
Tags
Full text
# Quantlib bootstraping fails on 5y swap
# Quantlib bootstraping fails on 5y swap
I'm trying to build a euro swap curve with real up to date data. I should say that examples provided in github work fine. as soon as I add the 5y swap, I got the following error :
```
RuntimeError: 1st iteration: failed at 14th alive instrument, maturity September 28th, 2020, reference date September 28th, 2015: root not bracketed: f[-1,1] -> [3.260558e-001,5.904525e-002]
```
edit (error with error lines) :
```
RuntimeError: void __thiscall QuantLib::IterativeBootstrap<class QuantLib::PiecewiseYieldCurve<struct QuantLib::ForwardRate,class QuantLib::BackwardFlat,class QuantLib::IterativeBootstrap> >::calculate(void) const:
d:\PycharmProjects\quantlib\QuantLib\ql/termstructures/iterativebootstrap.hpp(217):
1st iteration: failed at 14th alive instrument, maturity September 28th, 2020, reference date September 28th, 2015: double __thiscall QuantLib::Solver1D<class QuantLib::Brent>::solve<class QuantLib::BootstrapError<class QuantLib::PiecewiseYieldCurve<struct QuantLib::ForwardRate,class QuantLib::BackwardFlat,class QuantLib::IterativeBootstrap> >>(const class QuantLib::BootstrapError<class QuantLib::PiecewiseYieldCurve<struct QuantLib::ForwardRate,class QuantLib::BackwardFlat,class QuantLib::IterativeBootstrap> > &,double,double,double,double) const:
d:\PycharmProjects\quantlib\QuantLib\ql/math/solver1d.hpp(202):
root not bracketed: f[-1,1] -> [3.260558e-001,5.904525e-002]
```
I have to say that I get no error if I remove the 5y swap. I have QL_NEGATIVE_RATES in userconfig.hpp that isn't commented.
the piece of code I'm using is here.
thanks
```
import QuantLib as ql
import datetime
calendar = ql.TARGET()
settlementDays = 2
now = datetime.datetime.now()
today = calendar.adjust(ql.Date(now.day, now.month, now.year))
ql.Settings.instance().evaluationDate = today
settlementDate = calendar.advance(today, settlementDays, ql.Days)
# market quotes
deposits = {(1, ql.Weeks): -0.141,
(1, ql.Months): -0.107,
(2, ql.Months): -0.066,
(3, ql.Months): -0.039,
(6, ql.Months): 0.033,
(9, ql.Months): 0.083,
(12, ql.Months): 0.147}
futures = {ql.Date(16, 3, 2016): 100.045,
ql.Date(15, 6, 2016): 100.055,
ql.Date(21, 9, 2016): 100.06,
ql.Date(21, 12, 2016): 100.055,
ql.Date(15, 3, 2017): 100.04}
swaps = {(2, ql.Years): 0.0557,
(3, ql.Years): 0.1275,
(4, ql.Years): 0.2331,
#(5, ql.Years): 0.3523
}
# convert them to Quote objects
for n, unit in deposits.keys():
deposits[(n, unit)] = ql.SimpleQuote(deposits[(n, unit)])
for d in futures.keys():
futures[d] = ql.SimpleQuote(futures[d])
for n, unit in swaps.keys():
swaps[(n, unit)] = ql.SimpleQuote(swaps[(n, unit)])
dayCounter = ql.Actual360()
depositHelpers = [
ql.DepositRateHelper(ql.QuoteHandle(deposits[(n, unit)]), ql.Period(n, unit), settlementDays, calendar,
ql.ModifiedFollowing, False, dayCounter) for n, unit in sorted(deposits.iterkeys())]
dayCounter = ql.Actual360()
futuresHelpers = [
ql.FuturesRateHelper(ql.QuoteHandle(futures[d]), d, 3, calendar, ql.ModifiedFollowing, True, dayCounter,
ql.QuoteHandle(ql.SimpleQuote(0.0))) for d in sorted(futures.keys())]
settlementDays = 2
fixedLegFrequency = ql.Annual
fixedLegTenor = ql.Period(1, ql.Years)
fixedLegAdjustment = ql.Unadjusted
fixedLegDayCounter = ql.Thirty360()
floatingLegFrequency = ql.Semiannual
floatingLegTenor = ql.Period(6, ql.Months)
floatingLegAdjustment = ql.ModifiedFollowing
swapHelpers = [ql.SwapRateHelper(ql.QuoteHandle(swaps[(n, unit)]), ql.Period(n, unit), calendar, fixedLegFrequency,
fixedLegAdjustment, fixedLegDayCounter, ql.Euribor6M()) for n, unit in
swaps.keys()]
# term structure handles
discountTermStructure = ql.RelinkableYieldTermStructureHandle()
forecastTermStructure = ql.RelinkableYieldTermStructureHandle()
def printHelper(x):
print('{} | {}'.format(x.latestDate(), x.quote().value()))
def printH(t):
print('\n======')
print t
for i in list(t):
printHelper(i)
printH(depositHelpers)
printH(futuresHelpers)
printH(swapHelpers)
helpers = depositHelpers[:-2] + futuresHelpers + swapHelpers
printH(helpers)
depoFuturesSwapCurve = ql.PiecewiseFlatForward(settlementDate, helpers, ql.Actual360())
print depoFuturesSwapCurve.dates()
for c in depoFuturesSwapCurve.dates():
print depoFuturesSwapCurve.discount(c)
```
## Answer by Luigi Ballabio (score 7, accepted)
https://quant.stackexchange.com/a/20893
You're not the first to trip on this, and unfortunately the fact that the provided example is from a different era doesn't help.
Quite simply, you're not writing rates correctly. The 5-years swap rate, 0.3523%, must be written in decimal form as 0.003523. The same goes for the deposit rates.
As your code is now, you're writing that the 4-years rate is 23.31% and the 5-years rate is 35.23%, and the bootstrap code fails to find a solution that accounts for the extreme variation.
Writing the rates correctly should allow you to bootstrap the curve as expected.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.