Calculation Framework

This chapter describes the mechanisms common to all products: the calculation chain, the construction of scenarios, reading rates from curves, the calculation of a coupon and its components, and the rules that classify each cash flow (certainty, repricing date, sign, time bucket). The product sheets in chapter 2 build on these components without repeating them.

1.1 The calculation chain

Imported situation ──► Constant balance ──► 8 contexts ──► Cash flow generation ──► Post-processing ──► Metrics ──► EBA templates
 (transactions,          sheet (roll-over     (BASELINE +     (one generator per        (repricing date,     (MV, EVE,   (J 01.00 …
  curves, balances,      of maturing          7 shocks)       product family)           discounting,         NII, gaps)  J 11.02)
  run-off laws)          positions)                                                     currency conversion)

A cash flow generation runs through the following steps:

  1. Scenarios — the engine builds 8 valuation contexts: the original context and one context per shock (§1.2).
  2. Constant balance sheet — the engine adds to the portfolio the roll-over transactions replacing maturities falling within the horizon (rollHorizon); these transactions carry original = false (chapter 4).
  3. Generation — for each context and each transaction, the generator of the product family produces the list of cash flows (principal and interest, contractual view and behavioural view).
  4. Post-processing — for each transaction: calculation of the repricing date of floating-rate principal (§1.9), of the discount factor of each cash flow (§1.5), then of the equivalent amount in the pivot currency (§1.11).
  5. Metrics — the engine aggregates cash flows by currency, scenario, FINREP classification and origin (chapter 3).

A transaction in error (missing curve, inconsistent data) is isolated: it is removed from the calculation, the error is logged with its reference, and the rest of the portfolio is calculated.

1.2 Interest rate shock scenarios

The shocks are those of the EBA / Basel Committee standardised framework (BCBS SRP31.90). They are applied by IrrbbScenario.applyScenario to every point of every yield curve; the FX curves and liquidity spread curves (LIQUIDITY_SPREAD) are not shocked .

Shock sizes by currency

Currency Parallel \(\bar R_{par}\) Short \(\bar R_{short}\) Long \(\bar R_{long}\)
EUR 200 bp 250 bp 100 bp
USD 200 bp 300 bp 150 bp
GBP 250 bp 300 bp 150 bp
JPY 100 bp 100 bp 100 bp
CHF 100 bp 150 bp 100 bp
CAD 200 bp 300 bp 150 bp
AUD 300 bp 450 bp 200 bp
SEK 200 bp 300 bp 150 bp
SGD 150 bp 200 bp 100 bp
HKD 200 bp 250 bp 100 bp
CNY 250 bp 300 bp 150 bp
other EUR shock sizes

Formulas

For a curve point of maturity \(t\) (in years: months / 12, weeks / 52, days / 365), the decay factor of the short shock is denoted:

\[S(t) = e^{-t/4}\]

The shocked rate is \(r_\sigma(t) = r(t) + \Delta_\sigma(t)\) with:

Scenario \(\Delta_\sigma(t)\)
PARALLEL_UP \(+\bar R_{par}\)
PARALLEL_DOWN \(-\bar R_{par}\)
SHORT_UP \(+\bar R_{short} \cdot S(t)\)
SHORT_DOWN \(-\bar R_{short} \cdot S(t)\)
STEEPENER \(-0.65 \cdot \bar R_{short} \cdot S(t) + 0.9 \cdot \bar R_{long} \cdot (1 - S(t))\)
FLATTENER \(+0.8 \cdot \bar R_{short} \cdot S(t) - 0.6 \cdot \bar R_{long} \cdot (1 - S(t))\)
UP_1_BP \(+0.0001\) (sensitivity probe, DV01-type)

Post-shock floor

Each shocked rate is then bounded by the EBA floor, which starts at −150 bp for immediate maturities and rises by 3 bp per year of maturity up to 0% (reached at 50 years):

\[\text{floor}(t) = \min\big(0,\; -1.50\% + 0.03\% \cdot t\big) \qquad r_\sigma(t) \leftarrow \max\Big(\min\big(\text{floor}(t),\ r(t)\big),\; r_\sigma(t)\Big)\]

When the observed rate \(r(t)\) is already below the floor, the observed rate itself serves as the floor: a downward shock can never push a rate above its market level. The floor is applied only to shocked curves; the BASELINE curve is used as imported.

Scenarios of a custom simulation

The official run (IRRBB) always produces BASELINE and the seven scenarios above, floor applied. A custom simulation (Custom scenario):

  • always produces BASELINE, then only the chosen regulatory shocks;
  • adds, when an own shock is defined, the CUSTOM scenario, of signed size \(b\) in basis points (\(+100\) bp = \(+1\,\%\)):
Shape \(\Delta_{CUSTOM}(t)\)
Parallel \(b\)
Short \(b \cdot S(t)\)
Long \(b \cdot (1 - S(t))\)
  • applies the post-shock floor to no shocked scenario when it is turned off; turned on, it bounds every shocked scenario, CUSTOM included.

The metrics (EVE, NII, MV) are calculated for every scenario produced, CUSTOM included. The IRRBB templates read only BASELINE and the six supervisory shocks: only the official run produces them.

1.3 Reading a rate from a curve

A curve is a set of maturity → rate points at a reference date \(t_{ref}\), identified by its index (EURIBOR 3M, ESTR, …) and its currency. A curve quoted in swap rates (PAR) is first converted into a zero-coupon curve (see below); the rules below apply to that zero-coupon curve.

Curve selection: among the curves carrying the requested index (case-insensitive), the one whose reference date is closest to the reporting date. A gap of more than 10 days raises a warning, of more than 20 days an error (logged; the calculation continues).

Interpolation: the rate at date \(t\) is read for a maturity of \(d = t - t_{ref}\) days, by linear interpolation on the number of days between the two surrounding points:

\[r(d) = r_1 + \frac{r_2 - r_1}{d_2 - d_1}\,(d - d_1)\]

where point maturities are converted into days with 1 week = 7 d, 1 month = 30 d, 1 year = 365 d. Beyond the last point or before the first, the rate is flat-extrapolated (value of the nearest point). A negative maturity returns 0.

Curves quoted in swap rates (PAR)

A curve can be quoted in zero-coupon rates (ZERO, the default) or in market rates (PAR: money market rates at the short end, OIS swap rates beyond). The quotation is declared per curve in the client configuration (dataMapping.curveQuotations). A PAR curve is never read as is: it is first transformed (“bootstrapped”) into a zero-coupon curve, once per curve, and it is this zero-coupon curve that is used for rate readings, forwards and discounting.

Each maturity point of \(d\) days (1 month = 30 d, 1 year = 365 d) yields a discount factor:

  • money market point (up to one year by default), simple interest Actual/360: \[DF(d) = \frac{1}{1 + r \cdot d / 360}\]
  • swap point (beyond one year, whole number of years \(n\)), OIS swap at par with an annual fixed leg, accrual fraction \(\alpha = 365/360\): \[DF_n = \frac{1 - S_n \sum_{i<n} \alpha\, DF_i}{1 + S_n\, \alpha}\]

The intermediate years required for the annuity are filled in by linear interpolation of money market rates (before the first swap) or of swap rates (between two swaps), and added to the curve. Each factor is finally converted into an annual zero-coupon rate Actual/365:

\[z(d) = DF(d)^{-365/d} - 1\]

In a shocked scenario, the shock and the EBA floor are applied to the zero-coupon rates of the bootstrapped curve, never to the quoted swap rates. A money-market coupon read on this curve returns its quote (§1.4).

1.4 Forward rates

The forward rate between \(t_a\) and \(t_b\) is the simple rate of the period, in the day-count basis \(B\) of the coupon that carries it (the basis of the transaction’s or leg’s rate, the one already used to compute the interest), derived from the curve’s discount factors:

\[F_B(t_a, t_b) = \frac{1}{\tau_B(t_a, t_b)} \left( \frac{DF(t_a)}{DF(t_b)} - 1 \right)\]

where \(DF\) is the discount factor of §1.5, read on the same curve (its zero-coupon curve if it is quoted PAR), with the same interpolation, and \(\tau_B\) the year fraction of the period in basis \(B\). Accrued in that same basis, the coupon earns exactly what the discount factors imply over the period: a money-market coupon starting at the reporting date returns its quote. On EURIBOR 6M SWAP at 30/06/2025, the one-year Exact/360 coupon starting at the reporting date reads 2.02%, the quoted rate of the 1-year point. The curve points are still read as annual Exact/365 zero-coupon rates; only the coupon rate is expressed in its basis. A period of zero length in basis \(B\) accrues no interest; it reads the zero-coupon rate at its end.

The same formula is used for every rate read on a curve: the forward of a floating coupon in every product family, securities included (product sheets), the current index rate in LAST_FIXING mode and its shock term (§4.4). The roll-over of a fixed-rate transaction reads a variant of it (§4.1). Observed rates (known fixing, pre-fixing) are not read on a curve and do not change.

The engine then adds the requested spread, where applicable, and applies pre-fixing (a rate entered manually for a transaction, which takes precedence over the curve rate for a certain cash flow — see §4.5).

For a coupon that has already started at the reporting date (\(t_{k-1} < t_0\)), there is no fixing history: the start of the forward is moved to \(t_0\), which amounts to reading the simple rate from \(t_0\) to the end of the period, \(F_B(t_0, t_k)\), with \(DF(t_0) = 1\).

1.5 Discount factor

In each scenario, each cash flow receives a discount factor read from the curve of the context’s discounting index (discountFactorIndex) — its zero-coupon curve if it is quoted PAR —, with annual compounding and Actual/365 basis:

\[DF_\sigma(t) = \big(1 + r_\sigma(t)\big)^{-\tau(t_0, t)} \qquad \tau(t_0, t) = \frac{t - t_0}{365}\]

  • A cash flow at a date \(t \le t_0\) is not discounted: \(DF = 1\), without reading the curve (a date before the curve’s reference date has no rate on it).
  • A rate such that \(1 + r \le 0\) is rejected (explicit error rather than an infinite factor).
  • Two factors are calculated: \(DF\) at the payment date (discountFactor) and \(DF\) at the repricing date (adjustedDiscountFactor, §1.9). For a cash flow that is not moved, the two are equal.

The discount curve is that of the scenario: in PARALLEL_UP, all cash flows are discounted on the shocked curve, whether certain or not.

1.6 Day-count conventions (year fraction)

The year fraction \(\tau_B(t_a, t_b)\) is calculated according to the basis carried by the transaction’s rate:

Basis Formula
EXACT_EXACT \(\dfrac{\text{days}(t_a, t_b)}{\text{days}(t_b - 1\text{ year},\ t_b)}\) (365 or 366 depending on the year preceding \(t_b\))
EXACT_365 \(\dfrac{\text{days}(t_a, t_b)}{365}\)
EXACT_360 \(\dfrac{\text{days}(t_a, t_b)}{360}\)
ABB_30_360 \(\dfrac{360\,(y_b - y_a) + 30\,(m_b - m_a) + (j_b - j_a)}{360}\), where \(y\), \(m\), \(j\) are the year, the month and the day of the month, after adjustment: \(j_a = 31 \Rightarrow j_a = 30\), then \(j_b = 31\) and \(j_a = 30 \Rightarrow j_b = 30\)

ABB_30_360 is the 30/360 bond basis (ISDA 2006 §4.16(f), “30/360”, “360/360”, “Bond Basis”); the end of February is not adjusted. Examples: 15/01 → 14/02 = 29/360, 31/01 → 28/02 = 28/360, 31/03 → 30/06 = 90/360. It is the engine’s only 30/360 basis: an imported 30E/360 label is mapped to it, and differs from it only for a period ending on the 31st that starts before the 30th.

1.7 The coupon and its analytical breakdown

Interest amount

The all-in client rate of a coupon is the “rate” component (contractual fixed rate, or index rate for a floating-rate transaction) plus the contractual spread:

\[R = r_{taux} + m\]

The interest amount depends on the interest mode of the rate (interestMode):

Mode Interest over the period \([t_{k-1}, t_k]\)
SIMPLE \(I = N \cdot R \cdot \tau_B(t_{k-1}, t_k)\)
COMPOUND \(I = N \cdot \big[(1 + R)^{\tau_B(t_{k-1}, t_k)} - 1\big]\)

Some families calculate the coupon differently (fraction \(1/p\) of the frequency, amortisation schedule): each product sheet specifies the formula it uses.

Rate / liquidity / margin breakdown

Each coupon is broken down into three components, whose sum gives back the coupon:

\[R = \underbrace{r_{taux}}_{\text{RATE}} + \underbrace{\ell}_{\text{LIQUIDITY}} + \underbrace{(R - r_{taux} - \ell)}_{\text{MARGIN}}\]

The liquidity spread \(\ell\) is the spread carried by the product (liquiditySpread) if it has one, otherwise the rate read at the period end date on the LIQUIDITY_SPREAD curve of the currency (0 if the reading fails; a missing curve is an error). The amount is allocated pro rata to the rates:

\[I_{RATE} = I \cdot \frac{r_{taux}}{R} \qquad I_{LIQUIDITY} = I \cdot \frac{\ell}{R} \qquad I_{MARGIN} = I \cdot \frac{R - r_{taux} - \ell}{R}\]

(all zero if \(R = 0\)). The cash flow also carries the GLOBAL component = \(I\).

Accrued coupon

If the reporting date falls within the coupon period, the cash flow is flagged broken and carries the portion accrued at \(t_0\):

\[I_{couru} = I \cdot \frac{\tau_B(t_{k-1}, t_0)}{\tau_B(t_{k-1}, t_k)}\]

Choice of rate context

A certain coupon is always valued on the BASELINE context; an uncertain coupon on the context of the current scenario. This is the core IRRBB rule: a rate that is already fixed does not move with the shock, so that for a certain cash flow \(I_{BASELINE} = I_{PARALLEL\_UP} = I_{PARALLEL\_DOWN}\).

Rate of a floating-rate coupon

For a FLOATING rate, the \(r_{taux}\) component is determined in the following order:

  1. No index (empty or unrecognised index): the rate currently carried by the transaction (rate.value). The transaction remains floating but becomes insensitive to shocks; a warning flags this.
  2. Current coupon with known fixing: if the coupon has already started (\(t_{k-1} \le t_0\)) and the transaction carries its fixing (non-zero rate.value), that fixing is the coupon rate (pre-fixing still takes precedence). A rate.value of 0 is treated as “fixing not provided” and the curve is read.
  3. LAST_FIXING mode and uncertain coupon: the last known rate, shocked (§4.4).
  4. General case: the forward of the index over the coupon period, \(F_B(\max(t_{k-1}, t_0),\ t_k)\) in the rate’s basis, read from the curve of the selected context.

Swaps apply rules 1, 3 and 4 (not rule 2: the rate value of a floating leg is for information only). The product sheets specify this.

1.8 Certainty of a cash flow

Certainty indicates whether the rate that determines the cash flow is already known. It is calculated by the engine, in this order:

Condition Certainty
New production transaction (original = false) UNCERTAIN
Principal cash flow CERTAIN
No rate, or rate without type CERTAIN
FIXED rate CERTAIN
FLOATING rate without index CERTAIN
FLOATING rate, coupon whose period is known CERTAIN if \(t_{k-1} \le t_0\) (current coupon, fixed at the start of the period), UNCERTAIN otherwise
FLOATING rate, cash flow without period (run-off tranches) CERTAIN if the cash flow date \(\le\) next fixing date, UNCERTAIN otherwise

Next fixing date (calendar rule): the tenor is read from the index name (ESTR, EONIA, O/N → 1 day; 12M / 1Y → 12 months; 6M; 3M; 1M), failing that from the refixing period of the curve, failing that 3 months (with a warning). The date is then: the day after the reporting date (1 day), end of month, end of quarter, end of half-year or end of calendar year (strictly after \(t_0\)).

The first rule overrides all others for every cash flow, including the principal exchanges that the generators create directly as CERTAIN: it is applied when each cash flow is built, so that no cash flow of a new production transaction is certain.

1.9 Repricing date of floating-rate principal

For EVE and the interest rate gap, the principal of a floating-rate transaction is not positioned at its contractual maturity but at its next repricing date: after refixing, the leg resets to par and no longer has any interest rate sensitivity (doc/IRRBB.md).

The engine applies, for each transaction, product, currency and origin:

  1. Among the floating-rate coupons, take the end of the last certain coupon (the current coupon).
  2. If there is none (forward start, new production), the end of the first upcoming coupon.
  3. Every floating-rate principal cash flow paid after this date receives this date as its adjusted date (adjustedEndDate) and the corresponding time bucket (adjustedTimeBucket); earlier cash flows (an initial exchange on a forward-starting transaction) keep their date.

Example (payer swap, fixed against Euribor 3M, notional EUR 100m, last reset today, 5-year maturity): on the floating leg, the coupon already fixed is paid in 3 months and the EUR 100m principal is positioned in 3 months. The EVE sensitivity of the leg is that of a 3-month asset, not a 5-year one.

Fixed-rate principal is never moved: its adjusted date is its contractual date.

1.10 Sign conventions

Cash flows are signed from the bank’s point of view. The sign of a transaction combines its direction (\(s = +1\) for BUY, \(-1\) for SELL) and the direction of the cash flow (OutflowSign: \(+1\) inflow, \(-1\) outflow):

Product Initial exchange Coupons Redemption
Bullet loan, amortising loan (BUY = loan granted) \(-s \cdot N\) \(+s\) \(+s\)
Term deposit (BUY = deposit received) \(+s \cdot N\) \(-s\) \(-s\)
Security (BUY = security purchased) \(-s \cdot (N - \text{premium})\) \(+s\) \(+s \cdot N\)
Repo (BUY = reverse repo, cash lending) \(-s \cdot N\) \(+s\) \(+s \cdot N\)
Swap, receive leg \(-s \cdot N\) \(+s\) \(+s \cdot N\)
Swap, pay leg \(+s \cdot N\) \(-s\) \(-s \cdot N\)
Sight deposit, accounting balance (deposit: \(+N\)) sign of the imported amount sign of the imported amount

A liability therefore has negative cash flows, EVE, NII and MV in the engine. The EBA ITS convention (“liabilities reported as positive”) is applied only for presentation, in the J templates (chapter 5).

1.11 Currencies

Each cash flow is calculated in its own currency, then converted into the context’s pivot currency:

\[CF^{pivot} = CF \cdot x(t_k)\]

where \(x(t_k)\) is the number of pivot currency units per unit of the cash flow currency, read at the cash flow date from an FX curve: the PIVOT/DEV pair is looked up first (taking \(1/\text{fixing}\)), then DEV/PIVOT (taking the fixing). A cash flow already in the pivot currency has \(x = 1\). Without an FX curve, the transaction is in error.

Regulatory metrics (EVE, NII, MV, notionals) are calculated by currency, in the currency of the cash flow; the pivot equivalent is used for management views (gaps, reports).

1.12 Time buckets

Each cash flow is assigned to one of the 19 EBA repricing buckets, based on its payment date (timeBucket) and its repricing date (adjustedTimeBucket):

Bucket Interval Bucket Interval
1 \(]t_0,\ t_0 + 1d]\) (overnight) 11 \(]4y, 5y]\)
2 \(]1d, 1m]\) 12 \(]5y, 6y]\)
3 \(]1m, 3m]\) 13 \(]6y, 7y]\)
4 \(]3m, 6m]\) 14 \(]7y, 8y]\)
5 \(]6m, 9m]\) 15 \(]8y, 9y]\)
6 \(]9m, 12m]\) 16 \(]9y, 10y]\)
7 \(]12m, 18m]\) 17 \(]10y, 15y]\)
8 \(]18m, 2y]\) 18 \(]15y, 20y]\)
9 \(]2y, 3y]\) 19 \(> 20y\)
10 \(]3y, 4y]\)

Bucket boundaries are calendar-based (reporting date + n months / n years): a cash flow paid exactly one year after the reporting date belongs to bucket 6 (\(]9m, 12m]\)), not to bucket 7.