attack68 / rateslib

A fixed income library for pricing bonds and bond futures, and derivatives such as IRS, cross-currency and FX swaps. Contains tools for full Curveset construction with market standard optimisers and automatic differention (AD) and risk sensitivity calculations including delta and cross-gamma.
https://rateslib.readthedocs.io/en/stable/
Other
119 stars 21 forks source link

DOC: plotting curves with tenors longer than 1y and simple rates #246

Open henrikparssinen opened 2 months ago

henrikparssinen commented 2 months ago

Hey,

I'm trying to recreate the SOFR curve from swaps, and after roughly getting the discount factors right, it seems like the curve fails to plot longer tenors. The data I have from Bloomberg is below:

px_last term security_typ issue_dt maturity DF USOSFR1Z BGN Curncy 5.3256 1W SWAP 2024-07-03 2024-07-10 0.998966 USOSFR2Z BGN Curncy 5.3286 2W SWAP 2024-07-03 2024-07-17 0.997932 USOSFR3Z BGN Curncy 5.3315 3W SWAP 2024-07-03 2024-07-24 0.996900 USOSFRA BGN Curncy 5.3346 1M SWAP 2024-07-03 2024-08-05 0.995134 USOSFRB BGN Curncy 5.3352 2M SWAP 2024-07-03 2024-09-03 0.990895 USOSFRC BGN Curncy 5.3221 3M SWAP 2024-07-03 2024-10-03 0.986582 USOSFRD BGN Curncy 5.3017 4M SWAP 2024-07-03 2024-11-04 0.982066 USOSFRE BGN Curncy 5.281 5M SWAP 2024-07-03 2024-12-03 0.978048 USOSFRF BGN Curncy 5.2537 6M SWAP 2024-07-03 2025-01-03 0.973850 USOSFRG BGN Curncy 5.2215 7M SWAP 2024-07-03 2025-02-03 0.969759 USOSFRH BGN Curncy 5.187 8M SWAP 2024-07-03 2025-03-03 0.966172 USOSFRI BGN Curncy 5.1508 9M SWAP 2024-07-03 2025-04-03 0.962276 USOSFRJ BGN Curncy 5.1123 10M SWAP 2024-07-03 2025-05-05 0.958355 USOSFRK BGN Curncy 5.0765 11M SWAP 2024-07-03 2025-06-03 0.954891 USOSFR1 BGN Curncy 5.0408 1Y SWAP 2024-07-03 2025-07-03 0.951377 USOSFR1F BGN Curncy 4.7629 18M SWAP 2024-07-03 2026-01-05 0.931077 USOSFR2 BGN Curncy 4.5898 2Y SWAP 2024-07-03 2026-07-06 0.912894 USOSFR3 BGN Curncy 4.3325 3Y SWAP 2024-07-03 2027-07-06 0.879158 USOSFR4 BGN Curncy 4.1827 4Y SWAP 2024-07-03 2028-07-03 0.847588 USOSFR5 BGN Curncy 4.0946 5Y SWAP 2024-07-03 2029-07-03 0.816886 USOSFR6 BGN Curncy 4.0448 6Y SWAP 2024-07-03 2030-07-03 0.786845 USOSFR7 BGN Curncy 4.0146 7Y SWAP 2024-07-03 2031-07-03 0.757600 USOSFR8 BGN Curncy 3.9953 8Y SWAP 2024-07-03 2032-07-06 0.728914 USOSFR9 BGN Curncy 3.9847 9Y SWAP 2024-07-03 2033-07-05 0.701375 USOSFR10 BGN Curncy 3.9795 10Y SWAP 2024-07-03 2034-07-03 0.674692 USOSFR12 BGN Curncy 3.9802 12Y SWAP 2024-07-03 2036-07-03 0.623241 USOSFR15 BGN Curncy 3.9852 15Y SWAP 2024-07-03 2039-07-05 0.552861 USOSFR20 BGN Curncy 3.9536 20Y SWAP 2024-07-03 2044-07-05 0.457430 USOSFR25 BGN Curncy 3.8634 25Y SWAP 2024-07-03 2049-07-06 0.388550 USOSFR30 BGN Curncy 3.768 30Y SWAP 2024-07-03 2054-07-06 0.335664 USOSFR40 BGN Curncy 3.554 40Y SWAP 2024-07-03 2064-07-03 0.267253 USOSFR50 BGN Curncy 3.3385 50Y SWAP 2024-07-03 2074-07-03 0.228792>

image

timestamps = data[data['maturity'] >= start_date + pd.DateOffset(months = 6)]['maturity'].tolist()

new_timestamps = [timestamps[0]] * 4 + timestamps[1:-1] + [timestamps[-1]] * 4

curve = rl.Curve(
    id="my_curve",
    convention= 'Act360',
    interpolation= 'log_linear',
    calendar = 'nyc',
    **t,
    nodes={
        **{data['issue_dt'].iloc[0]: 1.0},  # <- this is today's DF,
        **{_: 1.0 for _ in 
           data['maturity']
          },
    }
)

args = dict(spec='sofr', curves=curve)
solver = rl.Solver(
    curves=[curve],
    instruments = [rl.IRS(termination=maturity, effective=effective_date, **args) for maturity, effective_date in zip(data["maturity"], data["issue_dt"])],
    s=data["px_last"],
    id="rates"
)
data["DF"] = [float(curve[_]) for _ in data["maturity"]]

The 1-year curve looks to be on par with Bloomberg: image image

However, when the tenor is 25 years, it fails by quite a bit: image image

attack68 commented 2 months ago

A Curve object does not know the conventions of a swap. It does not plot a swap rate. It plots a curve rate. Normally this is used for tenors such as 1d, 1m, 3m, 6m, 12m. Tenors longer than 12m will are expressed as single period simple rates.

Your 5y discount factor is 0.816521. The first point on a plot for a 5Y rate would be calculated with: (1.0 / 0.816521 -1) (1 / 5.0) 100 = 4.494%

Which is the ration of two discount factors at start and end scaled by a day count fraction between the dates.

To return a different calculation, e,g.

would all require a definition and a different type of calculation.

These additional calculations are all currently not defined, nor developed.