Skip to content

IRB Approach

The Internal Ratings-Based (IRB) approach allows banks with regulatory approval to use their own risk estimates for calculating RWA. This provides greater risk sensitivity than the Standardised Approach.

Overview

Two IRB variants are available:

Approach PD LGD EAD CCF
Foundation IRB (F-IRB) Bank Supervisory Supervisory Supervisory
Advanced IRB (A-IRB) Bank Bank Bank Bank

Basel 3.1 Restrictions

Under Basel 3.1, A-IRB is no longer permitted for: - Large corporates (revenue > £440m) - Banks/Financial Institutions - Equity exposures

IRB Formula

The core IRB formula calculates the capital requirement (K). See irb/formulas.py for the pure Polars implementation.

K = [LGD × N((1-R)^(-0.5) × G(PD) + (R/(1-R))^0.5 × G(0.999)) - LGD × PD] × MA

Where: - N() = Standard normal cumulative distribution function - G() = Inverse standard normal distribution function - R = Asset correlation - PD = Probability of Default - LGD = Loss Given Default - MA = Maturity Adjustment (for non-retail)

Then:

RWA = K × 12.5 × EAD × Scaling Factor

Stats Backend

The IRB formulas require statistical functions (normal CDF and inverse CDF). The calculator uses polars-normal-stats for native Polars CDF/PPF expressions.

Installation

uv add polars-normal-stats
Capital K Formula Implementation (formulas.py)

See the _polars_capital_k_expr() function in src/rwa_calc/engine/irb/formulas.py for the full Polars expression implementation.

Risk Parameters

Probability of Default (PD)

PD is the likelihood of default within one year. CRR uses a uniform 0.03% floor; Basel 3.1 introduces differentiated floors (0.03%–0.10%) by exposure class.

PD_effective = max(PD_estimated, PD_floor)

Loss Given Default (LGD)

LGD is the percentage of exposure lost after recoveries. F-IRB uses supervisory values (0%–75% depending on collateral); A-IRB uses bank estimates subject to floors under Basel 3.1.

Under Basel 3.1, A-IRB firms have two methods for incorporating collateral into LGD (Art. 191A):

  • LGD Modelling Collateral Method (Art. 169A/169B) — the firm's own LGD model captures collateral-specific recovery characteristics, subject to PRA approval and annual revaluation. Must produce estimates at least as conservative as the Foundation Collateral Method.
  • Foundation Collateral Method (FCM, Art. 230) — uses supervisory LGDS values by collateral type, blended with LGDU for partially secured exposures.

For unfunded credit protection (guarantees), A-IRB firms may use the LGD Adjustment Method (LGD-AM, Art. 183) or the Parameter Substitution Method (PSM, Art. 236). LGD-AM is available only for exposure classes where the firm holds A-IRB own-LGD permission under Art. 143(2A)(c); F-IRB classes and Art. 147A SA-only classes (sovereigns, institutions, large corporates, FSEs) must use PSM instead. See Credit Risk Mitigation, the B31 CRM § LGD-AM Availability Gate, and the CRM Specification for details.

Exposure at Default (EAD)

F-IRB uses regulatory CCFs based on risk_type (CRR Art. 166(8)–(9)):

Risk Type F-IRB CCF Rule
FR / FRC 100% Full risk / certain drawdown
MR 75% Medium risk (Art. 166(8))
MLR 75% Medium-low risk (Art. 166(8))
LR 0% Unconditionally cancellable

Art. 166(9) Exception: Short-term letters of credit arising from the movement of goods retain 20% CCF under F-IRB. Flag these exposures with is_short_term_trade_lc = True.

Basel 3.1 Art. 166C fully aligns F-IRB CCFs to SA Table A1. The separate CRR Art. 166(8) supervisory schedule (75% for MR/MLR) is eliminated — F-IRB exposures use the same CCFs as SA exposures:

Risk Type CRR F-IRB B31 F-IRB Change
FR / FRC 100% 100% Unchanged
MR 75% 50% Down from 75%
MLR 75% 20% Down from 75%
OC 75%* 40% New Table A1 Row 5 category
LR (UCC) 0% 10% Up from 0%

* Under CRR, OC had no separate category. These commitments were classified by maturity into MR/MLR, both 75% under F-IRB Art. 166(8).

Art. 166(9) Trade LC Exception Removed

CRR Art. 166(9) is blanked by PRA PS1/26. The 20% short-term trade LC carve-out no longer applies under Basel 3.1 — these exposures receive the standard SA CCF for their risk type. The is_short_term_trade_lc flag has no effect under Basel 3.1.

A-IRB uses bank-estimated CCFs via the ccf_modelled column, subject to Basel 3.1 restrictions: own-estimate CCFs are limited to revolving facilities only (Art. 166D(1)(a)), with a floor of 50% of the SA CCF (CRE32.27). Non-revolving A-IRB exposures use SA CCFs. See A-IRB CCF Restrictions for full details.

Details: See Key Differences for the complete CCF comparison table across CRR and Basel 3.1, and CCF Specification for the full regulatory treatment.

Maturity (M)

Effective maturity affects capital through the maturity adjustment (Art. 162).

  • Range: 1 year (floor) to 5 years (cap)
  • Retail exemption: No maturity adjustment for retail (MA = 1.0)
# A-IRB: effective maturity from cash flows
M = max(1, min(5, weighted_average_life))

CRR F-IRB fixed maturities (Art. 162(1)):

Exposure Type M
Repo-style transactions (repos, securities/commodities lending) 0.5 years
All other exposures 2.5 years

Basel 3.1 deletes Art. 162(1) — all IRB firms must calculate M from cash flows or contractual terms. Revolving exposures must use the maximum contractual termination date (Art. 162(2A)(k)), not the current drawing repayment date.

Exceptions to the 1-Year Floor

Art. 162(3) allows a one-day maturity floor for daily-margined repos, derivatives, and margin lending, and for qualifying short-term exposures (FX settlement, trade finance ≤ 1yr, securities settlement). The has_one_day_maturity_floor input flag controls this. See the F-IRB specification for full details.

Details: See Technical Reference for the full CRR vs Basel 3.1 comparison table.

Asset Correlation

Asset correlation (R) determines how sensitive the exposure is to systematic risk. See _polars_correlation_expr() for the pure Polars implementation.

Corporate/Bank Correlation

R = 0.12 × (1 - exp(-50 × PD)) / (1 - exp(-50)) +
    0.24 × [1 - (1 - exp(-50 × PD)) / (1 - exp(-50))]

This produces: - R = 24% for very low PD - R = 12% for high PD

SME Size Adjustment

For SME corporates (turnover €5m-€50m). The turnover_m column is provided in GBP millions; the calculator converts to EUR using config.eur_gbp_rate:

# Convert GBP turnover to EUR
S_eur = turnover_gbp / eur_gbp_rate  # e.g. £25m / 0.8732 = €28.6m

# Clamp to range and apply adjustment
S = min(50, max(5, S_eur))
R_adjusted = R - 0.04 × (1 - (S - 5) / 45)

This reduces correlation by up to 4 percentage points for smaller firms.

Retail Correlations

Retail Type Correlation
Residential Mortgage 15%
QRRE 4%
Other Retail 3-16% (PD-dependent)

Other Retail:

R = 0.03 × (1 - exp(-35 × PD)) / (1 - exp(-35)) +
    0.16 × [1 - (1 - exp(-35 × PD)) / (1 - exp(-35))]

Actual Correlation Implementation (formulas.py)

See _polars_correlation_expr() in src/rwa_calc/engine/irb/formulas.py for the full Polars expression implementation including SME adjustment.

Financial Sector Entity Correlation Multiplier

Large financial sector entities (LFSEs) and unregulated financial sector entities receive a 1.25x multiplier on their asset correlation R (Art. 153(2) / CRE31.5). This increases capital requirements for exposures to financial institutions. The multiplier mechanism is unchanged between CRR and Basel 3.1; only the LFSE total-assets threshold differs:

  • CRR: total assets ≥ EUR 70 billion (CRR Art. 142(1)(4)).
  • Basel 3.1: total assets ≥ GBP 79 billion (PS1/26 Glossary p. 78, with Note "corresponds to Article 142(1)(4) of CRR").

Set apply_fi_scalar = True on the counterparty record for entities meeting these criteria. The calculator derives requires_fi_scalar directly from this flag.

Not the same as the Art. 147A large corporate threshold

The 1.25x correlation multiplier applies to financial sector entities based on the LFSE total-assets threshold (EUR 70bn CRR / GBP 79bn B31). The Art. 147A(1)(e) large corporate threshold (GBP 440m revenue) is an approach restriction (F-IRB only under Basel 3.1) — it does not trigger the correlation uplift. See the Key Differences for comparison.

Maturity Adjustment

For non-retail exposures. See _polars_maturity_adjustment_expr() for the pure Polars implementation.

# Maturity factor b
b = (0.11852 - 0.05478 × ln(PD))^2

# Maturity adjustment
MA = (1 + (M - 2.5) × b) / (1 - 1.5 × b)

Where M is effective maturity in years.

Actual Maturity Adjustment (formulas.py)

See _polars_maturity_adjustment_expr() in src/rwa_calc/engine/irb/formulas.py for the full Polars expression implementation.

Example values:

PD M=1yr M=2.5yr M=5yr
0.03% 0.853 1.000 1.221
0.10% 0.880 1.000 1.177
1.00% 0.934 1.000 1.099
5.00% 0.966 1.000 1.050

Scaling Factor

Framework Scaling Factor
CRR 1.06
Basel 3.1 1.00 (none)
# CRR
RWA = K × 12.5 × EAD × MA × 1.06

# Basel 3.1
RWA = K × 12.5 × EAD × MA

Expected Loss (EL)

IRB requires calculation of Expected Loss:

EL = PD × LGD × EAD

EL is compared to provisions: - EL > Provisions: Shortfall deducted from capital - EL < Provisions: Excess added to Tier 2 (with limits)

Detailed Calculation Example

Exposure: - Corporate loan, £50m - Bank-estimated PD: 0.50% - F-IRB (LGD = 45%) - Maturity: 3 years - Counterparty turnover: £25m (SME)

Step 1: Apply PD Floor

PD = max(0.0050, 0.0003) = 0.0050  # 0.50%

Step 2: Calculate Asset Correlation

# Base correlation
R_base = 0.12 × (1 - exp(-50 × 0.005)) / (1 - exp(-50)) +
         0.24 × (1 - (1 - exp(-50 × 0.005)) / (1 - exp(-50)))
R_base = 0.12 × 0.221 + 0.24 × 0.779 = 0.214

# SME adjustment (turnover £25m, converted to EUR)
S_eur = 25 / 0.8732 = 28.63  # GBP → EUR conversion
S = min(50, max(5, 28.63)) = 28.63
adjustment = 0.04 × (1 - (28.63 - 5) / 45) = 0.04 × 0.475 = 0.019
R = 0.214 - 0.019 = 0.195

Step 3: Calculate Capital Requirement (K)

# Intermediate calculations
G_PD = norm.ppf(0.005) = -2.576
G_999 = norm.ppf(0.999) = 3.090

term1 = (1 - R)^(-0.5) × G_PD = 1.115 × (-2.576) = -2.871
term2 = (R / (1-R))^0.5 × G_999 = 0.492 × 3.090 = 1.521

K_pre = LGD × N(term1 + term2) - LGD × PD
K_pre = 0.45 × N(-1.350) - 0.45 × 0.005
K_pre = 0.45 × 0.0885 - 0.00225
K_pre = 0.0398 - 0.00225 = 0.0376

Step 4: Calculate Maturity Adjustment

b = (0.11852 - 0.05478 × ln(0.005))^2 = (0.11852 + 0.290)^2 = 0.167
MA = (1 + (3 - 2.5) × 0.167) / (1 - 1.5 × 0.167)
MA = 1.0835 / 0.7495 = 1.446

Step 5: Calculate RWA

# CRR
RWA_CRR = K × 12.5 × EAD × MA × 1.06
RWA_CRR = 0.0376 × 12.5 × 50,000,000 × 1.446 × 1.06
RWA_CRR = £36,019,860

RW_CRR = RWA / EAD = 72.0%

# Basel 3.1 (no scaling)
RWA_B31 = 0.0376 × 12.5 × 50,000,000 × 1.446 × 1.00
RWA_B31 = £33,981,000

RW_B31 = 68.0%

Step 6: Check Output Floor (Basel 3.1)

# SA equivalent RWA (assume 100% RW corporate)
RWA_SA = 50,000,000 × 100% = £50,000,000

# Output floor (72.5%)
Floor = 50,000,000 × 0.725 = £36,250,000

# Final RWA
RWA_final = max(33,981,000, 36,250,000) = £36,250,000

Implementation

The IRB logic is plain module-level typed functions in irb/transforms.py and irb/formulas.py, composed over a LazyFrame via lf.pipe(fn, config):

import polars as pl
from datetime import date
from rwa_calc.contracts.config import CalculationConfig
from rwa_calc.engine.irb.transforms import (
    classify_approach,    # Determine F-IRB vs A-IRB
    apply_firb_lgd,       # Apply supervisory LGD for F-IRB
    prepare_columns,      # Ensure required columns exist
    apply_all_formulas,   # Run full IRB calculation
)

config = CalculationConfig.crr(reporting_date=date(2026, 12, 31))

# Create sample exposure data
exposures = pl.LazyFrame({
    "exposure_reference": ["EXP001"],
    "pd": [0.005],
    "lgd": [0.45],
    "ead_final": [50_000_000.0],
    "maturity": [3.0],
    "exposure_class": ["CORPORATE"],
    "turnover_m": [25.0],  # Annual turnover in millions
})

# IRB calculation pipeline composed via lf.pipe(fn, config)
result = (
    exposures
    .pipe(classify_approach, config)
    .pipe(apply_firb_lgd, config)
    .pipe(prepare_columns, config)
    .pipe(apply_all_formulas, config)
    .collect()
)

# Access results
print(result.select([
    "pd_floored", "correlation", "k", "maturity_adjustment", "rwa", "expected_loss"
]))

Using the IRB Calculator

import polars as pl
from datetime import date
from rwa_calc.engine.irb.calculator import IRBCalculator
from rwa_calc.contracts.config import CalculationConfig

# Create calculator
calculator = IRBCalculator()

# calculate_branch() takes a pre-filtered IRB LazyFrame and returns a LazyFrame
irb_lf = pl.LazyFrame({
    "exposure_reference": ["EXP001"],
    "pd": [0.005],
    "lgd": [0.45],
    "ead_final": [50_000_000.0],
    "maturity": [3.0],
    "exposure_class": ["CORPORATE"],
    "turnover_m": [25.0],
})

result_lf = calculator.calculate_branch(
    irb_lf,
    CalculationConfig.crr(reporting_date=date(2026, 12, 31)),
)

irb_rwa_df = result_lf.collect()
print(irb_rwa_df.select("rwa", "expected_loss"))

Using IRB Formulas Directly

The IRB formulas are implemented in irb/formulas.py. Both scalar functions and pure Polars expression functions are available.

Implementation Architecture

  • Vectorized expressions: Pure Polars expressions for bulk processing (used by the IRB transforms (engine/irb/transforms.py) and the calculator via lf.pipe(...))
  • Scalar wrappers: Thin wrappers around vectorized expressions for single-value calculations
from rwa_calc.engine.irb.formulas import (
    calculate_k,
    calculate_correlation,
    calculate_maturity_adjustment,
    calculate_irb_rwa,  # Convenience function for single exposures
)

# Calculate components
R = calculate_correlation(
    pd=0.005,
    exposure_class="CORPORATE",
    turnover_m=25.0,  # Turnover in GBP millions (converted to EUR internally)
)

MA = calculate_maturity_adjustment(pd=0.005, maturity=3)

K = calculate_k(pd=0.005, lgd=0.45, correlation=R)

# Calculate RWA
rwa = K * 12.5 * ead * MA * scaling_factor

# Or use the convenience function for complete calculation
result = calculate_irb_rwa(
    ead=50_000_000,
    pd=0.005,
    lgd=0.45,
    correlation=R,
    maturity=3.0,
    apply_scaling_factor=True,  # CRR 1.06 factor
)
print(f"RWA: {result['rwa']:,.0f}")
Scalar K Calculation (formulas.py)

See calculate_k() in src/rwa_calc/engine/irb/formulas.py for the scalar wrapper around the Polars expression.

Expected Loss Calculation

from rwa_calc.engine.irb.formulas import calculate_expected_loss

el = calculate_expected_loss(
    pd=0.005,
    lgd=0.45,
    ead=50_000_000
)
# el = 0.005 × 0.45 × 50,000,000 = £112,500

F-IRB vs A-IRB Comparison

Parameter F-IRB A-IRB
PD Bank estimate Bank estimate
LGD Supervisory (45%/75%) Bank estimate (floored)
CRM for collateral Foundation Collateral Method (Art. 230) LGD modelling (Art. 169A/169B) or FCM
EAD Regulatory Bank estimate
CCF Regulatory (CRR: 75% MR/MLR; B31: SA-aligned via Art. 166C) Bank estimate (B31: revolving only, floored at 50% SA)
Typical RW Higher Lower
Complexity Lower Higher
Approval Easier Harder

Regulatory References

Topic CRR Article BCBS CRE
IRB approach overview Art. 142-150 CRE30
K formula Art. 153 CRE31
Default definition Art. 178 (specification) CRE36.67–82
PD estimation Art. 179-180 CRE32
LGD estimation Art. 181 CRE32
Correlation Art. 153 CRE31
Maturity adjustment Art. 162 CRE31
Supervisory LGD Art. 161 CRE32
F-IRB CCF Art. 166(8)–(9) (CRR) / Art. 166C (B31) CRE32
A-IRB CCF / EAD Art. 166 (CRR) / Art. 166D (B31) CRE32
A-IRB LGD modelling for collateral Art. 169A/169B CRE32
CRM method taxonomy Art. 191A

Next Steps