Skip to content

naenumtou/nmd_model

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

87 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Non-Maturity Deposit Models (NMD Models)

Python Pandas NumPy SciPy Matplotlib Seaborn MIT

A complete end-to-end implementation of Non-Maturity Deposit (NMD) Models for IRRBB Measurement, behavioral analysis, and Asset-Liability Management (ALM) — built in Python across 10 sequential Jupyter notebooks.

สอนการพัฒนาแบบจำลอง Non-Maturity Deposit Models (NMD Models) ตั้งแต่ต้นจนจบ

Overview

What is NMD and why does it matter?

Non-Maturity Deposits (NMD) are bank deposits with no contractual maturity date e.g., savings accounts, current accounts, and demand deposits. Customers can withdraw at any time, yet in practice most balances remain stable for years. This creates a fundamental challenge for banks:

  • Liquidity risk (ILAAP): If customers suddenly withdraw, the bank must have enough liquid assets to pay them.
  • Interest rate risk (IRRBB): The bank invests deposits at market rates but pays customers a lower "sticky" deposit rate. When interest rates change, both the value of investments and the cost of funding shift — but not at the same speed.

Under BCBS 368 (Interest Rate Risk in the Banking Book, April 2016), banks are required to model NMD Behavior explicitly, quantify how sensitive their economic value is to interest rate movements, and report this exposure to regulators. This repository implements the complete NMD Modeling framework from raw data to IRRBB Disclosure tables with consideration of ILAAP integrated.

Project Structure

The project is built as 10 sequential Jupyter notebooks (Notebook 01 - Notebook 10), each responsible for one modeling layer. Outputs from upstream notebooks flow as inputs into downstream ones via serialized model objects (.pkl).

Synthetic Data → Behavioral Models → Rate Models → Valuation → Hedging → IRRBB Report
     NB01            NB02–NB03        NB04–NB05    NB06–NB07  NB08–NB09     NB10

What makes this implementation distinctive

  • Fully reproducible — Every result can be traced back to Notebook 01's random seed.
  • BCBS 368 Compliant — EVE Discount method, regulatory caps, and scenario shocks follow the standard.
  • No OOP — All logic implemented as standalone functions with full docstrings.
  • Modular — Each notebook can be modified or extended independently.
nmd_model/
├── model/                                        #Trainned model and parameters (pkl.)
│   ├── hazard_rate.pkl
│   ├── ci_model.pkl
│   ├── hp_model.pkl
│   ├── gbm_model.pkl
│   ├── ddm_model.pkl
│   ├── ddy_model.pkl
│   ├── beta_model.pkl
│   ├── threshold_model.pkl
│   ├── jvd_model.pkl
│   ├── threshold_model.pkl
│   ├── runoff_model.pkl
│   ├── replicating_weights.pkl
│   ├── yield_curve.pkl
│   └── dynamics_model.pkl
├── notebooks/
│   ├── 01_data_generation.ipynb
│   ├── 02_survival_decay.ipynb
│   ├── 03_stable_nonstable.ipynb
│   ├── 04_deposit_rate_model.ipynb
│   ├── 05_deposit_decay.ipynb
│   ├── 06_economic_theory.ipynb
│   ├── 07_nmd_floor.ipynb
│   ├── 08_structural_hedge.ipynb
│   ├── 09_wealth_allocation.ipynb
│   └── 10_irrbb_integration.ipynb
├── src/
│   ├── data_generator.py
│   ├── survival_analysis.py
│   ├── stable_nonstable_model.py
│   ├── deposit_rate_model.py
│   ├── deposit_decay_model.py
│   ├── economic_theory.py
│   ├── nmd_floor.py
│   ├── caterpillar.py
│   ├── wealth_allocation.py
│   ├── reporting.py
│   └── plot_function.py
├── data/          
│   ├── processed
│   └── raw/
|   └── └── nmd_data.parquet
├── requirements.txt
└── README.md

Data Flow Architecture

Each notebook exports key outputs via pickle for downstream use. The diagram below shows the complete dependency chain.

NB01: Data Generation
  └─► nmd_data.parquet
        │
        ├─► NB02: Survival Decay
        │     └─► hazard_rate.pkl ──────────────────────────────► NB05
        │
        ├─► NB03: Stable/Non-Stable
        │     └─► stable_pct (CI method) ──────────────────────► NB05
        │
        ├─► NB04: Deposit Rate Model
        │     └─► γ=0.2072, α=0.0008, β₂=0.0797 ─────────────► NB05, NB06
        │
        ├─► NB05: Deposit Decay
        │     └─► core_balance=3,823 MB, WAL=2.85Y ───────────► NB06, NB08, NB09, NB10
        │     └─► IRRBB repricing buckets ─────────────────────► NB10
        │
        ├─► NB06: Economic Theory (EVE)
        │     └─► EVE=7,870 MB, ΔEVE=−1,062 MB ─────────────────► NB10
        │
        ├─► NB07: NMD Floor
        │     └─► Floor K=0%: 21 MB, K=d₀: 50 MB ──────────────► NB10
        │
        ├─► NB08: Structural Hedge
        │     └─► 5Y/5 tranches, NII=186 MB ────────────────────► NB09, NB10
        │
        └─► NB09: Wealth Allocation
              └─► w_cat=85%, NII=172 MB, LCR=173%, NSFR=135% ──► NB10
                  └─► Asset repricing profile ────────────────────► NB10

NB10: IRRBB Integration
  └─► 6 output tables:
        1. NMD Model Summary
        2. EVE Sensitivity Table
        3. NII Sensitivity Table
        4. Repricing Gap
        5. Net Repricing Gap (Asset − Liability)
        6. LCR / NSFR

Project Details

1. Synthetic Data Generation

สอนการพัฒนาแบบจำลอง Non-Maturity Deposit Models (NMD Models) ตั้งแต่ต้นจนจบ

Purpose: Generate 150 months of realistic NMD Data capturing the joint dynamics of market rate, deposit rate, deposit balance, and CDS spread. The real NMD Data is proprietary and varies significantly across institutions. Synthetic data allows full reproducibility and enables stress scenario testing by simply changing seed parameters.

The four variables are simulated jointly with realistic correlations

Variable Model
Market rate r_t AR(1) on changes
Deposit rate d_t Asymmetric error correction
Balance D_t Log-linear with macro drivers
CDS spread cs_t Log AR(1)

Output: data/raw/nmd_data.parquet — 150 rows × 4 columns

สอนการพัฒนาแบบจำลอง Non-Maturity Deposit Models (NMD Models) ตั้งแต่ต้นจนจบ

2. Survival Decay Model

สอนการพัฒนาแบบจำลอง Non-Maturity Deposit Models (NMD Models) ตั้งแต่ต้นจนจบ

Purpose: Measure how quickly deposit balances run off each month using cohort analysis. The aggregated balance series hides vintage effects. Accounts opened during a low rate environment behave differently from those opened during rate hikes. Cohort analysis separates these effects precisely.

Method — Discrete Hazard Rate:

  1. Build a balance matrix B(t, c) where t = observation month, c = cohort (origination month)
  2. Compute monthly runoff rate per cohort: RR(t, c) = 1 − B(t, c) / B(t−1, c)
  3. Average across all surviving cohorts at time t: h(t) = mean(RR(t, c))

Why not Cox Regression? Cox PH is designed for binary events (closed vs. open). NMD has partial runoff that balance declines gradually with no single exit event. Discrete hazard rates capture this continuous erosion.

Stressed runoff — two approaches:

Approach Method Use Case
Percentile P95 of h(t) ILAAP Base stress floor
MEV Regression h(t) = α + β₁·r_repo + β₂·unemployment + ε Macro scenario projection

Output:

  • hazard_rate.pkl — monthly hazard rates for first 24 months
  • Average base hazard ≈ 3.65% per month

สอนการพัฒนาแบบจำลอง Non-Maturity Deposit Models (NMD Models) ตั้งแต่ต้นจนจบ

3. Stable / Non-Stable Decomposition

สอนการพัฒนาแบบจำลอง Non-Maturity Deposit Models (NMD Models) ตั้งแต่ต้นจนจบ

Purpose: Decompose total balance into core and non-core (volatile) portions, subject to BCBS 368 regulatory caps. This decomposition matters because only the stable portion can be invested long term. Investing volatile balances in long duration assets creates liquidity risk.

3.1 Confidence Interval

OLS regression of balance on time trend, using the lower 95% CI as the core floor:

stable_pct = mean(lower_CI) / mean(balance)

3.2 Hodrick-Prescott

Decomposes balance into trend and cycle using λ = 1,600. The cycle component is non-core.

3.3 Geometric Brownian Motion

Simulates worst-case paths using historical log-return volatility. Balances below the P5 quantile are non-core.

3.4 Drawdowm Analysis

Maximum historical MoM or YoY decline percentage is treated as the non-corefraction.

BCBS 368 Regulatory Caps:

Segment Max Core % Max WAL
Retail Transactional 90% 5.0Y
Retail Non-transactional 70% 4.5Y
Wholesale 50% 4.0Y

Output: HP Method stable_pct = 96.36% on the synthetic data.

สอนการพัฒนาแบบจำลอง Non-Maturity Deposit Models (NMD Models) ตั้งแต่ต้นจนจบ

4. Deposit Rate Model

สอนการพัฒนาแบบจำลอง Non-Maturity Deposit Models (NMD Models) ตั้งแต่ต้นจนจบ

Purpose: Model how deposit rates respond to changes in market rates considering both in the short run and long run. The pass-through speed determines how quickly the bank's funding cost increases when rates rise. Slow pass-through means sticky deposit rates that wider spreads for longer where creating implicit duration risk.

4.1 Beta Regression (Long-run Pass-through)

d = α + β·r   →   β = 0.2072

A 100bps rise in market rates produces only a 20.72bps rise in deposit rates in the long run.

4.2 Threshold Model

Banks raise deposit rates slowly (λ⁺ = 0.15) but cut them quickly (λ⁻ = 0.45) — protecting margins on the way down. Also estimates bank spread α = 0.0008.

4.3 Jarrow Ven Deventer

Δd_t = β₀ + β₁·t + β₂·Δr_t   →   β₂ = 0.0797

Only ~7.97% of any rate change passes through to deposit rates in the same month.

Output:

Parameter Value Meaning
γ (gamma) 0.2072 Long-run pass-through
α (alpha) 0.0008 Bank spread
β₂ (beta_2) 0.0797 Short-term pass-through

สอนการพัฒนาแบบจำลอง Non-Maturity Deposit Models (NMD Models) ตั้งแต่ต้นจนจบ

5. Deposit Decay Model

สอนการพัฒนาแบบจำลอง Non-Maturity Deposit Models (NMD Models) ตั้งแต่ต้นจนจบ

Purpose: Build a forward 60-month(s) runoff portfolio profile of core deposits and producing by replicating portfolio for the IRRBB Repricing gap distribution.

Core Balance Formula:

Core Balance = Total Balance × stable_pct × (1 − β)
             = 5,039 MB × 96.36% × (1 − 0.2072) = 3,850 MB
  • stable_pct: Behavioural stability (does not exhibit volatile withdrawal patterns)
  • (1 − β): Repricing stickiness (does not reprice immediately when market rates change)

5.1 Historical Runoff Profile

Maximise  WAL(seed)
s.t.      WAL5.0Y            # BCBS 368 cap
          Core_remaining90%  # BCBS 368 cap

Solved by scipy.optimize.differential_evolution with tolerance 1e-8.

5.2 Replicating Portfolio

Minimise  std(X·w − margin − deposit_rate)
s.t.      Σwᵢ = 1,  WAL ≤ 5Y,  0 ≤ wᵢ ≤ 50%

Output: Weight Average Life (WAL) = 2.85 years

สอนการพัฒนาแบบจำลอง Non-Maturity Deposit Models (NMD Models) ตั้งแต่ต้นจนจบ

6. Economic Theory

สอนการพัฒนาแบบจำลอง Non-Maturity Deposit Models (NMD Models) ตั้งแต่ต้นจนจบ

สอนการพัฒนาแบบจำลอง Non-Maturity Deposit Models (NMD Models) ตั้งแต่ต้นจนจบ

6.1 Dynamics Model Simulation

6.2 Economic Value of Equity (EVE)

7. NMD Floor

สอนการพัฒนาแบบจำลอง Non-Maturity Deposit Models (NMD Models) ตั้งแต่ต้นจนจบ

สอนการพัฒนาแบบจำลอง Non-Maturity Deposit Models (NMD Models) ตั้งแต่ต้นจนจบ

8. Structual Hedge - Caterpillar

สอนการพัฒนาแบบจำลอง Non-Maturity Deposit Models (NMD Models) ตั้งแต่ต้นจนจบ

สอนการพัฒนาแบบจำลอง Non-Maturity Deposit Models (NMD Models) ตั้งแต่ต้นจนจบ

9. Wealth Allocation Model

สอนการพัฒนาแบบจำลอง Non-Maturity Deposit Models (NMD Models) ตั้งแต่ต้นจนจบ

สอนการพัฒนาแบบจำลอง Non-Maturity Deposit Models (NMD Models) ตั้งแต่ต้นจนจบ

10. IRRBB Reporting

สอนการพัฒนาแบบจำลอง Non-Maturity Deposit Models (NMD Models) ตั้งแต่ต้นจนจบ

สอนการพัฒนาแบบจำลอง Non-Maturity Deposit Models (NMD Models) ตั้งแต่ต้นจนจบ

License

MIT · Built for learning purposes

About

A complete guide of Non-Maturity Deposit model (NMD Models).

Topics

Resources

License

Stars

Watchers

Forks

Contributors