Diagnosing a SOFR Futures Curve Bootstrap Error in QuantLib
Summary
This question examines a QuantLib error encountered while bootstrapping a yield curve from quarterly SOFR futures. The supplied setup builds futures rate helpers and a piecewise linear zero curve; the reported failure refers to a missing SOFR overnight fixing on a date around Juneteenth. The author asks whether the holiday causes the error or whether the curve is misconstructed.
The accepted answer says that, assuming the evaluation date and known historical SOFR fixings are configured, the behavior was a library bug fixed in QuantLib version 1.35. Updating to that release is presented as the remedy. The answer is brief and does not detail the underlying defect or provide a reproducible fix for older versions, so the diagnosis depends on the stated assumptions about curve setup and fixings.
Key ideas
- A SOFR futures curve bootstrap can fail when the required overnight fixing is missing from the calculation path.
- The answer attributes this case to a QuantLib bug fixed in version 1.35.
- Curve setup should include the evaluation date and known historical SOFR fixings.
- The response does not explain the bug’s internals or an older-version workaround.
Tags
Full text
# Is Juneteenth causing a runtime error when bootstrapping curve off of futures?
# Is Juneteenth causing a runtime error when bootstrapping curve off of futures?
```
import io
import pandas as pd
import QuantLib as ql
data = io.StringIO("""Start Date Days Ticker Last Rate 6 Mo 1 Yr 2 Yr 3 Yr 5 Yr 7 Yr 10 Yr
08/01/2024 48 SFRM4 94.6524 5.3476 5.062 4.741 4.236 3.988 3.823 3.843
09/18/2024 91 SFRU4 94.955 5.045 4.872 4.551 4.11 3.902 3.777 3.815
12/18/2024 91 SFRZ4 95.36 4.64 4.472 4.217 3.904 3.764 3.708 3.775
03/19/2025 91 SFRH5 95.745 4.255 4.129 3.952 3.748 3.662 3.663 3.751
06/18/2025 91 SFRM5 96.04 3.96 3.874 3.762 3.64 3.594 3.64 3.741
09/17/2025 91 SFRU5 96.25 3.75 3.697 3.635 3.571 3.555 3.633 3.742
12/17/2025 91 SFRZ5 96.39 3.61 3.581 3.554 3.53 3.536 3.639 3.752
03/18/2026 91 SFRH6 96.48 3.52 3.508 3.506 3.509 3.533 3.654 3.767
06/17/2026 91 SFRM6 96.535 3.465 3.465 3.479 3.502 3.541 3.674 3.786
09/16/2026 91 SFRU6 96.565 3.435 3.442 3.467 3.506 3.556 3.698 3.807
12/16/2026 91 SFRZ6 96.58 3.42 3.432 3.465 3.518 3.579 3.724 3.827
03/17/2027 91 SFRH7 96.585 3.415 3.432 3.471 3.537 3.608 3.752 3.85
06/16/2027 91 SFRM7 96.58 3.42 3.437 3.485 3.561 3.64 3.782
09/15/2027 91 SFRU7 96.575 3.425 3.45 3.504 3.59 3.675 3.811
12/15/2027 91 SFRZ7 96.555 3.445 3.471 3.531 3.625 3.711 3.84
03/15/2028 98 SFRH8 96.535 3.465 3.495 3.562 3.665 3.75 3.871
06/21/2028 91 SFRM8 96.505 3.495 3.528 3.599 3.709 3.79 3.9
09/20/2028 91 SFRU28 96.47 3.53 3.566 3.638 3.752 3.827 3.928
12/20/2028 91 SFRZ28 96.43 3.57 3.606 3.683 3.794 3.863 3.952
03/21/2029 91 SFRH29 96.39 3.61 3.644 3.732 3.836 3.898 3.976
06/20/2029 91 SFRM29 96.355 3.645 3.692 3.779 3.875 3.93
09/19/2029 91 SFRU29 96.295 3.705 3.75 3.827 3.912 3.959
12/19/2029 91 SFRZ29 96.24 3.76 3.796 3.867 3.944 3.985
03/20/2030 91 SFRH30 96.205 3.795 3.831 3.901 3.971 4.009
06/19/2030 91 SFRM30 96.17 3.83 3.864 3.932 3.996 4.028
09/18/2030 91 SFRU30 96.14 3.86 3.897 3.959 4.016 4.045
12/18/2030 91 SFRZ30 96.105 3.895 3.924 3.984 4.034 4.057
03/19/2031 91 SFRH31 96.085 3.915 3.944 4.004 4.053 4.07
06/18/2031 91 SFRM31 96.065 3.935 3.965 4.022 4.067
09/17/2031 91 SFRU31 96.045 3.955 3.985 4.037 4.078
12/17/2031 91 SFRZ31 96.025 3.975 4 4.048 4.084
03/17/2032 91 SFRH32 96.015 3.985 4.008 4.066 4.093
06/16/2032 91 SFRM32 96.01 3.99 4.015 4.075
09/15/2032 91 SFRU32 96 4 4.043 4.084
12/15/2032 91 SFRZ32 95.955 4.045 4.053 4.085
03/16/2033 91 SFRH33 95.98 4.02 4.041 4.085
06/15/2033 98 SFRM33 95.98 4.02 4.034
09/21/2033 91 SFRU33 95.995 4.005 4.046
12/21/2033 84 SFRZ33 95.95 4.05
""")
df = pd.read_csv(data, sep='\t')
df['Start Date'] = pd.to_datetime(df['Start Date'])
df['contract_month'] = df['Ticker'].apply(lambda x: 3 if x[3] == 'H' else 6 if x[3] == 'M' else 9 if x[3] == 'U' else 12)
```
Creating the curve
```
dates = []
rates = []
futures = []
for i, row in df.iterrows():
price = row['Last']
year = row['Start Date'].year
future = ql.SofrFutureRateHelper(price, row['contract_month'], year, ql.Quarterly)
futures.append(future)
curve = ql.PiecewiseLinearZero(2, ql.TARGET(), futures, ql.Actual360())
yts = ql.YieldTermStructureHandle(curve)
engine = ql.DiscountingBondEngine(yts)
```
Viewing the nodes
```
curve.nodes()
```
Error
```
RuntimeError: 1st iteration: failed at 1st alive instrument, pillar September 18th, 2024, maturity September 18th, 2024, reference date August 1st, 2024: missing rate on June 19th, 2024 for index SOFRON Actual/360
```
Do I get the above error because the contract start date begins on Juneteenth? Is this a bug in the library or am I failing to construct the curve properly?
## Answer by Luigi Ballabio (score 3, accepted)
https://quant.stackexchange.com/a/80157
It's not shown in your code, but I assume you're also setting the evaluation date and adding to the SOFR instance the known past fixings, right?
If that's the case, what you're hitting was in fact a bug in the library; it was fixed in version 1.35, released last week. Upgrading should fix the problem.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.