Skip to content

Settlement risk — Failed trades (Art. 378–380)

CRR Part Three Title V ("Own funds requirements for settlement risk") sits outside the SA-CCR exposure-at-default chain (Art. 274 et seq.) — failed-settlement capital is a separate Pillar 1 charge that runs on its own input frame (RawCCRBundle.failed_trades) and emits RWA directly per row rather than producing an EAD that downstream risk-weight stages consume. The charge applies to two distinct settlement-failure shapes:

  • Delivery-versus-Payment (DvP) transactions unsettled after their due delivery date — Art. 378. The charge is a price-difference ladder that scales with the number of working days past due: at t+5–15 working days the institution holds 8% of the price difference as own funds; at t+46 or more the multiplier reaches 100% of the price difference (the regulatory equivalent of a 1,250% risk weight on that price difference once the standard RWA = own-funds × 12.5 conversion is applied).
  • Free deliveries (non-DvP transactions) — Art. 379. Where the institution has paid for securities, FX or commodities before receiving them (or delivered before receiving payment), the three-column Table 2 routing applies: no charge in Column 2 (up to the first contractual leg), the value-transferred treated as a credit exposure in Column 3 (from the first leg up to four days after the second leg), and a 1,250% risk weight on value_transferred + current_positive_exposure in Column 4 (five business days after the second leg onwards).

Art. 380 layers a system-wide failure waiver on top: where a clearing system, settlement system or CCP suffers a system-wide failure, the competent authority may waive both the Art. 378 and Art. 379 own-funds requirements until the situation is rectified.

This page documents:

  • the Art. 378 Table 1 DvP multiplier ladder (8%, 50%, 75%, 100% by working-days-past-due band) and the price-difference base on which it operates;
  • the Art. 379 free-delivery three-column treatment and the Column-4 1,250% RW that produces the RWA = exposure × 12.5 conversion;
  • the 1,250% RW equivalent at the top of the DvP ladder (cross-link output-floor.md for the underlying 8% capital ratio inverse 1 / 0.08 = 12.5);
  • the Art. 380 system-wide failure waiver (schema-supported as a Boolean flag; engine currently treats waiver as off);
  • the engine entry point (compute_failed_trade_rwa) and its pipeline status (function exists with full unit-test coverage, but is not yet wired into the orchestrator);
  • one worked example per Art. 378 band plus the Art. 379 Column-4 shape, replaying the five-row P8.24 fixture (tests/fixtures/ccr/failed_trade_builder.py) used by the unit- test suite.

Regulatory citation

Primary source: PRA Rulebook — onshored CRR Part Three Title V ("Own funds requirements for settlement risk"), Articles 378–380. The substantive ladder, the free-delivery three-column treatment and the system-wide failure waiver all live in the carry-forward CRR text. PRA PS1/26 does not restate Art. 378–380 in Appendix 1 — instead, the PS1/26 calculation of total_risk_exposure_amount explicitly points back at the CRR articles via Art. 92(3)(ca):

"the own funds requirements for settlement risk calculated in accordance with Articles 378 and 380 of CRR"

— PS1/26 Appendix 1, Required Level of Own Funds (CRR) Part, Article 92(3)(ca), p. 14 (source PDF: docs/assets/ps126app1.pdf).

and at Article 92(3)(a):

"the risk-weighted exposure amounts for credit risk and dilution risk, calculated in accordance with Title II of Part Three of CRR, the credit risk rules, the Counterparty Credit Risk (CRR) Part and Articles 379 and 380 of CRR in respect of all the business activities of an institution […]"

— PS1/26 Appendix 1, Required Level of Own Funds (CRR) Part, Article 92(3)(a), p. 14.

The split is deliberate. Art. 378 (DvP) and Art. 379 (free delivery) produce different output shapes: Art. 378 produces an own-funds requirement that PS1/26 routes through paragraph (ca) ("own funds requirements for settlement risk"); Art. 379 Column 4 produces an RWA at the 1,250% pin that PS1/26 routes through paragraph (a) ("risk-weighted exposure amounts"). The engine collapses both into the same failed_trade_rwa column by applying the standard RWA = own_funds × 12.5 conversion on the DvP branch — but the upstream regulatory categorisation differs.

Sub-article Coverage BCBS cross-reference
Art. 378 ¶1 Scope: unsettled-after-due-date transactions in debt instruments, equities, FX, commodities. Repos and securities lending / borrowing are excluded.
Art. 378 ¶2 Price-difference base: max(0, agreed_settlement_price − current_market_value), taken only where the difference could imply a loss for the institution.
Art. 378 ¶3 + Table 1 Multiplier ladder by working-days-past-due band — 5–15 d → 8%; 16–30 d → 50%; 31–45 d → 75%; ≥ 46 d → 100%.
Art. 379(1) Table 2 Col 2 Up to the first contractual payment / delivery leg: no capital charge.
Art. 379(1) Table 2 Col 3 From the first leg up to four days after the second leg: treat as an exposure (IRB / SA risk-weight per ordinary credit-risk rules).
Art. 379(1) Table 2 Col 4 From five business days after the second leg until extinction: treat as an exposure risk-weighted at 1,250%.
Art. 379(2) IRB PD inference + immateriality 100% RW alternative (engine-deferred — flag present in schema, default False).
Art. 379(3) CET1 deduction alternative to the Column-4 1,250% RW (engine-deferred — flag present in schema, default False).
Art. 380 System-wide failure waiver (engine-deferred — flag present in schema, default False).
PS1/26 Art. 92(3)(a) UK onshoring carry-forward for Art. 379 / 380 RWA contribution.
PS1/26 Art. 92(3)(ca) UK onshoring carry-forward for Art. 378 / 380 own-funds-requirement contribution; standard RWA = own_funds × 12.5 conversion (= 1 / 0.08).

Citation note — watchfire CRR index gap. The bundled watchfire CRR rulebook index (rulebook_version 2026-05-15) does not yet contain CRR Title V (Articles 378–380), so the engine module (src/rwa_calc/engine/ccr/failed_trades.py) deliberately omits the @cites(...) decorators that other CCR modules carry. The article attribution lives entirely in the docstring + this spec page until the watchfire index re-extension lands. See the leading comment on failed_trades.py:71–77 for the same explanation in the source.

Verbatim text — CRR Art. 378

"In the case of transactions in which debt instruments, equities, foreign currencies and commodities excluding repurchase transactions and securities or commodities lending and securities or commodities borrowing are unsettled after their due delivery dates, an institution shall calculate the price difference to which it is exposed.

The price difference is calculated as the difference between the agreed settlement price for the debt instrument, equity, foreign currency or commodity in question and its current market value, where the difference could involve a loss for the credit institution.

The institution shall multiply that price difference by the appropriate factor in the right column of the following Table 1 in order to calculate the institution's own funds requirement for settlement risk."

— CRR (EU 575/2013 as onshored), Part Three Title V, p. 365 (source PDF: docs/assets/crr.pdf).

CRR Art. 378 Table 1 — DvP multiplier ladder

Number of working days after due settlement date (%) own-funds factor
5 — 15 8
16 — 30 50
31 — 45 75
46 or more 100

The table operates on the price difference, not on the full transaction notional. The price difference is the residual loss the institution would incur if it closed out the failed leg at the prevailing market price — agreed_settlement_price (what the counterparty was contractually due to pay / deliver against) less current_market_value (what the institution would now realise in the market), floored at zero so the institution never books a negative own-funds figure on an in-the-money failed trade.

The first 5 working days carry no own-funds requirement under Art. 378 — the regulator's recognition that operational settlement delays of up to one week are a routine market hygiene matter that does not justify a Pillar 1 charge. The 5-band lower bound is the post-settlement grace period built into the ladder itself; it is not a separate Art. 379 mechanism (the plan-item summary on this page conflated the two and is corrected here).

Conversion to RWA — Art. 92(3)(ca) × 12.5

CRR Art. 378 produces an own-funds requirement, not an RWA. The standard 8%-capital-ratio inverse converts to an RWA-equivalent in the firm-level aggregator:

RWA_dvp = own_funds_dvp × 12.5      (12.5 = 1 / 0.08)

At the top of the ladder (≥ 46 working days past due, multiplier 100%), the institution holds the full price difference as own funds — equivalent to applying a 1,250% risk weight to the price difference (100% own-funds factor × 12.5 = 1,250%). This is the same 1,250% pin that Art. 379 Column 4 applies directly to the non-DvP exposure — see output-floor.md § OF-ADJ Capital Adjustment for the canonical discussion of the 12.5 = 1 / 0.08 conversion in the framework-level aggregator.

Verbatim text — CRR Art. 379(1)

"An institution shall be required to hold own funds, as set out in Table 2, where the following occurs:

(a) it has paid for securities, foreign currencies or commodities before receiving them or it has delivered securities, foreign currencies or commodities before receiving payment for them;

(b) in the case of cross-border transactions, one day or more has elapsed since it made that payment or delivery."

— CRR, Part Three Title V, p. 365.

CRR Art. 379(1) Table 2 — Free-delivery capital treatment

Transaction Type Column 2 — Up to first contractual payment or delivery leg Column 3 — From first contractual leg up to four days after second contractual leg Column 4 — From 5 business days post second contractual leg until extinction
Free delivery No capital charge Treat as an exposure Treat as an exposure risk weighted at 1,250%

The three-column structure scales the treatment with the temporal distance between the institution's payment / delivery and the counterparty's reciprocal performance. Column 2 reflects the regulator's recognition that, until the institution itself has performed, no asymmetric exposure exists. Column 3 treats the transferred value as an ordinary credit-risk exposure to the counterparty — risk-weighted via the standard SA / IRB ladder per Title II — once the institution has performed but before five business days have elapsed past the second leg. Column 4 caps the escalation at the 1,250% RW pin — the regulatory expression of "this exposure should consume own funds at the rate of full deduction" under the standard RWA × 8% = own_funds capital ratio.

The engine currently implements only the Column 4 path (the schema and bands cover all three columns; the engine's regulatory_band falls through to dvp_pre_t5 for the Column 2 path, and Column 3 is not yet implemented — see "Engine status" below).

Art. 379(2) — IRB inference + immateriality 100% RW alternative

"In applying a risk weight to free delivery exposures treated according to Column 3 of Table 2, an institution using the Internal Ratings Based approach set out in Part Three, Title II, Chapter 3 may assign PDs to counterparties, for which it has no other non- trading book exposure, on the basis of the counterparty's external rating. […] Alternatively, an institution using the Internal Ratings Based approach […] may apply the risk weights of the Standardised Approach […] or may apply a 100% risk weight to all such exposures.

If the amount of positive exposure resulting from free delivery transactions is not material, institutions may apply a risk weight of 100% to these exposures, except where a risk weight of 1,250% in accordance with Column 4 of Table 2 in paragraph 1 is required."

— CRR, Part Three Title V, p. 366.

The Art. 379(2) immateriality election is preserved on the schema (is_immaterial: Boolean, default=False) but is not consumed by the engine today. Materiality assessment is firm-judgement-driven; the implementation path is to (i) introduce a fresh regulatory_band value "non_dvp_immaterial_100rw", (ii) branch on the flag in compute_failed_trade_rwa, and (iii) skip the immateriality branch on Column 4 rows per the carve-out in the last sentence of Art. 379(2).

Art. 379(3) — CET1 deduction alternative

"As an alternative to applying a risk weight of 1,250% to free delivery exposures according to Column 4 of Table 2 in paragraph 1, institutions may deduct the value transferred plus the current positive exposure of those exposures from Common Equity Tier 1 items in accordance with point (k) of Article 36(1)."

— CRR, Part Three Title V, p. 366.

The CET1 deduction route is the economic dual of the 1,250% RW pin (exposure × 1,250% × 8% = exposure × 100%, i.e. full own-funds consumption either way). The schema preserves the election as elect_cet1_deduction: Boolean, default=False but the engine path is deferred — the deduction would feed the CET1-deduction line of the aggregator rather than the RWA line, requiring a coordinated change across the failed-trades calculator and the aggregator's own-funds surface.

Verbatim text — CRR Art. 380 (system-wide failure waiver)

"Where a system wide failure of a settlement system, a clearing system or a CCP occurs, competent authorities may waive the own funds requirements calculated as set out in Articles 378 and 379 until the situation is rectified. In this case, the failure of a counterparty to settle a trade shall not be deemed a default for purposes of credit risk."

— CRR, Part Three Title V, p. 366.

Art. 380 is the regulatory escape hatch: when the failed settlement is the consequence of a system-wide infrastructure failure (not counterparty distress), the competent authority may suspend the Art. 378 / 379 charges entirely, and — separately — the counterparty's failure to settle is explicitly carved out of the credit-risk default definition (Art. 178). The schema preserves the election as system_wide_failure_waiver: Boolean, default=False so a firm can flag rows under an active PRA waiver; the engine path is deferred — when the flag is True the calculator would short-circuit to own_funds_requirement = 0 and failed_trade_rwa = 0 regardless of the band, and the row would also need to be excluded from any upstream Art. 178 default flagging on the counterparty.


Engine entry point

The failed-trade calculation is a free function on engine/ccr/failed_trades.py that consumes the FailedTradesBundle.failed_trades LazyFrame and emits a per-row LazyFrame with the own-funds requirement, RWA, and a stable regulatory_band audit string for downstream attribution:

from rwa_calc.engine.ccr.failed_trades import compute_failed_trade_rwa

def compute_failed_trade_rwa(
    failed_trades: pl.LazyFrame,
    config: CalculationConfig,
) -> pl.LazyFrame:
    """Compute own-funds and RWA for failed trades per CRR Art. 378 / 379.

    For DvP rows (``settlement_type == "dvp"``): compute
    ``price_difference = max(0, agreed_settlement_price - current_market_value)``
    and look up the Art. 378 Table 1 multiplier by ``working_days_past_due``
    band (5-15, 16-30, 31-45, 46+). Own-funds = price_difference x multiplier;
    RWA = own_funds x 12.5.

    For non-DvP free-delivery rows (``settlement_type ==
    "non_dvp_free_delivery"``) past t+5: compute
    ``exposure_amount = value_transferred + current_positive_exposure``,
    treat as a credit-risk exposure at 1250% RW, so RWA = exposure x 12.5
    and own_funds = exposure (Art. 379(1) Table 2 Column 4)."""

Source: src/rwa_calc/engine/ccr/failed_trades.py::compute_failed_trade_rwa (lines 78–217).

# NOTE: No ``@cites("CRR Art. 378")`` / ``@cites("CRR Art. 379")`` —
# watchfire's bundled rulebook index (rulebook_version 2026-05-15) does not
# yet contain CRR Title V (Settlement Risk) Art. 378-380. Article
# attribution is preserved in the docstring; re-extending the watchfire CRR
# index for Title V is a separate follow-up (mirrors the P8.7 fix-commit
# pattern for Art. 280a/b/c and the existing rc.py / sa_ccr.py waivers for
# Art. 274 / 275).
def compute_failed_trade_rwa(
    failed_trades: pl.LazyFrame,
    config: CalculationConfig,  # noqa: ARG001 — numerics identical under CRR and PS1/26
) -> pl.LazyFrame:
    """Compute own-funds and RWA for failed trades per CRR Art. 378 / 379.

    Args:
        failed_trades: LazyFrame matching ``FAILED_TRADE_SCHEMA`` — one row
            per failed settlement. Required columns: ``failed_trade_id``,
            ``counterparty_reference``, ``settlement_type``,
            ``working_days_past_due``, plus the branch-specific value
            columns (``agreed_settlement_price`` + ``current_market_value``
            for DvP; ``value_transferred`` + ``current_positive_exposure``
            for non-DvP free delivery).
        config: Calculation configuration. The Art. 378/379 numerical
            ladder is identical under CRR and PRA PS1/26, so the framework
            field is not branched on — the parameter is kept for signature
            consistency with sibling CCR calculators.

    Returns:
        LazyFrame with one row per input row, carrying:
        ``failed_trade_id``, ``counterparty_reference``, ``settlement_type``,
        ``working_days_past_due``, ``price_difference`` (DvP-only,
        null otherwise), ``exposure_amount`` (non-DvP-only, null otherwise),
        ``multiplier_or_rw``, ``own_funds_requirement``, ``failed_trade_rwa``,
        ``regulatory_band``.

    References:
        CRR Art. 378 + Table 1 (DvP multiplier ladder);
        CRR Art. 379(1) + Table 2 Col 4 (non-DvP, 1250% RW);
        PRA PS1/26 Art. 92(3)(a), 92(3)(ca).
    """
    is_dvp = pl.col("settlement_type") == _SETTLEMENT_TYPE_DVP
    is_non_dvp = pl.col("settlement_type") == _SETTLEMENT_TYPE_NON_DVP
    days = pl.col("working_days_past_due")

    # DvP price difference: max(0, agreed - mv). Null on non-DvP rows.
    price_difference = (
        pl.when(is_dvp)
        .then(
            pl.max_horizontal(
                pl.col("agreed_settlement_price") - pl.col("current_market_value"),
                pl.lit(0.0),
            )
        )
        .otherwise(pl.lit(None, dtype=pl.Float64))
        .alias("price_difference")
    )

    # Non-DvP exposure: value_transferred + current_positive_exposure.
    # Null on DvP rows.
    exposure_amount = (
        pl.when(is_non_dvp)
        .then(pl.col("value_transferred") + pl.col("current_positive_exposure"))
        .otherwise(pl.lit(None, dtype=pl.Float64))
        .alias("exposure_amount")
    )

    # DvP Art. 378 Table 1 multiplier ladder (highest band wins). The band
    # BOUNDS are int counts from data/tables; the multipliers come from the
    # rulepack (module-level _DVP_MULT_* above).
    dvp_multiplier = (
        pl.when(days >= _DVP_BAND_46_PLUS_LOWER)
        .then(pl.lit(_DVP_MULT_46_PLUS))
        .when(days >= _DVP_BAND_31_45_LOWER)
        .then(pl.lit(_DVP_MULT_31_45))
        .when(days >= _DVP_BAND_16_30_LOWER)
        .then(pl.lit(_DVP_MULT_16_30))
        .when(days >= _DVP_BAND_5_15_LOWER)
        .then(pl.lit(_DVP_MULT_5_15))
        .otherwise(pl.lit(0.0))
    )

    # Combined multiplier_or_rw: DvP multiplier on DvP rows; the Col-4 RWA
    # multiplier (12.5) on non-DvP rows in Column 4. The own-funds factor
    # against the full exposure on non-DvP Col 4 is 1.0 (RW=1250% =>
    # own_funds = exposure; RWA = exposure * 12.5).
    multiplier_or_rw = (
        pl.when(is_dvp)
        .then(dvp_multiplier)
        .when(is_non_dvp & (days >= _NON_DVP_COL4_LOWER))
        .then(pl.lit(_NON_DVP_COL4_RW_MULTIPLIER))
        .otherwise(pl.lit(0.0))
        .alias("multiplier_or_rw")
    )

    # Regulatory band string (audit / aggregation key).
    regulatory_band = (
        pl.when(is_dvp & (days >= _DVP_BAND_46_PLUS_LOWER))
        .then(pl.lit("dvp_46_plus"))
        .when(is_dvp & (days >= _DVP_BAND_31_45_LOWER))
        .then(pl.lit("dvp_31_45"))
        .when(is_dvp & (days >= _DVP_BAND_16_30_LOWER))
        .then(pl.lit("dvp_16_30"))
        .when(is_dvp & (days >= _DVP_BAND_5_15_LOWER))
        .then(pl.lit("dvp_5_15"))
        .when(is_non_dvp & (days >= _NON_DVP_COL4_LOWER))
        .then(pl.lit("non_dvp_col4_t5_plus"))
        .otherwise(pl.lit("dvp_pre_t5"))
        .alias("regulatory_band")
    )

    # Own-funds: DvP = price_difference * multiplier; non-DvP Col 4 =
    # exposure_amount (1.0 factor against the full exposure).
    own_funds = (
        pl.when(is_dvp)
        .then(pl.col("price_difference") * dvp_multiplier)
        .when(is_non_dvp & (days >= _NON_DVP_COL4_LOWER))
        .then(pl.col("exposure_amount"))
        .otherwise(pl.lit(0.0))
        .alias("own_funds_requirement")
    )

    # RWA: own_funds * 12.5 (CRR Art. 92(3)(ca)).
    failed_trade_rwa = (pl.col("own_funds_requirement") * _OWN_FUNDS_TO_RWA_FACTOR).alias(
        "failed_trade_rwa"
    )

    return (
        failed_trades.with_columns([price_difference, exposure_amount])
        .with_columns([multiplier_or_rw, regulatory_band, own_funds])
        .with_columns([failed_trade_rwa])
        .select(
            [
                "failed_trade_id",
                "counterparty_reference",
                "settlement_type",
                "working_days_past_due",
                "price_difference",
                "exposure_amount",
                "multiplier_or_rw",

Inputs (FailedTradesBundle.failed_trades)

The calculator consumes a LazyFrame matching FAILED_TRADE_SCHEMA (defined on src/rwa_calc/data/schemas.py:926). Settlement-type discriminates the two branches: DvP rows must supply agreed_settlement_price + current_market_value; non-DvP rows must supply value_transferred + current_positive_exposure.

Column Source Dtype Notes
failed_trade_id FAILED_TRADE_SCHEMA.failed_trade_id String Primary key — passes through unchanged.
counterparty_reference FAILED_TRADE_SCHEMA.counterparty_reference String Joins the row back to the firm's CP master.
settlement_type FAILED_TRADE_SCHEMA.settlement_type String "dvp" or "non_dvp_free_delivery" — branch discriminator.
working_days_past_due FAILED_TRADE_SCHEMA.working_days_past_due Int32 Number of working days elapsed since due settlement date — drives the Art. 378 / 379 band lookup.
instrument_class FAILED_TRADE_SCHEMA.instrument_class String "debt" \| "equity" \| "fx" \| "commodity" — audit attribution only; not consumed by the formula.
agreed_settlement_price FAILED_TRADE_SCHEMA.agreed_settlement_price Float64 DvP-only required. Null on non-DvP rows. Art. 378 ¶2 price-difference numerator.
current_market_value FAILED_TRADE_SCHEMA.current_market_value Float64 DvP-only required. Null on non-DvP rows. Art. 378 ¶2 price-difference subtrahend.
value_transferred FAILED_TRADE_SCHEMA.value_transferred Float64 Non-DvP-only required. Null on DvP rows. Art. 379(1) exposure numerator (the "value transferred" leg).
current_positive_exposure FAILED_TRADE_SCHEMA.current_positive_exposure Float64 Non-DvP-only required. Null on DvP rows. Art. 379(1) exposure addend.
is_repo_or_sec_lending FAILED_TRADE_SCHEMA.is_repo_or_sec_lending Boolean (default False) Art. 378 ¶1 scope exclusion — not currently consumed (engine treats every row as in scope).
is_immaterial FAILED_TRADE_SCHEMA.is_immaterial Boolean (default False) Art. 379(2) immateriality election — not currently consumed.
elect_cet1_deduction FAILED_TRADE_SCHEMA.elect_cet1_deduction Boolean (default False) Art. 379(3) CET1 deduction election — not currently consumed.
system_wide_failure_waiver FAILED_TRADE_SCHEMA.system_wide_failure_waiver Boolean (default False) Art. 380 waiver — not currently consumed.

Outputs

The calculator returns a LazyFrame with one row per input row and the following derived columns. The two branch-specific value columns (price_difference and exposure_amount) are emitted on both branches as nullable Floats so the resulting frame has a stable shape; non-applicable cells are null.

Column Dtype Formula Article
price_difference Float64 (nullable) max(0, agreed_settlement_price − current_market_value) on DvP rows; null on non-DvP rows. Art. 378 ¶2
exposure_amount Float64 (nullable) value_transferred + current_positive_exposure on non-DvP rows; null on DvP rows. Art. 379(1) Col 4
multiplier_or_rw Float64 DvP rows: Table 1 multiplier (0.08 / 0.50 / 0.75 / 1.00 / 0.0); non-DvP Col 4 rows: 12.5; 0.0 otherwise. Art. 378 Table 1; Art. 379(1) Table 2
own_funds_requirement Float64 DvP: price_difference × multiplier; non-DvP Col 4: exposure_amount; 0.0 otherwise. Art. 378 ¶3; Art. 379(1)
failed_trade_rwa Float64 own_funds_requirement × 12.5 (= 1 / 0.08). Art. 92(3)(ca)
regulatory_band String One of dvp_5_15, dvp_16_30, dvp_31_45, dvp_46_plus, non_dvp_col4_t5_plus, dvp_pre_t5. Audit / attribution

Pending — engine wiring gaps

Engine gap — orchestrator hook missing (P8.24 follow-up)

compute_failed_trade_rwa is implemented end-to-end and pinned by a 6-test unit suite (tests/unit/ccr/test_failed_trades.py) but is not yet wired into the ccr_sa_ccr registry stage (src/rwa_calc/engine/stages/ccr.py) or src/rwa_calc/engine/ccr/pipeline_adapter.py. The FAILED_TRADE_SCHEMA and the regulatory scalars — now cited pack params in src/rwa_calc/rulebook/packs/common.py (DvP multipliers failed_trade_dvp_mult_*, band lower-bound IntParams failed_trade_dvp_band_*_lower_days / failed_trade_non_dvp_col4_lower_days, non-DvP Col-4 RW multiplier failed_trade_non_dvp_col4_rw_multiplier, own_funds_to_rwa_factor) — are in place, but the orchestrator does not yet read a failed_trades leaf off the RawCCRBundle or aggregate the resulting failed_trade_rwa into the firm-level totals. Until the wiring lands, firms with failed settlements must compute the Art. 378 / 379 RWA off-system and fold it into the aggregator's manual_addon channel (or report-only adjustment).

The following Art. 378–380 sub-articles are not consumed by the engine today (all schema-supported with default-False Boolean flags):

Gap Article Engine state
Art. 378 ¶1 repo / securities-lending / securities-borrowing exclusion gate Art. 378 ¶1 is_repo_or_sec_lending flag on FAILED_TRADE_SCHEMA, not branched on.
Pre-t+5 DvP rows Art. 378 Table 1 Produce regulatory_band = "dvp_pre_t5", multiplier_or_rw = 0.0, own_funds = 0.0 — the correct outcome by construction.
Art. 379(1) Table 2 Column 2 (pre-first-leg) / Column 3 (post-first-leg up to t+4 after second leg) Art. 379(1) Schema present; engine's regulatory_band falls through to dvp_pre_t5 for the Column 2 path, and Column 3 is not yet implemented (the Column-3 path requires routing to the SA / IRB risk-weight ladder rather than a fixed pin).
Art. 379(2) immateriality 100% RW alternative Art. 379(2) is_immaterial flag present; not branched on.
Art. 379(3) CET1 deduction election Art. 379(3) elect_cet1_deduction flag present; not branched on. Would require a CET1-deduction emission to the aggregator.
Art. 380 system-wide failure waiver Art. 380 system_wide_failure_waiver flag present; not branched on. Would short-circuit the row to zero capital.

The implementation notes are preserved in the engine module docstring on failed_trades.py:23–40 for the engine-implementer follow-up.


Pipeline ordering

PipelineOrchestrator.run_with_data
  → Loader
  → ccr_sa_ccr registry stage (engine/stages/ccr.py)
      ├─ apply_legal_enforceability_gate    (Art. 272(4))
      ├─ apply_wwr_gate                     (Art. 291(5)(a))
      ├─ ccr_rows_to_exposures              (SA-CCR EAD chain → CCR_DERIVATIVE rows)
      └─ compute_failed_trade_rwa           (Art. 378-380; this page)     ← PENDING ORCHESTRATOR WIRING
            └─ per-row failed_trade_rwa  → aggregator (manual_addon today)
  → Classifier / CRM / SA / IRB
  → OutputAggregator                        (firm-level totals + output floor)

The failed-trade calculator is strictly orthogonal to the SA-CCR EAD chain — it does not consume any SA-CCR output and is not consumed by any SA-CCR downstream stage. Architecturally it lives in the same engine/ccr/ package because settlement-failure rows arrive on the same RawCCRBundle input as SA-CCR derivatives data; mechanically it is a self-contained Pillar 1 stage that produces RWA directly.

Interaction with the output floor

The 1,250% RW pin at the top of both the DvP ladder (46+ band) and the non-DvP Column 4 is invariant under the output floor: the Basel 3.1 output floor compares IRB-driven RWA against an SA-equivalent RWA at 72.5% (see output-floor.md), but the failed-trade RWA has no IRB / SA divergence to floor — both branches of the floor calculation see the same own_funds × 12.5 contribution. The failed-trade row therefore enters both the U-TREA (unfloored) and S-TREA (standardised) legs of the floor comparison identically and does not contribute to any floor binding / non-binding state.

The 12.5 = 1 / 0.08 conversion factor used to translate the Art. 378 own-funds requirement into RWA is the same multiplier that output-floor.md § OF-ADJ Capital Adjustment discusses for the OF-ADJ own-funds-to-RWA translation — both arise from the inverse of the 8% Pillar 1 minimum capital ratio.


Worked numeric examples

All five examples below are pinned by the P8.24 unit-test suite at tests/unit/ccr/test_failed_trades.py via the fixture builder tests/fixtures/ccr/failed_trade_builder.py. The five rows are designed to exercise every published Art. 378 band plus the Art. 379 Column 4 path, with the portfolio aggregate adding to 29,737,500.

Example 1 — DvP band 5–15 (multiplier 8%) — FT001

A debt-instrument settlement that has failed five working days past its due settlement date. The price difference is 1,000,000 − 950,000 = 50,000; the Art. 378 Table 1 multiplier for the 5–15 band is 0.08:

Inputs:
  settlement_type            = "dvp"
  working_days_past_due      = 5                        (within the 5-15 band)
  agreed_settlement_price    = 1,000,000
  current_market_value       =   950,000

Art. 378 ¶2 price difference:
  price_difference           = max(0, 1,000,000 - 950,000)   = 50,000

Art. 378 Table 1 band lookup:
  regulatory_band            = "dvp_5_15"                    (5 ≥ 5; 5 < 16)
  multiplier_or_rw           = 0.08                          (Table 1: 5-15 → 8%)

Art. 378 ¶3 own-funds requirement:
  own_funds_requirement      = 50,000 × 0.08                = 4,000

Art. 92(3)(ca) RWA conversion:
  failed_trade_rwa           = 4,000 × 12.5                 = 50,000

Example 2 — DvP band 16–30 (multiplier 50%) — FT002

The same shape with a larger price difference (200,000) sitting 20 working days past due — comfortably inside the 16–30 band:

Inputs:
  working_days_past_due      = 20
  agreed_settlement_price    = 2,000,000
  current_market_value       = 1,800,000
  price_difference           = max(0, 2,000,000 - 1,800,000) = 200,000

Band lookup:
  regulatory_band            = "dvp_16_30"                   (20 ≥ 16; 20 < 31)
  multiplier_or_rw           = 0.50

own-funds + RWA:
  own_funds_requirement      = 200,000 × 0.50              = 100,000
  failed_trade_rwa           = 100,000 × 12.5              = 1,250,000

The 6.25× RWA uplift between Example 1 and Example 2 (50,000 → 1,250,000) is driven by both the larger price difference (4×) and the 6.25× multiplier step (0.08 → 0.50) — the regulator's quantitative expression that 16+ working days past due is materially worse than the 5-15 grace tier.

Example 3 — DvP band 31–45 (multiplier 75%) — FT003

Inputs:
  working_days_past_due      = 35                          (within the 31-45 band)
  price_difference           = max(0, 500,000 - 400,000)   = 100,000

Band lookup:
  regulatory_band            = "dvp_31_45"
  multiplier_or_rw           = 0.75

own-funds + RWA:
  own_funds_requirement      = 100,000 × 0.75              = 75,000
  failed_trade_rwa           = 75,000 × 12.5               = 937,500

Example 4 — DvP band 46+ (multiplier 100% — the 1,250% RW equivalent) — FT004

The top of the Art. 378 ladder. At 46+ working days past due the institution holds the entire price difference as own funds — mechanically equivalent to applying a 1,250% risk weight to the price difference once the standard × 12.5 conversion lands:

Inputs:
  working_days_past_due      = 50                          (≥ 46)
  price_difference           = max(0, 750,000 - 600,000)   = 150,000

Band lookup:
  regulatory_band            = "dvp_46_plus"
  multiplier_or_rw           = 1.00                        (the regulatory cap)

own-funds + RWA:
  own_funds_requirement      = 150,000 × 1.00              = 150,000
  failed_trade_rwa           = 150,000 × 12.5              = 1,875,000

Cross-check — equivalent risk weight:
  effective_rw = failed_trade_rwa / price_difference        = 1,875,000 / 150,000
                                                            = 12.5
                                                            = 1,250%        ← Art. 379 Col 4 equivalent

The 1,875,000 / 150,000 = 12.5 = 1,250% cross-check is the load- bearing equivalence between the top of the Art. 378 ladder and the Art. 379 Column 4 pin — both routes produce identical capital consumption on the affected exposure base (the price difference under Art. 378; the full transferred value plus current positive exposure under Art. 379 Col 4). The 12.5 conversion factor is the inverse of the 8% minimum capital ratio — see output-floor.md § OF-ADJ Capital Adjustment for the canonical discussion in the framework-level aggregator.

Example 5 — Non-DvP free delivery, Column 4 (1,250% RW direct) — FT005

A free-delivery transaction six working days past the second contractual leg — into the Art. 379(1) Table 2 Column 4 band. The exposure base is the sum of the value the institution transferred to the counterparty (1,000,000) and the current positive mark-to-market on the open second leg (1,050,000):

Inputs:
  settlement_type              = "non_dvp_free_delivery"
  working_days_past_due        = 6                          (≥ 5; into Col 4)
  value_transferred            = 1,000,000
  current_positive_exposure    = 1,050,000

Art. 379(1) Col 4 exposure base:
  exposure_amount              = 1,000,000 + 1,050,000     = 2,050,000

Col 4 risk-weight pin:
  regulatory_band              = "non_dvp_col4_t5_plus"
  multiplier_or_rw             = 12.5                       (1,250% RW direct)
  own_funds_requirement        = 2,050,000 × 1.00          = 2,050,000   (RW = 1,250% ⇒ own-funds factor = 1.0)
  failed_trade_rwa             = 2,050,000 × 12.5          = 25,625,000

Note that on the non-DvP Column 4 branch the engine sets multiplier_or_rw = 12.5 directly (rather than the 0.08 / 0.50 / 0.75 / 1.00 DvP multipliers). This is the column's regulatory pin expressed as the RWA multiplier — own_funds = exposure × 1.0 (because the 1,250% RW implies an own-funds factor of exactly 1.0 against the full exposure), and RWA = own_funds × 12.5 = exposure × 12.5 directly. The implementation's multiplier_or_rw expression preserves this asymmetry deliberately so the column carries audit-trail value (DvP rows show the 8% / 50% / 75% / 100% multiplier; non-DvP Col 4 rows show the 12.5 RW multiplier).

Portfolio aggregate

FT001 (DvP   t+5  band dvp_5_15)            RWA       50,000
FT002 (DvP   t+20 band dvp_16_30)           RWA    1,250,000
FT003 (DvP   t+35 band dvp_31_45)           RWA      937,500
FT004 (DvP   t+50 band dvp_46_plus)         RWA    1,875,000
FT005 (non-DvP t+6 band non_dvp_col4_t5+)   RWA   25,625,000
                                            ----------------
Total failed-trade RWA                            29,737,500

The non-DvP Column 4 row dominates the portfolio total (86%) — a direct consequence of the 1,250% RW pin applying to the full transferred value plus current positive exposure, not to a residual price difference. The four DvP rows together (8.5% of the portfolio) demonstrate the laddered nature of Art. 378: the 100%-multiplier band at the top is 10× higher RWA than the 8% band at the bottom on comparable price differences — see Example 1 vs Example 4 for the direct illustration (50,000 vs 1,875,000 RWA on price differences of 50,000 vs 150,000 respectively).


References

  • PRA Rulebook — onshored CRR Part Three Title V, Article 378 — DvP settlement-risk own-funds-requirement ladder; price-difference base; Table 1 multipliers by working-days-past-due band (5-15 → 8%; 16-30 → 50%; 31-45 → 75%; 46+ → 100%).
  • PRA Rulebook — onshored CRR Part Three Title V, Article 379(1) — free-delivery three-column treatment; Column 4 1,250% RW from t+5 business days after the second contractual leg.
  • PRA Rulebook — onshored CRR Part Three Title V, Article 379(2) — IRB PD inference + immateriality 100% RW alternative (engine- deferred; flag on schema).
  • PRA Rulebook — onshored CRR Part Three Title V, Article 379(3) — CET1 deduction alternative to the Column 4 1,250% RW (engine- deferred; flag on schema).
  • PRA Rulebook — onshored CRR Part Three Title V, Article 380 — system-wide failure waiver (engine-deferred; flag on schema).
  • PRA PS1/26 Appendix 1, Required Level of Own Funds (CRR) Part, Article 92(3)(a) — UK onshoring carry-forward routing Art. 379 / 380 RWA into total_risk_exposure_amount.
  • PRA PS1/26 Appendix 1, Required Level of Own Funds (CRR) Part, Article 92(3)(ca) — UK onshoring carry-forward routing Art. 378 / 380 own-funds-requirement into total_risk_exposure_amount via the standard × 12.5 = 1 / 0.08 conversion.
  • src/rwa_calc/engine/ccr/failed_trades.py::compute_failed_trade_rwa — engine implementation (lines 78–217) of the Art. 378 / 379 calculator.
  • src/rwa_calc/rulebook/packs/common.py — cited pack params: DvP multipliers (failed_trade_dvp_mult_* = 0.08 / 0.50 / 0.75 / 1.00), band lower-bound IntParams (failed_trade_dvp_band_*_lower_days, failed_trade_non_dvp_col4_lower_days = 5 / 16 / 31 / 46 days), non-DvP Col-4 RW multiplier (failed_trade_non_dvp_col4_rw_multiplier = 12.5), own_funds_to_rwa_factor (12.5) — resolved in engine/ccr/failed_trades.py via _PACK.scalar_param(...) / _PACK.int_param(...).
  • src/rwa_calc/data/schemas.py::FAILED_TRADE_SCHEMA — failed-trade input schema; the four electives (is_repo_or_sec_lending, is_immaterial, elect_cet1_deduction, system_wide_failure_waiver) default to False per the Art. 378-380 scope rules.
  • tests/unit/ccr/test_failed_trades.py — unit-test suite pinning the five-row P8.24 portfolio and the portfolio-aggregate RWA of 29,737,500.
  • tests/fixtures/ccr/failed_trade_builder.py — P8.24 fixture builder; single source of truth for the five-row scenario consumed by the worked examples on this page.
  • Output floor (Basel 3.1) — OF-ADJ — the canonical discussion of the 12.5 = 1 / 0.08 own-funds-to-RWA conversion factor that the failed-trade calculator uses to translate the Art. 378 own-funds requirement into RWA.
  • Legal enforceability — sibling CCR spec page; the failed-trade calculator is independent of the SA-CCR legal-enforceability gate (failed trades operate on their own input frame).
  • CCP exposures — sibling CCR spec page; the Art. 380 waiver carve-out specifically covers system-wide failures of CCPs alongside settlement / clearing systems.
  • CCR index — full SA-CCR specification index with the failed-trades row at line 90.