Diagnosing a One-Year EUR Swap Rate Mismatch in Rateslib
Summary
The document describes an attempt to build a EUR interest-rate curve from Bloomberg tenor rates with Rateslib, then price swaps at different maturities. The curve uses the EUR IRS convention, log-linear interpolation, and discount factors calibrated from the quoted rates. The central question is why the one-year swap rate differs materially from the Bloomberg figure while the two-year and five-year results are close.
The comparison suggests that curve construction and matching market conventions may be relevant, but the document does not provide a diagnosis or a confirmed fix. It notes that the input rates may not exactly match those used in Bloomberg’s swap-pricing screen. A reader should treat the displayed discrepancies as a troubleshooting case rather than evidence that a specific Rateslib setting is wrong. Short-tenor swaps can be especially sensitive to effective dates, schedules, conventions, and instrument definitions, but those possibilities are not tested in the text.
Key ideas
- The example calibrates a EUR swap curve from Bloomberg tenor rates using Rateslib.
- The one-year swap quote differs substantially from the Bloomberg comparison while longer-tenor examples are close.
- The author suspects that the Bloomberg input rates and swap-pricing screen may use different market data.
- The document raises a pricing and convention question but does not establish its cause.
Tags
Full text
# Rateslib - Pricing 1y EUR vs 6M (EUSA01)
# Rateslib - Pricing 1y EUR vs 6M (EUSA01)
I am using the rateslib python library to try to price some European swaps. It seems to be working for most tenors aside from the 1y for some reason.
The code I am using is below:
```
from rateslib import *
from tia.bbg import LocalTerminal
import pandas as pd
today = dt.today()
start = add_tenor(today,"2b","F",get_calendar("tgt"))
maturity = add_tenor(start,"1Y","F",get_calendar("tgt"))
eurcurve = '45'
eurcurve_id = 'YCSW' + eurcurve.zfill(4) + ' Index'
eurresp = LocalTerminal.get_reference_data(eurcurve_id,'CURVE_TENOR_RATES')
eurdf = eurresp.as_frame()
eurtenors = eurdf['CURVE_TENOR_RATES'].iloc[0]['Tenor'].to_list()
eurrates = eurdf['CURVE_TENOR_RATES'].iloc[0]['Mid Yield'].to_list()
eurdata = pd.DataFrame({"Term": eurtenors,
"Rate":eurrates})
eurdata["Termination"] = [add_tenor(start,_,"F","tgt") for _ in eurdata["Term"]]
eureur = Curve(
id = "eureur",
convention=defaults.spec["eur_irs"]["convention"],
calendar=defaults.spec["eur_irs"]["calendar"],
modifier=defaults.spec["eur_irs"]["modifier"],
interpolation="log_linear",
nodes={
**{today:1.0},
**{_:1.0 for _ in eurdata["Termination"]},
}
)
eur_kws = dict(
spec="eur_irs",
effective = start,
curves ="eureur"
)
eursolver = Solver(
curves=[eureur],
instruments=[IRS(termination=_,**eur_kws)for _ in eurdata["Termination"]],
s = eurdata["Rate"],
instrument_labels=eurdata["Term"],
id = "eur_rates"
)
eurdata["DF"] = [float(eureur[_]) for _ in eurdata["Termination"]]
eurirs = IRS(
notional = -10e6,
termination = maturity,
**eur_kws
)
eurswaprate = eurirs.rate(solver=eursolver)
print(eurswaprate)
print(eurdata)
```
so the output from this code, looking at the rates and the discount factors vs bbg, look fine to me
Here are the BBG values
and here are the ones in 'eurdata' from my code
so these have a slightly bigger margin of error than i see in the sofr curve example in the rateslib docs but i think this is because the rates that i pull from bbg dont match exactly what the 'curves' tab in SWPM has.
Anyway, when i then print the rate out on this 1y swap i get some pretty different values.
BBG = 3.68
Me = 3.451
I thought this was just something i was doing wrong (and it likely still is). However what i found that was weird is that it seems to match much much closer (within a bp) when looking at essentially any other tenor. for example the 2y and 5y below
2y BBG = 3.4185
2y Me = 3.418
5y BBG = 3.03145
5y Me = 3.032
So my question is:
Is there something fundamentally incorrect with how I am either using the rateslib library or how i am approaching this problem? I have done my best to try and piece together from the docs but struggling to get thereShown 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.