I'm trying to construct a EUR implied curve using FxSwapRateHelper
in QuantLib. I can't get the dates to match those in FRD in Bloomberg without an error. If I use 0 for fixingDays
in FxSwapRateHelper
, the helpers have the correct pillar dates, but I get:
RuntimeError: 1st iteration: failed at 2nd alive instrument, pillar November 11th, 2022, maturity November 11th, 2022, reference date November 8th, 2022: negative time (-0.00555556) given
when I try to get the nodes with impl_eur_curve.nodes()
.
Here's my code that works (to get the dates), setting fixingDays = 2
will provide the nodes with incorrect dates.
import QuantLib as ql
def div_by_100(num: float) -> float:
return round(num / 100, 8)
def div_by_10000(num: float) -> float:
return round(num / 10000, 8)
def get_sofr_curve():
calendar = ql.UnitedStates()
day_count = ql.Actual360()
sofr_nodes = [
(ql.Date(10, 11, 2022), 1.0),
(ql.Date(14, 11, 2022), 0.9995779559741442),
(ql.Date(17, 11, 2022), 0.9992622391373923),
(ql.Date(25, 11, 2022), 0.998418255878124),
(ql.Date(1, 12, 2022), 0.9977891072112927),
(ql.Date(12, 12, 2022), 0.9966338580706822),
(ql.Date(10, 1, 2023), 0.9931999845745259),
(ql.Date(10, 2, 2023), 0.9894144200226028),
(ql.Date(10, 3, 2023), 0.9857474068271933),
(ql.Date(10, 4, 2023), 0.9815857913821401),
(ql.Date(10, 5, 2023), 0.9775559534359467),
(ql.Date(12, 6, 2023), 0.9730736992505439),
(ql.Date(10, 7, 2023), 0.969199512546515),
(ql.Date(10, 8, 2023), 0.9650337705526182),
(ql.Date(11, 9, 2023), 0.9607535382151032),
(ql.Date(10, 10, 2023), 0.9568766271055823),
(ql.Date(10, 11, 2023), 0.9528272435541583),
(ql.Date(10, 5, 2024), 0.9304818696115411),
(ql.Date(12, 11, 2024), 0.9103278844920676),
(ql.Date(10, 11, 2025), 0.8762437729086668),
(ql.Date(10, 11, 2026), 0.8452465324284965),
(ql.Date(10, 11, 2027), 0.8157050701514608),
(ql.Date(10, 11, 2028), 0.7867887092729156),
(ql.Date(13, 11, 2029), 0.7588199446126264),
(ql.Date(12, 11, 2030), 0.732069060453959),
(ql.Date(10, 11, 2031), 0.7059251588134711),
(ql.Date(10, 11, 2032), 0.6799049355113026),
(ql.Date(10, 11, 2034), 0.630423657538001),
(ql.Date(10, 11, 2037), 0.5627945692471997),
(ql.Date(10, 11, 2042), 0.47577127579148787),
(ql.Date(12, 11, 2047), 0.41562782173279106),
(ql.Date(12, 11, 2052), 0.3686829168732379),
(ql.Date(10, 11, 2062), 0.30503422701272137),
(ql.Date(10, 11, 2072), 0.2659786086494946),
]
dates, dfs = zip(*sofr_nodes)
result = ql.DiscountCurve(dates, dfs, day_count, calendar)
result.enableExtrapolation()
return result
def get_impl_eur_curve(sofr_curve):
fixingDays = 0
calendar = ql.TARGET()
business_convention = ql.Following
day_count = ql.Actual360()
end_of_month = True
base_ccy_is_collateral = False
collateral_curve = sofr_curve
joint_calendar = ql.JointCalendar(calendar, collateral_curve.calendar())
eur_prices = [
("spot", 0.9957),
("3d", 0.671),
("1w", 4.74),
("2w", 9.52),
("3w", 14.34),
("1m", 20.65),
("2m", 53.72),
("3m", 75.55),
("4m", 95.54),
("5m", 120.7),
("6m", 139.66),
("9m", 202.46),
("1y", 260.53),
("15m", 321.25),
("18m", 370.5),
("2y", 458.02),
("3y", 605.12),
("4y", 731.64),
("5y", 848.4),
("6y", 968.05),
("7y", 1067.13),
("8y", 1165.0),
("9y", 1267.0),
("10y", 1368.5),
]
spot = ql.SimpleQuote(eur_prices.pop(0)[1])
eur_fwds = [(tenor, ql.SimpleQuote(pts)) for tenor, pts in eur_prices]
helpers = [
ql.DepositRateHelper(
ql.QuoteHandle(
ql.DerivedQuote(ql.QuoteHandle(ql.SimpleQuote(1.401)), div_by_100)
),
ql.Period("1d"),
0,
calendar,
business_convention,
end_of_month,
day_count,
)
]
helpers += [
ql.FxSwapRateHelper(
ql.QuoteHandle(ql.DerivedQuote(ql.QuoteHandle(quote), div_by_10000)),
ql.QuoteHandle(spot),
ql.Period(tenor),
fixingDays,
calendar,
business_convention,
end_of_month,
base_ccy_is_collateral,
ql.YieldTermStructureHandle(collateral_curve),
calendar,
)
for tenor, quote in eur_fwds
]
for helper in helpers:
print(helper.pillarDate())
result = ql.PiecewiseLogCubicDiscount(0, calendar, helpers, day_count)
result.enableExtrapolation()
return result
def main():
price_date = ql.Date(4, 11, 2022)
ql.Settings.instance().setEvaluationDate = price_date
sofr_curve = get_sofr_curve()
impl_eur_curve = get_impl_eur_curve(sofr_curve)
if __name__ == "__main__":
main()