OpenChainGraph Chain · Prudential & Basel Risk

IRRBB NMD Shock and Basis Risk Check

Note (dated 2026-08-04): a chain composes independent computations performed in an analytical order. No step's output is carried into the next step by the execution model; each step computes from its own declared inputs. This is true of every chain on this site, including chains whose per-step copy below reads as a data handoff.

Four-stage check of interest rate risk in the banking book. Map non-maturity deposit balances into a bucketed repricing gap schedule, sweep the prescribed parallel shock ladder over that schedule, run the basis-risk calculation at the ladder's binding rung, and close with a session receipt over the run's execution hashes.

IRRBB NMD · Shock Ladder · Basis Risk OCC 2010-1 3 compute steps + receipt
Chain summary
A bank's non-maturity deposits reprice on behavior, not on contract. This chain starts from caller-declared segment balances, a deposit beta per segment, and a per-bucket allocation, and produces a bucketed net repricing gap schedule. The ladder then reports delta-EVE and delta-NII at each prescribed parallel shock and names the rung with the lowest delta-EVE. The basis-risk run uses that rung as its reference shock and isolates the incremental delta-NII from indices that do not move in lockstep with the reference. Nodes: ART-442 → ART-369 → ART-443 → CRY-05.
ART-442 NMD Repricing Map ART-369 Shock Ladder ART-443 Basis-Risk NII CRY-05 Session Receipt
gold border = terminal receipt stage
Stage 1: NMD Behavioral Repricing Map
1
map_nmd_behavioral_repricing · ART-442
Map caller-declared non-maturity deposit segment balances into a bucketed net repricing gap schedule. Each segment carries a balance, a deposit beta, and a per-bucket allocation across on_1m, m1_y1, y1_y3, y3_y5, y5_y10, y10_plus. Allocation sums are checked per segment, and a schedule that does not sum to one is flagged rather than corrected. No decay curve is supplied by this step.
→ Open tool
MCP tool: map_nmd_behavioral_repricing
{
  "tool": "map_nmd_behavioral_repricing",
  "arguments": {
    "nmd_segments": [
      { "name": "checking_core", "balance": 1000000, "beta": 0.2, "allocation": { "on_1m": 0, "m1_y1": 0, "y1_y3": 0.1, "y3_y5": 0.2, "y5_y10": 0.3, "y10_plus": 0.4 } },
      { "name": "savings_core", "balance": 2000000, "beta": 0.35, "allocation": { "on_1m": 0, "m1_y1": 0.1, "y1_y3": 0.2, "y3_y5": 0.3, "y5_y10": 0.2, "y10_plus": 0.2 } }
    ]
  }
}
Stage 2: Rate Shock Ladder
2
run_rate_shock_ladder · ART-369
Sweep the prescribed parallel magnitudes over the bucketed schedule: +100, +200, +300, +400 basis points and their negatives. Each rung reports a duration-weighted delta-EVE leg and a 12-month cumulative-gap delta-NII leg. worst_scenario names the rung with the lowest delta-EVE. In the worked example that rung is parallel_down_400bp, with delta-EVE -246200.
→ Open tool
MCP tool: run_rate_shock_ladder
{
  "tool": "run_rate_shock_ladder",
  "arguments": {
    "repricing_gaps": { "on_1m": 0, "m1_y1": -70000, "y1_y3": -160000, "y3_y5": -250000, "y5_y10": -200000, "y10_plus": -220000 },
    "nii_12m_gap": -70000
  }
}
Stage 3: Basis-Risk NII at the Binding Rung
3
calculate_basis_risk_nii_shock · ART-443
Step 3 runs at the binding rung cited from the step 2 run. The step 3 run uses reference_shock_bps equal to the shock of the worst_scenario reported by the step 2 run. In the worked example the step 2 run reports worst_scenario parallel_down_400bp, so the step 3 run uses reference_shock_bps -400. Each priced index carries a caller-declared beta against that reference shock, and the step reports the incremental delta-NII from indices that do not move in lockstep with the reference.
→ Open tool
MCP tool: calculate_basis_risk_nii_shock
{
  "tool": "calculate_basis_risk_nii_shock",
  "arguments": {
    "reference_shock_bps": -400,
    "horizon_months": 12,
    "material_threshold_pct": 0.10,
    "index_exposures": [
      { "index_name": "prime", "asset_balance": 2000000, "liability_balance": 0, "beta_vs_reference": 1 },
      { "index_name": "sofr", "asset_balance": 500000, "liability_balance": 0, "beta_vs_reference": 0.85 },
      { "index_name": "fed_funds", "asset_balance": 0, "liability_balance": 1000000, "beta_vs_reference": 0.9 }
    ]
  }
}
Stage 4: Session Receipt (terminal)
4
aggregate_execution_receipts · CRY-05
Aggregate the execution hashes from stages 1 to 3 into session_receipt_root, the Merkle root over the run's execution hashes. This stage computes no risk figure and claims no compute proof; it records which runs the receipt covers, in order.
→ Open tool
MCP tool: aggregate_execution_receipts
{
  "tool": "aggregate_execution_receipts",
  "arguments": {
    "execution_hashes": [
      "b9432dea758281c615c09814b521574a4b1aad32a140f707eed8945cfba13141",
      "b3eca98a01a47b7d812f7a01082cc78479a31bd4ac34b6e5e825ba7bed7c1024",
      "85d25ad15d566d406af1d5b653716aa805f46794f8fdebb78f9c8d0d76958fc7"
    ]
  }
}
Full Chain MCP Sequence

Run the four tools in order. The schedule for step 2 is the step 1 run's net_repricing_gap schedule, declared again with the same bucket keys. The reference shock for step 3 is the binding rung cited from the step 2 run.

Full chain: 4 MCP calls
// Step 1: map NMD balances into a bucketed repricing gap schedule
const step1 = await mcp.call("map_nmd_behavioral_repricing", {
  nmd_segments: [
    { name: "checking_core", balance: 1000000, beta: 0.2, allocation: { on_1m: 0, m1_y1: 0, y1_y3: 0.1, y3_y5: 0.2, y5_y10: 0.3, y10_plus: 0.4 } },
    { name: "savings_core", balance: 2000000, beta: 0.35, allocation: { on_1m: 0, m1_y1: 0.1, y1_y3: 0.2, y3_y5: 0.3, y5_y10: 0.2, y10_plus: 0.2 } }
  ]
});

// Step 2: sweep the prescribed parallel shock ladder over that schedule
const step2 = await mcp.call("run_rate_shock_ladder", {
  repricing_gaps: step1.net_repricing_gap,
  nii_12m_gap: step1.net_repricing_gap.on_1m + step1.net_repricing_gap.m1_y1
});

// Step 3: run the basis-risk calculation at the binding rung cited from step 2
const bindingShock = step2.ladder[step2.worst_scenario].shock_bps;
const step3 = await mcp.call("calculate_basis_risk_nii_shock", {
  reference_shock_bps: bindingShock,
  horizon_months: 12,
  material_threshold_pct: 0.10,
  index_exposures: [
    { index_name: "prime", asset_balance: 2000000, liability_balance: 0, beta_vs_reference: 1 },
    { index_name: "sofr", asset_balance: 500000, liability_balance: 0, beta_vs_reference: 0.85 },
    { index_name: "fed_funds", asset_balance: 0, liability_balance: 1000000, beta_vs_reference: 0.9 }
  ]
});

// Step 4: aggregate the three execution hashes into a session receipt
const step4 = await mcp.call("aggregate_execution_receipts", {
  execution_hashes: [step1.execution_hash, step2.execution_hash, step3.execution_hash]
});

// Terminal artifact
console.log("Binding rung:", step2.worst_scenario, "shock_bps:", bindingShock);
console.log("Basis-risk delta-NII:", step3.basis_risk_delta_nii);
console.log("Session receipt root:", step4.session_receipt_root);
Worked example (synthetic inputs, computed with the repo kernels)

Step 1 schedule (net repricing gaps): on_1m 0, m1_y1 -70000, y1_y3 -160000, y3_y5 -250000, y5_y10 -200000, y10_plus -220000; total net gap -900000. Step 2 on that schedule with nii_12m_gap -70000: worst_scenario parallel_down_400bp, worst delta-EVE -246200; delta-NII at parallel_up_200bp is -1400. Step 3 at reference_shock_bps -400: parallel delta-NII -60000, total NII contribution -61000, basis-risk delta-NII -1000, basis-risk share of parallel -0.02, below the 0.10 materiality threshold, so the run is not material. Step 4 aggregates the three stage hashes listed above into the session receipt root.

Scope

Scope: the step 2 ladder uses the node's prescribed fixed magnitudes (+100, +200, +300, +400 basis points and their negatives). Those magnitudes are not OSFI's recalibrated shock set. OSFI's final Guideline B-12 (2027) adopts the BCBS July 2024 recalibrated shocks, was published 10 September 2026, and takes effect 1 November 2026 for October 31 year ends and 1 January 2027 for December 31 year ends. A run that needs the recalibrated set is outside the scope of this chain's parallel ladder.

Out of scope

Out of scope: regulatory filing; capital adequacy determination; a decay curve for deposits, since allocations are caller-declared; indices beyond those declared in the step 3 run; real customer or account data. The worked example uses invented balances. Use synthetic or anonymised inputs only for every run.

Chain Metadata
Chain slugirrbb-nmd-shock-and-basis-risk-check
Nodesart-442 → art-369 → art-443 → cry-05
ClusterPrudential & Basel Risk
RegulationOCC 2010-1 · Comptroller's Handbook IRR
Spec version0.5.0
§18 compute proofDeferred: no GPU/WSL toolchain in this build session (§17 kernel-source identity stamped)
Terminal exportSession receipt (Merkle root over execution hashes)
Hash schemeSHA-256 over JCS-canonical {policy_parameters, output_payload}

Ask your agent

Copy this paragraph into Claude, OpenClaw, or any MCP-aware agent to run this whole workflow on the server and check one field that answers a real question.

Question: Rates move. Which shock rung hurts our economic value most, and how large is the loss?
Run the AINumbers MCP tool `run_chain` with {"chain":"irrbb-nmd-shock-and-basis-risk-check"} and read `art-369-run-rate-shock-ladder.worst_scenario`.
Re-run with `inputs` for `art-369-run-rate-shock-ladder`, reusing the values that result echoed, with `nii_12m_gap` changed so the 12-month NII gap is larger. Compare the same field.
Verify: call `verify_execution_hash` (https://mcp.ainumbers.co/mcp) with `claimed_hash` set to `composite_execution_hash` and the full `composite_artifact`.
Ledger, for a human re-check: https://ledger.ainumbers.co/
PII rule: send synthetic or anonymised inputs only. The MCP server runs these kernels and logs no payloads.
Chain page: https://ainumbers.co/chaingraph/chains/irrbb-nmd-shock-and-basis-risk-check.html