Pricing IBOR Interpolated Stub Periods#
Stubs on derivatives can often cause problems in derivatives pricing. The rateslib framework developed is designed to make the UI and pricing process simple. That is, an IRS, for example, has two Legs and each Leg might have two Curves for pricing; a forecasting Curve to project fixings (which is not necessary for the fixed leg), and a discounting Curve to discount the projected cashflows.
This all works well except for IBOR stub periods, which might need two forecasting curves on a Leg
to project two different IBOR rates and interpolate them to derive the resultant fixing for the
FloatPeriod
.
If we setup a pricing model we can explore the possibilities. This model creates Euribor 1M and 3M Curves and a separate ESTR discounting Curve.
In [1]: solver = Solver(
...: curves=[
...: Curve({dt(2022, 1, 1): 1.0, dt(2023, 1, 1): 1.0}, id="estr"),
...: Curve({dt(2022, 1, 1): 1.0, dt(2023, 1, 1): 1.0}, id="eur1m"),
...: Curve({dt(2022, 1, 1): 1.0, dt(2023, 1, 1): 1.0}, id="eur3m"),
...: ],
...: instruments=[
...: IRS(dt(2022, 1, 1), "1Y", spec="eur_irs", curves="estr"),
...: IRS(dt(2022, 1, 1), "1Y", spec="eur_irs1", curves=["eur1m", "estr"]),
...: IRS(dt(2022, 1, 1), "1Y", spec="eur_irs3", curves=["eur3m", "estr"]),
...: ],
...: s=[1.5, 1.6, 1.7],
...: instrument_labels=["1Ye", "1Y1s", "1Y3s"],
...: id="eur rates"
...: )
...:
SUCCESS: `func_tol` reached after 3 iterations (levenberg_marquardt), `f_val`: 3.987399602230732e-14, `time`: 0.0120s
We now create an IRS
which has 2 month stub periods at the
front and back of the Instrument.
In [2]: irs = IRS(
...: effective=dt(2022, 1, 15),
...: termination=dt(2022, 11, 15),
...: front_stub=dt(2022, 3, 15),
...: back_stub=dt(2022, 9, 15),
...: frequency="Q",
...: fixed_rate=1.5,
...: currency="eur",
...: leg2_fixing_method="ibor"
...: )
...:
If we price and risk this swap naively using the 3M IBOR curve the stub periods will be calculated from that 3M curve directly, and there is no dependence at all to the 1M curve.
In [3]: irs.rate(curves=["eur3m", "estr"], solver=solver)
Out[3]: <Dual: 1.666874, (eur3m0, eur3m1, estr0, ...), [100.4, -102.0, -0.0, ...]>
In [4]: irs.delta(curves=["eur3m", "estr"], solver=solver)
Out[4]:
local_ccy eur
display_ccy eur
type solver label
instruments eur rates 1Ye -0.59
1Y1s -0.00
1Y3s 80.95
In [5]: irs.cashflows(curves=["eur3m", "estr"], solver=solver)
Out[5]:
Type Period Ccy Acc Start Acc End Payment Convention DCF Notional DF Collateral Rate Spread Cashflow NPV FX Rate NPV Ccy
leg1 0 FixedPeriod Stub EUR 2022-01-15 2022-03-15 2022-03-17 ACT365F 0.16 1000000.00 1.00 None 1.50 NaN -2424.66 -2417.25 1.00 -2417.25
1 FixedPeriod Regular EUR 2022-03-15 2022-06-15 2022-06-17 ACT365F 0.25 1000000.00 0.99 None 1.50 NaN -3780.82 -3755.15 1.00 -3755.15
2 FixedPeriod Regular EUR 2022-06-15 2022-09-15 2022-09-17 ACT365F 0.25 1000000.00 0.99 None 1.50 NaN -3780.82 -3741.09 1.00 -3741.09
3 FixedPeriod Stub EUR 2022-09-15 2022-11-15 2022-11-17 ACT365F 0.17 1000000.00 0.99 None 1.50 NaN -2506.85 -2474.34 1.00 -2474.34
leg2 0 FloatPeriod Stub EUR 2022-01-15 2022-03-15 2022-03-17 ACT365F 0.16 -1000000.00 1.00 None 1.67 0.00 2693.15 2684.92 1.00 2684.92
1 FloatPeriod Regular EUR 2022-03-15 2022-06-15 2022-06-17 ACT365F 0.25 -1000000.00 0.99 None 1.67 0.00 4202.64 4174.11 1.00 4174.11
2 FloatPeriod Regular EUR 2022-06-15 2022-09-15 2022-09-17 ACT365F 0.25 -1000000.00 0.99 None 1.67 0.00 4202.64 4158.48 1.00 4158.48
3 FloatPeriod Stub EUR 2022-09-15 2022-11-15 2022-11-17 ACT365F 0.17 -1000000.00 0.99 None 1.67 0.00 2784.57 2748.46 1.00 2748.46
However, it is also possible, only in the case of an “ibor” fixing_method
, to supply a dict
of forecasting curves, from which it will interpolate the fixing using the maturity date of the
tenor fixings and the end date of the period. In this format the keys of the dict are the
IBOR tenors available, e.g. “3m” and the values of the dict represent the Curve objects or
the Curve str ids from which will identify the Curves to be extracted from the Solver.
In [6]: irs.rate(curves=[{"3m": "eur3m", "1m": "eur1m"}, "estr"], solver=solver)
Out[6]: <Dual: 1.647258, (eur3m0, eur3m1, eur1m0, ...), [80.3, -81.6, 20.1, ...]>
In [7]: irs.delta(curves=[{"3m": "eur3m", "1m": "eur1m"}, "estr"], solver=solver)
Out[7]:
local_ccy eur
display_ccy eur
type solver label
instruments eur rates 1Ye -0.59
1Y1s 16.24
1Y3s 64.74
In [8]: irs.cashflows(curves=[{"3m": "eur3m", "1m": "eur1m"}, "estr"], solver=solver)
Out[8]:
Type Period Ccy Acc Start Acc End Payment Convention DCF Notional DF Collateral Rate Spread Cashflow NPV FX Rate NPV Ccy
leg1 0 FixedPeriod Stub EUR 2022-01-15 2022-03-15 2022-03-17 ACT365F 0.16 1000000.00 1.00 None 1.50 NaN -2424.66 -2417.25 1.00 -2417.25
1 FixedPeriod Regular EUR 2022-03-15 2022-06-15 2022-06-17 ACT365F 0.25 1000000.00 0.99 None 1.50 NaN -3780.82 -3755.15 1.00 -3755.15
2 FixedPeriod Regular EUR 2022-06-15 2022-09-15 2022-09-17 ACT365F 0.25 1000000.00 0.99 None 1.50 NaN -3780.82 -3741.09 1.00 -3741.09
3 FixedPeriod Stub EUR 2022-09-15 2022-11-15 2022-11-17 ACT365F 0.17 1000000.00 0.99 None 1.50 NaN -2506.85 -2474.34 1.00 -2474.34
leg2 0 FloatPeriod Stub EUR 2022-01-15 2022-03-15 2022-03-17 ACT365F 0.16 -1000000.00 1.00 None 1.61 0.00 2610.16 2602.19 1.00 2602.19
1 FloatPeriod Regular EUR 2022-03-15 2022-06-15 2022-06-17 ACT365F 0.25 -1000000.00 0.99 None 1.67 0.00 4202.64 4174.11 1.00 4174.11
2 FloatPeriod Regular EUR 2022-06-15 2022-09-15 2022-09-17 ACT365F 0.25 -1000000.00 0.99 None 1.67 0.00 4202.64 4158.48 1.00 4158.48
3 FloatPeriod Stub EUR 2022-09-15 2022-11-15 2022-11-17 ACT365F 0.17 -1000000.00 0.99 None 1.62 0.00 2704.26 2669.19 1.00 2669.19
Notice that in this case the relevant risk sensitivity exposure has been measured against the 1M curve to which the IRS has some direct dependence.