FloatPeriod#
- class rateslib.periods.FloatPeriod(*args, float_spread=NoInput.blank, fixings=NoInput.blank, fixing_method=NoInput.blank, method_param=NoInput.blank, spread_compound_method=NoInput.blank, **kwargs)#
Bases:
BasePeriod
Create a period defined with a floating rate index.
- Parameters:
args (dict) – Required positional args to
BasePeriod
.float_spread (float or None, optional) – The float spread applied to determine the cashflow. Can be set to None and set later, typically after a mid-market float spread for all periods has been calculated. Expressed in basis points (bps).
spread_compound_method (str, optional) – The method to use for adding a floating rate to compounded rates. Available options are {“none_simple”, “isda_compounding”, “isda_flat_compounding”}.
fixings (float, list, or Series, optional) – If a float scalar, will be applied as the determined fixing for the whole period. If a list of n fixings will be used as the first n RFR fixings in the period and the remaining fixings will be forecast from the curve and composed into the overall rate. If a datetime indexed
Series
will use the fixings that are available in that object, and derive the rest from thecurve
. Must be input excludingfloat_spread
.fixing_method (str, optional) – The method by which floating rates are determined, set by default. See notes.
method_param (int, optional) – A parameter that is used for the various
fixing_method
s. See notes.kwargs (dict) – Required keyword arguments to
BasePeriod
.
Notes
The
cashflow
is defined as follows;\[C = -Ndr(r_i, z)\]The
npv()
is defined as;\[P = Cv(m) = -Ndr(r_i, z)v(m)\]The
analytic_delta()
is defined as;\[A = - \frac{\partial P}{\partial z} = Ndv(m) \frac{\partial r}{\partial z}\]Fixing Methods
Floating period rates depend on different
fixing_method
to determine their rates. For further info seeISDA RFR Compounding Memo (2006)
. The available options provided here are:“rfr_payment_delay”: this is the standard convention adopted by interbank RFR derivative trades, such as SOFR, SONIA, and ESTR OIS etc.
method_param
is not used for this method and defaults to zero,payment_lag
serves as the appropriate parameter for this method.“rfr_observation_shift”: typical conventions of FRNs. The
method_param
is the integer number of business days by which both the observation rates and the DCFs are shifted.“rfr_lockout”: this is a convention typically used on floating rate notes (FRNs), the
method_param
as integer number of business days is the number of locked-out days. E.g. SOFR based FRNs generally have 4.“rfr_lookback”: this is also a convention typically used on FRNs. The
method_param
as integer number of business days defines the observation offset, the DCFs remain static, measured between the start and end dates.“rfr_payment_delay_avg”, “rfr_observation_shift_avg”, “rfr_lockout_avg”, “rfr_lookback_avg”: these are the same as the previous conventions except that the period rate is defined as the arithmetic average of the individual fixings, weighted by the relevant DCF depending upon the method.
“ibor”: this the convention for determining IBOR rates from a curve. The
method_param
is the number of fixing lag days before the accrual start when the fixing is published. For example, Euribor or Stibor have 2.
The first two are the only methods recommended by Alternative Reference Rates Comittee (AARC), although other methods have been implemented in financial instruments previously.
Spread Compounding Methods
The spread compounding methods operate as follows:
“none_simple”: the float spread added in a simple way to the determined compounded period rate.
“isda_compounding”: the float spread is added to each individual rate and then everything is compounded.
“isda_flat_compounding”: the spread is added to each rate but is not used when compounding each previously calculated component.
The first is the most efficient and most encountered. The second and third are rarely encountered in modern financial instruments. For further info see
ISDA Compounding Memo (2009)
.Fixings
Warning
Providing
fixings
as aSeries
is best practice.But, RFR and IBOR fixings provided as datetime indexed
Series
require different formats:IBOR fixings are indexed by publication date and fixing value.
RFR fixings are indexed by reference value date and fixing value.
If an “ibor”
fixing method
is given the series should index the published IBOR rates by publication date, which usually lags the reference value dates. For example, EURIBOR lags its value dates by two business days. 3M EURIBOR was published on Thu-2-Mar-2023 as 2.801%, which is applicable to the start date of Mon-6-Mar-2023 with value end date of Tue-6-Jun-2023.In [1]: ibor_curve = Curve( ...: nodes={dt(2023, 3, 6): 1.0, dt(2024, 3, 6): 0.96}, ...: calendar="bus" ...: ) ...: In [2]: fixings = Series( ...: [1.00, 2.801, 1.00, 1.00], ...: index=[dt(2023, 3, 1), dt(2023, 3, 2), dt(2023, 3, 3), dt(2023, 3, 6)] ...: ) ...: In [3]: float_period = FloatPeriod( ...: start=dt(2023, 3, 6), ...: end=dt(2023, 6, 6), ...: payment=dt(2023, 6, 6), ...: frequency="Q", ...: fixing_method="ibor", ...: method_param=2, ...: fixings=fixings ...: ) ...: In [4]: float_period.rate(ibor_curve) # this will return the fixing published 2-Mar-23 Out[4]: 2.801 In [5]: float_period.fixings_table(ibor_curve) Out[5]: notional dcf rates obs_dates 2023-03-02 -1000000.00 None 2.80
RFR rates tend to be maintained by central banks. The modern tendency seems to be to present historical RFR data indexed by reference value date and not publication date, which is usually 1 business day in arrears. If the
fixing_method
is “rfr” based then the given series should be indexed in a similar manner. ESTR was published as 2.399% on Fri-3-Mar-2023 for the reference value start date of Thu-2-Mar-2023 (and end date of Fri-3-Mar-2023).In [6]: rfr_curve = Curve( ...: nodes={dt(2023, 3, 3): 1.0, dt(2024, 3, 3): 0.96}, ...: calendar="bus" ...: ) ...: In [7]: fixings = Series( ...: [1.00, 1.00, 2.399], ...: index=[dt(2023, 2, 28), dt(2023, 3, 1), dt(2023, 3, 2)] ...: ) ...: In [8]: float_period = FloatPeriod( ...: start=dt(2023, 3, 2), ...: end=dt(2023, 3, 3), ...: payment=dt(2023, 3, 3), ...: frequency="A", ...: fixing_method="rfr_payment_delay", ...: fixings=fixings ...: ) ...: In [9]: float_period.rate(rfr_curve) # this will return the fixing for reference 2-Mar-23 Out[9]: 2.3989999999969314 In [10]: float_period.fixings_table(rfr_curve) Out[10]: notional dcf rates obs_dates 2023-03-02 0.00 0.00 2.40
Examples
Create a stepped (log-linear interpolated) curve with 2 relevant steps:
In [11]: curve = Curve(nodes={dt(2022, 1, 1): 1.0, dt(2022, 2, 1): 0.999, dt(2022, 3, 1): 0.997}) In [12]: curve.rate(dt(2022, 1, 1), "1D") Out[12]: 1.1618901045684638 In [13]: curve.rate(dt(2022, 2, 1), "1D") Out[13]: 2.5766748046498478
A standard “rfr_payment_delay” period, which is the default for this library.
In [14]: period = FloatPeriod( ....: start=dt(2022, 1, 1), ....: end=dt(2022, 3, 1), ....: payment=dt(2022, 3, 1), ....: frequency="M", ....: ) ....: In [15]: period.fixing_method Out[15]: 'rfr_payment_delay' In [16]: period.rate(curve) Out[16]: 1.8360165241487176
An “rfr_lockout” period, here with 28 business days lockout (under a curve with a no holidays) meaning the observation period ends on the 1st Feb 2022 and the 1D rate between 31st Jan 2022 and 1st Feb is used consistently as the fixing for the remaining fixing dates.
In [17]: period = FloatPeriod( ....: start=dt(2022, 1, 1), ....: end=dt(2022, 3, 1), ....: payment=dt(2022, 3, 1), ....: frequency="M", ....: fixing_method="rfr_lockout", ....: method_param=28, ....: ) ....: In [18]: period.rate(curve) Out[18]: 1.162978262569729
An “rfr_lookback” period, here with 5 days offset meaning the observation period starts on 27th Jan and ends on 6th Feb.
In [19]: period = FloatPeriod( ....: start=dt(2022, 2, 1), ....: end=dt(2022, 2, 11), ....: payment=dt(2022, 2, 11), ....: frequency="M", ....: fixing_method="rfr_lookback", ....: method_param=5, ....: ) ....: In [20]: period.rate(curve) Out[20]: 1.8697123392650281
An “ibor” period, with a 2 day fixing lag
In [21]: period = FloatPeriod( ....: start=dt(2022, 1, 3), ....: end=dt(2022, 3, 3), ....: payment=dt(2022, 3, 3), ....: frequency="B", ....: fixing_method="ibor", ....: method_param=2, ....: ) ....: In [22]: period.rate(curve) Out[22]: 1.8841190295698882 In [23]: curve.rate(dt(2022, 1, 3), "2M") Out[23]: 1.8841190295698882
Methods Summary
analytic_delta
([curve, disc_curve, fx, base])Return the analytic delta of the FloatPeriod.
cashflow
(curve)cashflows
([curve, disc_curve, fx, base])Return the cashflows of the FloatPeriod.
fixings_table
(curve[, approximate, disc_curve])Return a DataFrame of fixing exposures.
npv
([curve, disc_curve, fx, base, local])Return the NPV of the FloatPeriod.
rate
(curve)Calculating the floating rate for the period.
Methods Documentation
- analytic_delta(curve=NoInput.blank, disc_curve=NoInput.blank, fx=NoInput.blank, base=NoInput.blank)#
Return the analytic delta of the FloatPeriod. See
BasePeriod.analytic_delta()
- cashflow(curve)#
- cashflows(curve=NoInput.blank, disc_curve=NoInput.blank, fx=NoInput.blank, base=NoInput.blank)#
Return the cashflows of the FloatPeriod. See
BasePeriod.cashflows()
- fixings_table(curve, approximate=False, disc_curve=NoInput.blank)#
Return a DataFrame of fixing exposures.
- Parameters:
curve (Curve, LineCurve, IndexCurve dict of such) – The forecast needed to calculate rates which affect compounding and dependent notional exposure.
approximate (bool, optional) – Perform a calculation that is broadly 10x faster but potentially loses precision upto 0.1%.
disc_curve (Curve) – A curve to make appropriate DF scalings. If None and
curve
contains DFs that will be used instead, otherwise errors are raised.
- Return type:
DataFrame
Notes
IBOR and RFR
fixing_method
have different representations under this method.For “ibor” based floating rates the fixing exposures are indexed by publication date and not by reference value date. IBOR fixings tend to occur either in advance, or the same day.
For “rfr” based floating rates the fixing exposures are indexed by the reference value date and not by publication date. RFR fixings tend to publish in arrears, usually at 9am the following business day. Central banks tend to publish data aligning the fixing rate with the reference value date and not by the publication date which is why this format is chosen. It also has practical application when constructing curves.
Examples
In [1]: rfr_curve = Curve( ...: nodes={dt(2022, 1, 1): 1.00, dt(2022, 1, 13): 0.9995}, ...: calendar="bus" ...: ) ...:
A regular rfr_payment_delay period.
In [2]: constants = { ...: "start": dt(2022, 1, 5), ...: "end": dt(2022, 1, 11), ...: "payment": dt(2022, 1, 11), ...: "frequency": "Q", ...: "notional": -1000000, ...: "currency": "gbp", ...: } ...: In [3]: period = FloatPeriod(**{ ...: **constants, ...: "fixing_method": "rfr_payment_delay" ...: }) ...: In [4]: period.fixings_table(rfr_curve) Out[4]: notional dcf rates obs_dates 2022-01-05 1000000.00 0.00 1.50 2022-01-06 1000041.68 0.00 1.50 2022-01-07 1000083.36 0.01 1.50 2022-01-10 1000208.41 0.00 1.50
A 2 business day rfr_observation_shift period. Notice how the above had 4 fixings spanning 6 calendar days, but the observation shift here attributes 4 fixings spanning 4 calendar days so the notional exposure to those dates is increased by effectively 6/4.
In [5]: period = FloatPeriod(**{ ...: **constants, ...: "fixing_method": "rfr_observation_shift", ...: "method_param": 2, ...: }) ...: In [6]: period.fixings_table(rfr_curve) Out[6]: notional dcf rates obs_dates 2022-01-03 1499749.96 0.00 1.50 2022-01-04 1499812.46 0.00 1.50 2022-01-05 1499874.97 0.00 1.50 2022-01-06 1499937.49 0.00 1.50
A 2 business day rfr_lookback period. Notice how the lookback period adjusts the weightings on the 6th January fixing by 3, and thus increases the notional exposure.
In [7]: period = FloatPeriod(**{ ...: **constants, ...: "fixing_method": "rfr_lookback", ...: "method_param": 2, ...: }) ...: In [8]: period.fixings_table(rfr_curve) Out[8]: notional dcf rates obs_dates 2022-01-03 999916.64 0.00 1.50 2022-01-04 999958.32 0.00 1.50 2022-01-05 2999749.95 0.01 1.50 2022-01-06 1000041.67 0.00 1.50
A 2 business day rfr_lockout period. Notice how the exposure to the final fixing which then spans multiple days is increased.
In [9]: period = FloatPeriod(**{ ...: **constants, ...: "fixing_method": "rfr_lockout", ...: "method_param": 2, ...: }) ...: In [10]: period.fixings_table(rfr_curve) Out[10]: notional dcf rates obs_dates 2022-01-05 999999.99 0.00 1.50 2022-01-06 4999958.32 0.00 1.50 2022-01-07 0.00 0.01 1.50 2022-01-10 0.00 0.00 1.50
An IBOR fixing table
In [11]: ibor_curve = Curve( ....: nodes={dt(2022, 1, 1): 1.00, dt(2023, 1, 1): 0.99}, ....: calendar="bus", ....: ) ....: period = FloatPeriod(**{ ....: **constants, ....: "fixing_method": "ibor", ....: "method_param": 2, ....: }) ....: period.fixings_table(ibor_curve) ....: Out[11]: notional dcf rates obs_dates 2022-01-03 1000000 None 0.99
- npv(curve=NoInput.blank, disc_curve=NoInput.blank, fx=NoInput.blank, base=NoInput.blank, local=False)#
Return the NPV of the FloatPeriod. See
BasePeriod.npv()
- rate(curve)#
Calculating the floating rate for the period.
- Parameters:
curve (Curve, LineCurve, IndexCurve, dict of curves) – The forecasting curve object.
- Return type:
Examples
Using a single forecast Curve.
In [1]: period.rate(curve) Out[1]: 2.157969412869587
Using a dict of Curves for stub periods calculable under an “ibor”
fixing_method
.In [2]: period.rate({"1m": curve, "3m": curve, "6m": curve, "12m": curve}) Out[2]: 2.157969412869587