SA-CCR — Replacement cost (Art. 275)¶
The replacement cost (RC) is the netting-set-grain measure of current counterparty exposure — the loss the institution would suffer today if the counterparty defaulted and every trade in the netting set had to be closed out at prevailing market prices, net of any collateral already held. RC is the first of the two components of the SA-CCR exposure formula (Art. 274(2)):
SA-CCR carries two RC branches:
- Unmargined netting sets — Art. 275(1) —
RC = max(V − C, 0). No CSA, so the bank carries the full mark-to-market shortfall whenever collateral does not cover the positive netting-set value. - Margined netting sets — Art. 275(2) —
RC = max(V − C, TH + MTA − NICA, 0). A CSA caps the counterparty's uncollateralised exposure at the threshold (TH) plus the minimum-transfer-amount buffer (MTA), net of any independent collateral already pledged (NICA). Even a fully-collateralised netting set retains a residual RC because the bank can only call for variation margin once losses breachTH + MTA.
The two formulas share inputs V (per-netting-set MTM aggregate) and
C (haircut-adjusted net collateral held) and differ only in the
right-hand floor that the margined branch adds. This page documents the
two formulas, the NICA / TH / MTA definitions, the engine entry
points, and the worked examples pinned by the unit test suite. The
upstream MPOR_eff cascade that feeds the margined maturity factor
sits one level up the chain on
maturity-factor.md — RC
itself is independent of MPOR_eff, but the margined branch only
engages on netting sets that also satisfy the Art. 285 margined-NS
gate.
Regulatory citation¶
Primary source: PRA PS1/26 Counterparty Credit Risk (CRR) Part — Article 275 (replacement cost) and Article 272 (definitions of margin agreement, NICA, threshold, MTA). The UK regime is a verbatim re-export of the onshored CRR text with the Basel 3.1 calibration retained. References below follow the PRA-priority convention: PRA Art. numbers first, BCBS CRE52 cross-reference second.
| Sub-article | Coverage | BCBS cross-reference |
|---|---|---|
| Art. 275(1) | Unmargined RC = max(V − C, 0) |
CRE52.10 |
| Art. 275(2) | Margined RC = max(V − C, TH + MTA − NICA, 0) |
CRE52.11 |
| Art. 272(7) | Definition of margin agreement | CRE50.11 |
| Art. 272(9) | Definition of margin period of risk — feeds MPOR_eff on the upstream MF stage |
CRE50.13 |
| Art. 271(7) | Definition of Net Independent Collateral Amount (NICA) | CRE50.16 |
| Art. 274(2) | EAD = α · (RC + PFE) — the consumer of RC |
CRE52.1 |
| Art. 285(2)–(5) | Margined-NS gate and MPOR cascade (maturity-factor.md) | CRE52.48–52 |
(a) Unmargined formula — Art. 275(1)¶
For a netting set without a legally enforceable margin agreement, the replacement cost is the larger of (i) the current market exposure net of collateral, and (ii) zero:
where:
V=v_net— the signed sum of mark-to-market values over every trade in the netting set. PositiveVmeans the netting set is in-the-money to the bank (counterparty owes); negativeVmeans out-of-the-money (bank owes).C=c_net— the net collateral held against the netting set, haircut-adjusted per the Title II CRM rules and signed positive when held by the bank. Collateral pledged by the bank (negativec_net) enlargesV − Cand therefore RC.
The max(·, 0) floor reflects the asymmetric default payoff: a bank
cannot owe the counterparty a positive replacement cost — if the
counterparty defaults while owing money to the bank (V − C > 0) the
bank loses; if the bank is the one out-of-the-money (V − C < 0) the
bank simply walks away from the obligation, RC = 0.
Worked example — positive V¶
V = +2,000,000 (in-the-money to the bank)
C = +1,850,000 (collateral held, haircut-adjusted)
V − C = +150,000
RC = max(150,000, 0) = 150,000
Worked example — negative V¶
V = −500,000 (out-of-the-money to the bank)
C = 0 (no collateral)
V − C = −500,000
RC = max(−500,000, 0) = 0
The negative-V case demonstrates the asymmetry: even though the bank
has an economic loss embedded in the trades today, the replacement
cost — the loss given counterparty default — is zero. The bank's
liability survives the counterparty's default and is unaffected by
SA-CCR.
Engine entry point¶
from rwa_calc.engine.ccr.rc import compute_rc_unmargined
def compute_rc_unmargined(netting_sets: pl.LazyFrame) -> pl.LazyFrame:
"""RC_unmargined = max(v_net − c_net, 0). Reads ``v_net`` and
``c_net`` on the netting-set-grain LazyFrame; writes a
``rc_unmargined: Float64`` column."""
Source: src/rwa_calc/engine/ccr/rc.py::compute_rc_unmargined.
def compute_rc_unmargined(netting_sets: pl.LazyFrame) -> pl.LazyFrame:
"""Replacement Cost for unmargined transactions (Art. 275(1)).
Adds an ``rc_unmargined`` column to ``netting_sets`` containing
``max(v_net - c_net, 0)``.
Args:
netting_sets: LazyFrame with at least ``v_net`` and ``c_net`` columns
at netting-set grain.
Returns:
LazyFrame with an additional ``rc_unmargined`` Float64 column.
"""
return netting_sets.with_columns(
pl.max_horizontal(pl.col("v_net") - pl.col("c_net"), pl.lit(0.0)).alias("rc_unmargined")
)
(b) Margined formula — Art. 275(2)¶
For a netting set covered by a legally enforceable margin agreement (typically an ISDA Master Agreement with a CSA requiring at least daily exchange of variation margin), the replacement cost is the maximum of three terms:
The middle term, TH + MTA − NICA, is the largest exposure the bank
can hold without triggering a margin call — and therefore the
smallest RC the bank can guarantee on demand. Even a CSA with zero
threshold and zero MTA cannot drive RC below −NICA, because
independent collateral is posted up-front and not reclaimable on a
single-day default. The three-way max enforces three distinct
economic floors:
| Arm | Economic meaning | When it dominates |
|---|---|---|
V − C |
Current uncollateralised market exposure | The CSA is operating as designed and the netting set is materially in-the-money relative to the CSA thresholds. |
TH + MTA − NICA |
The CSA's permitted uncollateralised buffer | The CSA is operating as designed but the netting set is only marginally in-the-money — RC is pinned to the buffer the bank tolerates before calling. |
0 |
The same default-asymmetry floor as Art. 275(1) | Both arms are negative (e.g. the netting set is out-of-the-money and NICA > TH + MTA). |
NICA — Net Independent Collateral Amount (Art. 271(7))¶
NICA is independent collateral — collateral posted up-front that secures the netting set regardless of mark-to-market — measured net of the directional flow:
NICA = (independent collateral held by the bank from the counterparty)
+ (segregated initial margin held by the bank from the counterparty)
− (independent collateral pledged by the bank to the counterparty,
non-segregated)
The asymmetry between "segregated IM held" (included) and "non-segregated IM pledged" (subtracted) is deliberate: segregated IM posted by the bank is bankruptcy-remote and would be returned on the counterparty's default, so it does not reduce NICA on the bank's balance sheet. Non-segregated IM the bank pledges is at risk and so shows up negatively.
NICA enters Art. 275(2) subtractively because it reduces the
buffer above which a margin call would be triggered: a counterparty
that has posted £200,000 of segregated IM and a CSA with TH = £50,000,
MTA = £10,000 has effectively a negative buffer
(50,000 + 10,000 − 200,000 = −140,000), so the IM contributes
directly to RC reduction up to (but not below) the zero floor.
TH — Threshold (Art. 272(7))¶
The threshold is the unsecured-exposure amount specified in the
CSA that the bank tolerates without calling for variation margin.
TH ≥ 0. A "zero-threshold" CSA (TH = 0) collateralises every
basis point of exposure; a high-threshold CSA (TH = £25m) lets the
counterparty run up to £25m of net positive MTM before any VM call.
MTA — Minimum Transfer Amount (Art. 272 definitions)¶
The minimum transfer amount is the operational floor for any
single margin call. MTA ≥ 0. CSAs use MTA to avoid the operational
overhead of calling for £100 of margin when the daily MTM moves by a
trivial amount — the CSA only calls when the cumulative
uncollateralised exposure exceeds TH + MTA. The bank therefore
carries both TH and MTA worth of exposure as a structural part of
the CSA, which is why both add into the RC floor.
Interaction with the Art. 285 MPOR cascade¶
The Art. 275(2) formula governs the current replacement cost on
the reporting date — it does not include any uplift for the
margin-period-of-risk close-out window. The MPOR enters SA-CCR one
level up the chain via the margined maturity factor
MF_margined = 1.5 × sqrt(MPOR_eff / 250), which scales the PFE
add-on rather than RC. See
maturity-factor.md § MPOR cascade — Art. 285(2)–(5)
for the full five-step cascade that derives MPOR_eff. RC and PFE
combine into EAD per Art. 274(2):
So even when the CSA pins RC to TH + MTA − NICA, an MPOR upgrade
(e.g. >5,000-trade netting set forcing MPOR_eff = 20 BD per
Art. 285(3)(a)) lifts the PFE branch and therefore the EAD without
touching RC itself.
Engine entry point¶
from rwa_calc.engine.ccr.rc import compute_rc_margined
def compute_rc_margined(netting_sets: pl.LazyFrame) -> pl.LazyFrame:
"""RC_margined = max(v_net − c_net, margin_threshold + minimum_transfer_amount
− nica, 0) for is_margined=True rows; null otherwise. Reads
``v_net``, ``c_net``, ``is_margined``, ``margin_threshold``,
``minimum_transfer_amount``, ``nica``; writes ``rc_margined:
Float64``."""
Source: src/rwa_calc/engine/ccr/rc.py::compute_rc_margined.
def compute_rc_margined(netting_sets: pl.LazyFrame) -> pl.LazyFrame:
"""Replacement Cost for margined transactions (Art. 275(2)).
Adds an ``rc_margined`` column to ``netting_sets`` containing
``max(v_net - c_net, margin_threshold + minimum_transfer_amount - nica, 0)``
for rows with ``is_margined=True``; null for unmargined rows.
Args:
netting_sets: LazyFrame with ``v_net``, ``c_net``, ``is_margined``,
``margin_threshold``, ``minimum_transfer_amount``, and ``nica``
columns at netting-set grain.
Returns:
LazyFrame with an additional ``rc_margined`` Float64 column.
"""
return netting_sets.with_columns(
pl.when(pl.col("is_margined"))
.then(
pl.max_horizontal(
pl.col("v_net") - pl.col("c_net"),
pl.col("margin_threshold") + pl.col("minimum_transfer_amount") - pl.col("nica"),
pl.lit(0.0),
)
)
.otherwise(pl.lit(None, dtype=pl.Float64))
.alias("rc_margined")
)
The function deliberately returns null on unmargined rows
(is_margined = False) rather than zero, so a downstream
coalesce-style join can pick the correct branch without an explicit
filter:
Worked examples — tests/unit/ccr/test_rc_margined.py¶
All four examples below are pinned by the unit-test suite in
tests/unit/ccr/test_rc_margined.py
via the fixture builder
tests/fixtures/ccr/rc_margined_builder.py.
Example 1 — TH + MTA − NICA floor dominates (NS_A)¶
A marginally in-the-money netting set under a CSA with material threshold and MTA, partially offset by independent collateral:
V = +2,000,000 (in-the-money)
C = +1,850,000 (haircut-adjusted VM held)
V − C = +150,000
TH = 250,000
MTA = 100,000
NICA = 50,000
TH + MTA − NICA = 300,000
RC_margined = max(150,000, 300,000, 0) = 300,000
The CSA's TH + MTA = 350,000 permits the counterparty to run up to
£350,000 of uncollateralised exposure before a margin call would
trigger; the £50,000 of NICA offsets some of that, leaving the bank
exposed to £300,000 even though current MTM net of VM is only
£150,000. This is the central regulatory point of Art. 275(2): the
CSA's structural buffer dominates current MTM whenever the netting
set is operating inside the buffer.
Example 2 — V − C arm dominates (NS_B)¶
A deeply in-the-money netting set whose current MTM exceeds the CSA buffer:
V = +1,500,000
C = +400,000
V − C = +1,100,000
TH = 100,000
MTA = 50,000
NICA = 25,000
TH + MTA − NICA = 125,000
RC_margined = max(1,100,000, 125,000, 0) = 1,100,000
When the netting set has moved through the CSA buffer, the bank
should already have called for additional VM — but until that VM is
received and reflected in C, the bank still carries the full
V − C shortfall as RC.
Example 3 — zero floor dominates (NS_C)¶
A deeply out-of-the-money netting set with NICA exceeding the CSA buffer:
V = −500,000 (out-of-the-money to the bank)
C = 0
V − C = −500,000
TH = 50,000
MTA = 10,000
NICA = 200,000
TH + MTA − NICA = −140,000
RC_margined = max(−500,000, −140,000, 0) = 0
Both the current-exposure arm and the CSA-buffer arm are negative — NICA is so large that the structural buffer goes net negative. The zero floor of Art. 275(2) prevents the bank from booking negative RC against the counterparty.
Example 4 — unmargined pass-through (NS_D)¶
When is_margined = False, the function emits null rather than
applying the margined formula. This lets the orchestrator coalesce
the margined and unmargined branches in a single pipeline pass
without a per-row filter — compute_rc_unmargined handles the same
row.
is_margined = False
margin_threshold = null
minimum_transfer_amount = null
nica = null
RC_margined = null (pass-through; Art. 275(1) applies)
Pipeline ordering¶
trades → years_to_maturity
→ adjusted_notional (Art. 279b)
→ supervisory_delta (Art. 279a)
→ maturity_factor (Art. 279c — unmargined OR margined per Art. 285)
→ assign_hedging_set (Art. 277)
→ compute_addon_per_asset_class (Art. 277a)
→ ↓ per-NS aggregation
netting_sets joined with per-NS v_net, c_net, addon_aggregate
→ compute_rc_unmargined (Art. 275(1)) ← this page
→ compute_rc_margined (Art. 275(2)) ← this page
→ compute_pfe (Art. 278)
→ ead = α × (RC + PFE) (Art. 274(2))
The orchestrator at
engine/ccr/pipeline_adapter.py::ccr_rows_to_exposures
currently routes only the unmargined branch — every netting set is
treated as unmargined for the CCR-A1 / CCR-A2 acceptance scenarios.
The margined function is implemented end-to-end and pinned by the
unit-test suite, but is not yet routed through the orchestrator
pending the margined-netting-set acceptance batch (CCR-A3, pending
engine batch P8.35–P8.38).
Inputs¶
| Column | Source | Schema | Notes |
|---|---|---|---|
v_net |
pipeline_adapter per-NS aggregation of trade.mtm_value |
Float64 |
Signed; fill_null(0.0) on empty NS. |
c_net |
pipeline_adapter per-NS aggregation of ccr_collateral.collateral_value |
Float64 |
Signed; haircut adjustment applied upstream of the aggregation per Title II. |
is_margined |
NETTING_SET_SCHEMA.is_margined |
Boolean (default False) |
Margined-branch gate. |
margin_threshold |
NETTING_SET_SCHEMA.margin_threshold |
Float64 (nullable) |
TH; null when is_margined = False. |
minimum_transfer_amount |
NETTING_SET_SCHEMA.minimum_transfer_amount |
Float64 (nullable) |
MTA; null when is_margined = False. |
nica |
NETTING_SET_SCHEMA.nica |
Float64 (nullable) |
Per Art. 271(7); null when is_margined = False. |
Outputs¶
| Column | Source | Schema | Notes |
|---|---|---|---|
rc_unmargined |
compute_rc_unmargined |
Float64 |
Always populated. Floored at 0. |
rc_margined |
compute_rc_margined |
Float64 (nullable) |
Null when is_margined = False; floored at 0 otherwise. |
The two columns coexist on the NS-grain frame so the orchestrator can
coalesce them into a single effective RC without re-running either
stage. The synthetic exposure row emitted by pipeline_adapter carries
rc_unmargined on every row today (margined branch not yet wired);
once margined routing lands, both columns travel through to the
COREP-reconciliation surface.
References¶
- PRA PS1/26 Counterparty Credit Risk (CRR) Part — Article 275 — unmargined and margined replacement cost.
- PRA PS1/26 Counterparty Credit Risk (CRR) Part — Article 274(2) —
consumes RC into the EAD formula
α · (RC + PFE). - PRA PS1/26 Counterparty Credit Risk (CRR) Part — Article 271(7) — definition of Net Independent Collateral Amount (NICA).
- PRA PS1/26 Counterparty Credit Risk (CRR) Part — Article 272(7)–(9) — definitions of margin agreement, margin period of risk, threshold, and minimum transfer amount.
- PRA PS1/26 Counterparty Credit Risk (CRR) Part — Article 285(2)–(5) — margined-NS gate and MPOR cascade; consumed by the margined maturity factor one stage upstream, not by RC itself (maturity-factor.md).
- BCBS CRE52.10–11 — Basel-level RC formulas (unmargined and margined).
- BCBS CRE50.11, CRE50.13, CRE50.16 — Basel-level definitions of margin agreement, MPOR, and NICA.
src/rwa_calc/engine/ccr/rc.py— engine implementation ofcompute_rc_unmarginedandcompute_rc_margined.src/rwa_calc/data/schemas.py::NETTING_SET_SCHEMA— netting-set input columns (is_margined,margin_threshold,minimum_transfer_amount,nica).tests/unit/ccr/test_rc_margined.py— pinned numeric examples for the three margined arms plus the unmargined pass-through case.tests/fixtures/ccr/rc_margined_builder.py— fixture builder for NS_A / NS_B / NS_C / NS_D used by the worked examples above.- Maturity factor — companion page for the
Art. 285 MPOR cascade that feeds the margined
MF. - Adjusted notional — companion page for
the per-asset-class
dformulas that feedaddon_aggregate→PFE. - CCR index — full SA-CCR specification index and the per-stage status table.