0
$\begingroup$

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()
$\endgroup$
1
  • $\begingroup$ I think I ran into issues discussed in section 1.5 (Term structures and their reference dates) in the QuantLib Python Cookbook. I instantiated the curve using today as the evaluation date and then set the evaluation date to the historical date above. When I change the evaluation date in this way, I get the correct nodes. I'll try to update the underlying quotes with the historical values to see if the curve updates to provide the expected nodes. $\endgroup$ Nov 9, 2022 at 14:26

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.