Skip to content

Synthetic swap book aggregator

ID MOD-165
System SD06
Repo bank-risk-platform
Build status Not started
Deployed No

Purpose

Maintains the bank's synthetic swap book — the internal treasury representation of the interest-rate exposure arising from all active fixed-rate components across all Flexible Loan Facilities (PRD-024). Aggregates component-level positions into standard maturity repricing buckets, assigns matched-tenor FTP rates, and computes daily EVE and NII sensitivity metrics under regulatory rate-shift scenarios. This is the module that connects the product-level fixed-rate component data (MOD-162) to the IRRBB capital and regulatory reporting frameworks.

Context

Every fixed-rate loan component is, from a treasury hedging perspective, a pay-fixed / receive-floating interest rate swap. When aggregated across the entire FLF book, these positions form a synthetic swap book: a ladder of known fixed cash flows by maturity bucket. RBNZ BS13 and APRA APS-117 both require banks to measure and capitalise this exposure. Without this module, the IRRBB gap would need to be computed manually from loan data, which is both error-prone and incompatible with the REP-004 AUTO posture.

The FTP dimension is equally important. Each fixed component is funded internally by treasury at the matched-tenor wholesale rate (from MOD-161). This isolates the lending business's net interest margin from rate movements — the lending business earns (contracted_rate - FTP_rate) regardless of what happens to market rates; treasury manages (FTP_rate - market_rate) risk across the aggregated book. Without FTP assignment at component level, P&L attribution between the lending business and treasury is impossible.

Data model

risk.synthetic_swap_positions

One row per active fixed-rate component position. Updated on component lifecycle events from MOD-162.

Column Type Notes
id uuid PK
component_id text NOT NULL — cross-system reference to credit.loan_facility_components.id
facility_id text NOT NULL — cross-system reference to credit.loan_facilities.id
customer_id text NOT NULL
jurisdiction char(2) NOT NULL CHECK ('NZ','AU')
notional_amount numeric(18,2) NOT NULL
currency char(3) NOT NULL
contracted_rate numeric(8,6) NOT NULL — the fixed rate locked at component establishment
ftp_rate numeric(8,6) NOT NULL — matched-tenor FTP rate from MOD-161 at establishment
ftp_tenor_months int NOT NULL — the tenor used for FTP lookup
market_rate_at_establishment numeric(8,6) NOT NULL — mid-market rate from MOD-085 at establishment (for break-cost basis reference)
start_date date NOT NULL
maturity_date date NOT NULL
maturity_bucket text NOT NULL CHECK ('ON','1M','3M','6M','1Y','2Y','3Y','5Y','7Y','10Y','15Y','15Y_PLUS')
status text NOT NULL CHECK ('ACTIVE','MATURED','PREPAID')
source_event_id text NOT NULL — EventBridge event_id of the originating component_created event
trace_id text NULL
created_at timestamptz NOT NULL DEFAULT now()
last_updated timestamptz NOT NULL DEFAULT now()

Index: (status, maturity_bucket, jurisdiction) for bucket aggregation queries. (component_id) UNIQUE for idempotent event processing.

maturity_bucket is assigned at position creation using the standard Basel repricing buckets: positions with maturity in ≤1 month go to 1M, ≤3 months to 3M, etc. The ON bucket is used for floating-rate positions (not applicable for fixed components, reserved for future use).

risk.irrbb_repricing_summary

Daily snapshot of the aggregated synthetic swap book. One row per snapshot_date × jurisdiction × maturity_bucket × scenario.

Column Type Notes
id uuid PK
snapshot_date date NOT NULL
jurisdiction char(2) NOT NULL CHECK ('NZ','AU','COMBINED')
maturity_bucket text NOT NULL
scenario text NOT NULL CHECK ('BASE','UP_100','UP_200','UP_300','DOWN_100','DOWN_200','DOWN_300')
total_notional numeric(18,2) NOT NULL
position_count int NOT NULL
weighted_avg_contracted_rate numeric(8,6) NOT NULL
weighted_avg_ftp_rate numeric(8,6) NOT NULL
nim_contribution_annual numeric(18,2) NOT NULL — (contracted_rate - ftp_rate) × total_notional
eve_impact numeric(18,2) NULL — populated for non-BASE scenarios; PV change under rate shift
nii_impact_12m numeric(18,2) NULL — populated for non-BASE scenarios; 12-month NII change
market_rate_used numeric(8,6) NULL — the shocked rate applied (BASE = mid-market; shifted = BASE ± bps)
created_at timestamptz NOT NULL DEFAULT now()

UNIQUE on (snapshot_date, jurisdiction, maturity_bucket, scenario).

Mutable for correction runs (if a position is subsequently found to have been missing from a snapshot, the snapshot can be recomputed). Snapshots older than 90 days are immutable; a CHECK constraint on the handler prevents writes to old snapshots outside of a designated correction window.

Handlers

consume-component-event — SQS consumer of bank.credit.component_created and bank.credit.component_status_changed from the bank-credit bus (cross-bus rule; see MOD-104 note below). On component_created for a FIXED component: calls MOD-161 for the matched-tenor FTP rate; determines the maturity bucket; inserts a row into risk.synthetic_swap_positions. On component_status_changed to MATURED or PREPAID: updates the position status. Idempotent on component_id UNIQUE constraint.

daily-irrbb-sweep — EB Scheduler cron, runs at 08:00 NZST daily (after MOD-162's 06:00 maturity sweep ensures terminal positions are closed). Reads all ACTIVE positions. For each of seven scenarios (BASE + six parallel rate shifts), computes bucket-level aggregates and EVE/NII sensitivities. Inserts snapshot rows into risk.irrbb_repricing_summary. Publishes bank.risk.irrbb_snapshot_updated on the bank-risk-platform bus.

EVE and NII sensitivity calculation

EVE impact for a parallel rate shift of Δr basis points applied to scenario S:

eve_impact_S = Σ_positions [ notional × (annuity_factor(r_base, remaining_months) − annuity_factor(r_base + Δr, remaining_months)) ]

Where annuity_factor(r, n) is the present value factor for a fixed annuity at rate r over n months. This is the standard interest rate risk duration-based approximation. A positive EVE impact means the bank's economic value increases under the scenario (rates rose, fixed-rate positions are now more valuable to the bank as receiver).

NII impact over a 12-month horizon for scenario S:

nii_impact_S = Σ_positions_repricing_within_12m [ notional × (r_base − (r_base + Δr)) × (remaining_months / 12) ]

For fixed-rate components repricing within 12 months (i.e. maturing within the horizon), the NII impact is the loss of contracted margin relative to the new market rate on rollover. For components with maturities beyond the horizon, the NII impact is zero (the fixed rate is locked through the period).

Both metrics are computed per jurisdiction (NZ, AU) and combined, consistent with RBNZ BS13 and APRA APS-117 reporting requirements.

FTP assignment

At position creation, the module calls MOD-161 with the component tenor in months and jurisdiction. MOD-161 returns the current matched-tenor FTP rate. This rate is locked on the position record — it does not change for the life of the component even as market rates move. The FTP rate represents the bank's internal cost of term funding for this component; locking it at establishment ensures the lending business's NIM is known and stable from day one.

The NIM contribution (contracted_rate - ftp_rate) per component is the lending business's reward for originating the loan. Treasury earns or loses (ftp_rate - current_market_rate) on the hedge. These two P&L streams are separate and managed independently.

Cross-bus consumption

MOD-165 consumes from the bank-credit EventBridge bus while residing in bank-risk-platform. Before deployment, file MOD-104-bank-credit-consumption-grant-mod165.handoff.md to bank-platform requesting: - BankRiskPlatformRole: events:PutRule + events:PutTargets on the bank-credit EventBridge bus ARN

This is the same pattern as MOD-024's cross-bus consumption of fraud alert events. The SQS queue and EventBridge rule are provisioned by MOD-165's SST config; they require the IAM grant to resolve.

Events published

Event Bus Trigger
bank.risk.irrbb_snapshot_updated bank-risk-platform Daily sweep completes

Consumers: MOD-033 (capital ratio engine — IRRBB RWA input), MOD-042 (CDC ingestion for Snowflake analytics), regulatory reporting modules.

Implementation notes

The maturity bucket assignment uses the component's maturity_date relative to today's date at the time the position is created. As time passes and remaining terms shorten, positions do not automatically migrate between buckets in the position table — the daily sweep recomputes bucket assignments dynamically from current maturity dates when building the repricing summary. The position table's maturity_bucket column reflects the bucket at origination only; the summary is always computed fresh.

For v1, only fixed-rate components from PRD-024 are tracked. The position table is designed to accommodate other fixed-rate products (future: fixed-rate term deposits, fixed-rate mortgages managed outside MOD-116) via a product_id column which should be added in v2 when additional products are brought into the synthetic book.

The floating-rate component of each FLF facility is excluded from the synthetic swap book by design. Floating-rate exposure is naturally hedged (the bank funds floating and receives floating); it creates no IRRBB gap and requires no internal FTP swap. The gap report should confirm zero contribution from floating components.


Module dependencies

Depends on

Module Title Required? Contract Reason
MOD-162 Loan facility & component manager Required Component lifecycle events (bank.credit.component_created, bank.credit.component_status_changed) from the loan facility component manager are the source of all synthetic swap position data; consumed cross-bus from the bank-credit EventBridge bus via a MOD-104 cross-bus consumption grant.
MOD-161 Transfer pricing Required Matched-tenor FTP rates are sourced from the transfer pricing rates module at the time each new fixed-rate component position is created, fixing the lending business's NIM for that component.
MOD-085 Market rates ingestion & normalisation Required Current mid-market swap rates for each maturity bucket are required for EVE and NII sensitivity calculations under rate-shift scenarios.
MOD-033 RWA & capital ratio engine Optional IRRBB repricing summaries and EVE/NII sensitivities are consumed by the capital ratio and RWA engine for IRRBB capital calculations; optional at deployment time as MOD-033 may not yet be built.
MOD-103 Neon database platform bootstrap Required Neon database and schema provisioned by MOD-103 must exist before this module can create risk.synthetic_swap_positions and risk.irrbb_repricing_summary.
MOD-104 AWS shared infrastructure bootstrap Required AWS shared infrastructure provisioned by MOD-104 (EventBridge buses, cross-bus IAM grants, SSM, KMS, Lambda execution role) is required; BankRiskPlatformRole needs events:PutRule and events:PutTargets on the bank-credit bus — file MOD-104-bank-credit-consumption-grant-mod165.handoff.md before building.

Required by

(No modules in this wiki currently declare a dependency on this module.)


Policies satisfied

Policy Title Mode How
CLQ-001 Capital Adequacy Policy CALC IRRBB repricing gap and EVE/NII sensitivity metrics produced by this module feed the capital calculation engine (MOD-033) for the interest-rate risk in the banking book capital requirement; the daily snapshot is the authoritative input for IRRBB RWA contribution.
REP-002 Prudential Reporting Policy CALC Component-level fixed cash flows and maturity-bucketed repricing summaries are computed and stored automatically after each daily sweep, forming the data source for RBNZ BS13 and APRA APS-117 regulatory IRRBB returns.
REP-004 Financial Statements Policy AUTO Daily IRRBB snapshots — repricing buckets, EVE sensitivities, and NII sensitivities — are written to the reporting store without manual intervention; no manual journal or analyst-prepared spreadsheet is required for the IRRBB position.

Capabilities satisfied

(No capabilities mapped)


Part of SD06 — Snowflake Analytics & Risk Platform Compiled 2026-05-22 from source/entities/modules/MOD-165.yaml