I hope all is well. I'm currently trying to match Bloomberg's bootstrapping algorithm for 6M Euribor for curve date 31/10/2023. I can fully match it for ESTR, so I assume I'm setting up my 6M Euribor curve wrong. I have mainly two questions:
import rateslib as rl
import pandas as pd
from datetime import datetime
########################################################################################################################
# ESTR
estr_data = pd.DataFrame(
{
"Term": ["1W", "2W", "1M", "2M", "3M", "4M", "5M", "6M", "7M", "8M", "9M", "10M", "11M", "1Y", "18M",
"2Y", "3Y", "4Y", "5Y", "6Y", "7Y", "8Y", "9Y", "10Y", "11Y", "12Y", "15Y", "20Y", "25Y", "30Y", "40Y", "50Y"],
"Rate": [3.89709997177124, 3.89860010147095, 3.90205001831055, 3.90700006484985, 3.91499996185303, 3.91925001144409,
4.91324996948242, 3.89674997329712, 3.87725019454956, 3.85500001907348, 3.82899999618531, 3.79800009727478,
4.76999998092652, 3.73399996757507, 3.50099992752076, 3.32200002670288, 3.10925006866456, 3.0239999294281,
2.99950003623962, 3.01049995422364, 3.02999997138977, 3.0605001449585, 3.09625005722046, 3.13425016403199,
3.18300008773804, 3.21140003204346, 3.2859001159668, 3.27040004730225, 3.17679977416992, 3.08430004119873,
2.96099996566772, 2.85899996757508],
}
)
estr_data["Termination"] = [rl.add_tenor(datetime(2023, 11, 2), _, "F", "bus") for _ in estr_data["Term"]]
estr = rl.Curve(
id="eurrfr",
convention="Act360",
calendar="bus",
modifier="MF",
interpolation="log_linear",
nodes={
**{datetime(2023, 10, 31): 1.0}, # <- this is today's DF,
**{_: 1.0 for _ in estr_data["Termination"]},
},
)
estr_args = dict(effective=datetime(2023, 11, 2), spec="eur_irs", curves="eurrfr")
solver_estr = rl.Solver(
curves=[estr],
instruments=[rl.IRS(termination=_, **estr_args) for _ in estr_data["Termination"]],
s=estr_data["Rate"],
instrument_labels=estr_data["Term"],
id="eurrfr",
)
estr_data["DF"] = [float(estr[_]) for _ in estr_data["Termination"]]
with pd.option_context("display.float_format", lambda x: "%.6f" % x):
print(estr_data)
########################################################################################################################
# EURIBOR
import pandas as pd
euribor_data = pd.DataFrame(
{
"Term": ["6M", "7M", "8M", "9M", "10M", "11M", "12M", "15M",
# "18M",
"2Y", "3Y", "4Y", "5Y", "6Y", "7Y", "8Y", "9Y", "10Y", "11Y", "12Y", "15Y", "20Y", "25Y", "30Y", "40Y",
"50Y"],
"Rate": [4.092, 4.045000076, 4.007999897, 3.946000099, 3.878000021, 3.817500114, 3.732999802, 3.486999989,
# 3.252000809,
3.592400074, 3.37925005, 3.28760004, 3.259000301, 3.25999999, 3.274549961, 3.292999983, 3.322000027,
3.346000195, 3.374000072, 3.396999836, 3.430500031, 3.372499943, 3.245500088, 3.143749714, 2.965250015,
2.817500114],
}
)
euribor_data["Termination"] = [rl.add_tenor(datetime(2023, 11, 2), _, "F", "bus") for _ in euribor_data["Term"]]
euribor_args = dict(curves="euribor6m", frequency="S", termination="6m", calendar="tgt")
euribor_args2 = dict(
curves=["euribor6m", "eurrfr"],
frequency="A",
calendar="tgt",
convention="Act360",
leg2_frequency="S",
leg2_convention="act360",
leg2_fixing_method="ibor",
)
euribor6m_curve = rl.Curve(
id="euribor6m",
convention="Act360",
calendar="bus",
modifier="MF",
interpolation="log_linear",
nodes={
**{datetime(2023, 10, 31): 1.0}, # <- this is today's DF,
**{_: 1.0 for _ in euribor_data["Termination"]},
},
)
instruments = [
rl.FRA(datetime(2023, 11, 2), **euribor_args), # 6M
rl.FRA(datetime(2023, 12, 2), **euribor_args), # 7M
rl.FRA(datetime(2024, 1, 2), **euribor_args), # 8M
rl.FRA(datetime(2024, 2, 2), **euribor_args), # 9M
rl.FRA(datetime(2024, 3, 2), **euribor_args), # 10M
rl.FRA(datetime(2024, 4, 2), **euribor_args), # 11M
rl.FRA(datetime(2024, 5, 2), **euribor_args), # 12M
rl.FRA(datetime(2024, 8, 2), **euribor_args), # 15M
# rl.FRA(datetime(2023, 11, 2), **euribor_args), # 18M
rl.IRS(datetime(2023, 11, 2), "2y", **euribor_args2),
rl.IRS(datetime(2023, 11, 2), "3y", **euribor_args2),
rl.IRS(datetime(2023, 11, 2), "4y", **euribor_args2),
rl.IRS(datetime(2023, 11, 2), "5y", **euribor_args2),
rl.IRS(datetime(2023, 11, 2), "6y", **euribor_args2),
rl.IRS(datetime(2023, 11, 2), "7y", **euribor_args2),
rl.IRS(datetime(2023, 11, 2), "8y", **euribor_args2),
rl.IRS(datetime(2023, 11, 2), "9y", **euribor_args2),
rl.IRS(datetime(2023, 11, 2), "10y", **euribor_args2),
rl.IRS(datetime(2023, 11, 2), "11y", **euribor_args2),
rl.IRS(datetime(2023, 11, 2), "12y", **euribor_args2),
rl.IRS(datetime(2023, 11, 2), "15y", **euribor_args2),
rl.IRS(datetime(2023, 11, 2), "20y", **euribor_args2),
rl.IRS(datetime(2023, 11, 2), "25y", **euribor_args2),
rl.IRS(datetime(2023, 11, 2), "30y", **euribor_args2),
rl.IRS(datetime(2023, 11, 2), "40y", **euribor_args2),
rl.IRS(datetime(2023, 11, 2), "50y", **euribor_args2),
]
solver = rl.Solver(
pre_solvers=[solver_estr],
curves=[euribor6m_curve],
instruments=instruments,
s=euribor_data["Rate"],
instrument_labels=euribor_data["Term"],
id="euribor6m"
)
euribor_data["DF"] = [float(euribor6m_curve[_]) for _ in euribor_data["Termination"]]
with pd.option_context("display.float_format", lambda x: "%.6f" % x):
print(euribor_data)