SECTION 1: LEARNING OBJECTIVES
By the end of this lesson, you will be able to:
-
Define liquidity risk – funding liquidity risk and market liquidity risk.
-
Understand the key components – LCR, NSFR, and stress testing.
-
Apply liquidity risk measurement techniques.
-
Implement liquidity stress testing.
-
Measure liquidity risk using key metrics.
-
Understand the regulatory framework – Basel III, LCR, NSFR.
-
Develop a liquidity risk strategy for a digital bank.
SECTION 2: WHAT IS LIQUIDITY RISK?
2.1 Definition
Liquidity risk is the risk that a bank will not be able to meet its obligations as they fall due without incurring unacceptable losses. It has two components:
| Component | Description | Example |
|---|---|---|
| Funding Liquidity Risk | Inability to obtain sufficient funding. | Deposit withdrawals, inability to roll over funding. |
| Market Liquidity Risk | Inability to sell assets quickly without price concessions. | Fire sale of assets during a crisis. |
2.2 Liquidity Risk in Digital Banking
| Aspect | Digital Banking Exposure | Mitigation |
|---|---|---|
| Deposit Volatility | Digital deposits are more volatile. | Diversified funding sources. |
| Real-Time Payments | Faster outflows. | Liquidity buffers. |
| Digital Channels | Increased withdrawal speed. | Stress testing. |
| Market Access | Potential for rapid outflows. | Contingency funding plan. |
SECTION 3: LIQUIDITY RISK MEASUREMENT
3.1 Key Liquidity Ratios
| Ratio | Description | Formula | Target |
|---|---|---|---|
| Liquidity Coverage Ratio (LCR) | Short-term liquidity (30 days). | HQLA / Net Cash Outflows | > 100% |
| Net Stable Funding Ratio (NSFR) | Structural funding (1 year). | ASF / RSF | > 100% |
| Liquidity Gap | Mismatch between assets and liabilities. | Assets – Liabilities (by maturity) | Positive. |
| Concentration Ratio | Dependence on large depositors. | Top 10 deposits / Total deposits | < 20% |
3.2 LCR Components
| Component | Description | Examples |
|---|---|---|
| HQLA (High-Quality Liquid Assets) | Assets that can be easily liquidated. | Cash, government bonds. |
| Net Cash Outflows | Outflows – Inflows (capped at 75% of outflows). | Deposit withdrawals, funding maturities. |
3.3 NSFR Components
| Component | Description | Examples |
|---|---|---|
| ASF (Available Stable Funding) | Stable funding sources. | Equity, long-term debt, stable deposits. |
| RSF (Required Stable Funding) | Funding required by assets. | Loans, securities, illiquid assets. |
SECTION 4: LIQUIDITY STRESS TESTING
4.1 Stress Testing Framework
┌─────────────────────────────────────────────────────────────────────────────┐ │ LIQUIDITY STRESS TESTING │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ ┌──────────────────────────────────────────────────────────────────────┐ │ │ │ SCENARIO DEFINITION │ │ │ │ (Idiosyncratic, market-wide, combined) │ │ │ └──────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ v │ │ ┌──────────────────────────────────────────────────────────────────────┐ │ │ │ CASH FLOW PROJECTION │ │ │ │ (Inflows and outflows under stress) │ │ │ └──────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ v │ │ └──────────────────────────────────────────────────────────────────────┘ │ │ ┌──────────────────────────────────────────────────────────────────────┐ │ │ │ LIQUIDITY GAP ANALYSIS │ │ │ │ (Identify potential shortfalls) │ │ │ └──────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ v │ │ ┌──────────────────────────────────────────────────────────────────────┐ │ │ │ CONTINGENCY PLANNING │ │ │ │ (Contingency funding plan, mitigants) │ │ │ └──────────────────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘
4.2 Stress Scenarios
| Scenario | Description | Impact |
|---|---|---|
| Idiosyncratic | Bank-specific stress. | Loss of confidence, deposit withdrawals. |
| Market-Wide | Systemic stress. | Market disruption, asset price declines. |
| Combined | Bank-specific + market-wide. | Severe liquidity pressure. |
SECTION 5: REGULATORY FRAMEWORK
5.1 Key Regulations
| Regulation | Focus | Requirement |
|---|---|---|
| Basel III (LCR) | Short-term liquidity. | LCR > 100%. |
| Basel III (NSFR) | Structural funding. | NSFR > 100%. |
| EBA Guidelines | Liquidity risk management. | Stress testing, contingency plans. |
| PRA | UK liquidity requirements. | LCR, NSFR, stress testing. |
5.2 Regulatory Expectations
| Expectation | Description | Implementation |
|---|---|---|
| LCR Compliance | Maintain LCR > 100%. | HQLA buffer, monitoring. |
| NSFR Compliance | Maintain NSFR > 100%. | Stable funding. |
| Stress Testing | Regular liquidity stress testing. | Internal stress tests. |
| Contingency Funding Plan | Plan for liquidity crises. | CFP, action triggers. |
SECTION 6: IMPLEMENTATION IN PYTHON – LIQUIDITY RISK
# =================================================================== # MODULE 8, LESSON 5: LIQUIDITY RISK MANAGEMENT # =================================================================== import pandas as pd import numpy as np import matplotlib.pyplot as plt import seaborn as sns from datetime import datetime, timedelta import warnings warnings.filterwarnings('ignore') print("="*70) print("LIQUIDITY RISK MANAGEMENT IN DIGITAL BANKING") print("="*70) # ---------------------------------------------------------------- # PART A: BALANCE SHEET SIMULATION # ---------------------------------------------------------------- print("\n" + "-"*60) print("PART A: Balance Sheet Simulation") print("-"*60) # Simulate bank balance sheet assets = { 'Cash': 200, 'Government Bonds': 300, 'Corporate Bonds': 200, 'Loans': 800, 'Other Assets': 100 } total_assets = sum(assets.values()) liabilities = { 'Retail Deposits': 500, 'Wholesale Deposits': 300, 'Short-term Borrowings': 200, 'Long-term Debt': 300, 'Other Liabilities': 100 } total_liabilities = sum(liabilities.values()) equity = total_assets - total_liabilities balance_sheet = pd.DataFrame({ 'Item': list(assets.keys()) + list(liabilities.keys()) + ['Equity'], 'Category': ['Asset']*len(assets) + ['Liability']*len(liabilities) + ['Equity'], 'Amount': list(assets.values()) + list(liabilities.values()) + [equity] }) print("Balance Sheet Summary:") print(f"Total Assets: ${total_assets:,.0f}M") print(f"Total Liabilities: ${total_liabilities:,.0f}M") print(f"Equity: ${equity:,.0f}M") print(f"Equity Ratio: {equity/total_assets:.2%}") # ---------------------------------------------------------------- # PART B: LCR CALCULATION # ---------------------------------------------------------------- print("\n" + "-"*60) print("PART B: Liquidity Coverage Ratio (LCR)") print("-"*60) # HQLA calculation hqla_level1 = assets['Cash'] + assets['Government Bonds'] # Level 1 (0% haircut) hqla_level2a = assets['Corporate Bonds'] * 0.5 # Level 2A (20% haircut) # Cap Level 2 at 40% of total HQLA hqla_level2 = hqla_level2a hqla_cap = 0.4 * (hqla_level1 + hqla_level2) if hqla_level2 > hqla_cap: hqla_level2_adj = hqla_cap else: hqla_level2_adj = hqla_level2 hqla_total = hqla_level1 + hqla_level2_adj print("HQLA Components:") print(f" Level 1: ${hqla_level1:,.0f}M") print(f" Level 2 (capped): ${hqla_level2_adj:,.0f}M") print(f" Total HQLA: ${hqla_total:,.0f}M") # Cash outflows (30-day stress scenario) outflows = { 'Retail Deposits (stable)': liabilities['Retail Deposits'] * 0.05, 'Retail Deposits (unstable)': liabilities['Retail Deposits'] * 0.10 * 0.5, 'Wholesale Deposits': liabilities['Wholesale Deposits'] * 0.40, 'Short-term Borrowings': liabilities['Short-term Borrowings'] * 1.0, 'Undrawn Commitments': 20 } total_outflows = sum(outflows.values()) # Cash inflows inflows = { 'Loan Repayments': 30, 'Securities Maturities': 20, 'Other Inflows': 10 } total_inflows = sum(inflows.values()) # Net cash outflows (inflows capped at 75% of outflows) inflow_cap = 0.75 * total_outflows net_outflows = total_outflows - min(total_inflows, inflow_cap) lcr = hqla_total / net_outflows print("\n30-Day Net Cash Outflows:") print(f" Total Outflows: ${total_outflows:,.0f}M") print(f" Total Inflows: ${total_inflows:,.0f}M") print(f" Net Outflows (capped): ${net_outflows:,.0f}M") print(f"\nLiquidity Coverage Ratio (LCR): {lcr:.2f} ({lcr*100:.0f}%)") if lcr >= 1.0: print(" ✓ LCR meets regulatory requirement (≥ 100%)") else: print(" ⚠ LCR is below 100% – action required") # ---------------------------------------------------------------- # PART C: NSFR CALCULATION # ---------------------------------------------------------------- print("\n" + "-"*60) print("PART C: Net Stable Funding Ratio (NSFR)") print("-"*60) # Available Stable Funding (ASF) asf_factors = { 'Equity': 1.0, 'Long-term Debt': 1.0, 'Retail Deposits (stable)': 0.95, 'Retail Deposits (unstable)': 0.90, 'Wholesale Deposits': 0.50, 'Short-term Borrowings': 0.0, 'Other Liabilities': 0.0 } asf_amount = ( equity * asf_factors['Equity'] + liabilities['Long-term Debt'] * asf_factors['Long-term Debt'] + liabilities['Retail Deposits'] * 0.5 * asf_factors['Retail Deposits (stable)'] + liabilities['Retail Deposits'] * 0.5 * asf_factors['Retail Deposits (unstable)'] + liabilities['Wholesale Deposits'] * asf_factors['Wholesale Deposits'] ) print(f"Available Stable Funding (ASF): ${asf_amount:,.0f}M") # Required Stable Funding (RSF) rsf_factors = { 'Cash': 0.0, 'Government Bonds': 0.05, 'Corporate Bonds': 0.50, 'Loans': 0.65, 'Other Assets': 0.50 } rsf_amount = ( assets['Cash'] * rsf_factors['Cash'] + assets['Government Bonds'] * rsf_factors['Government Bonds'] + assets['Corporate Bonds'] * rsf_factors['Corporate Bonds'] + assets['Loans'] * rsf_factors['Loans'] + assets['Other Assets'] * rsf_factors['Other Assets'] ) print(f"Required Stable Funding (RSF): ${rsf_amount:,.0f}M") nsfr = asf_amount / rsf_amount print(f"\nNet Stable Funding Ratio (NSFR): {nsfr:.2f} ({nsfr*100:.0f}%)") if nsfr >= 1.0: print(" ✓ NSFR meets regulatory requirement (≥ 100%)") else: print(" ⚠ NSFR is below 100% – action required") # ---------------------------------------------------------------- # PART D: LIQUIDITY GAP ANALYSIS # ---------------------------------------------------------------- print("\n" + "-"*60) print("PART D: Liquidity Gap Analysis") print("-"*60) # Define maturity buckets maturity_buckets = ['< 1 month', '1-3 months', '3-6 months', '6-12 months', '> 1 year'] # Asset distribution by maturity asset_maturity = { 'Cash': [100, 0, 0, 0, 0], 'Government Bonds': [20, 30, 50, 100, 100], 'Corporate Bonds': [10, 20, 30, 40, 100], 'Loans': [20, 40, 80, 160, 500], 'Other Assets': [10, 10, 20, 20, 40] } # Liability distribution by maturity liability_maturity = { 'Retail Deposits': [100, 100, 100, 100, 100], 'Wholesale Deposits': [150, 100, 50, 0, 0], 'Short-term Borrowings': [200, 0, 0, 0, 0], 'Long-term Debt': [0, 0, 0, 50, 250], 'Other Liabilities': [20, 20, 20, 20, 20] } # Calculate liquidity gap asset_gap = pd.DataFrame(asset_maturity, index=maturity_buckets) liability_gap = pd.DataFrame(liability_maturity, index=maturity_buckets) liquidity_gap = asset_gap.sum(axis=1) - liability_gap.sum(axis=1) print("Liquidity Gap by Maturity Bucket ($M):") gap_df = pd.DataFrame({ 'Bucket': maturity_buckets, 'Assets': asset_gap.sum(axis=1).values, 'Liabilities': liability_gap.sum(axis=1).values, 'Gap': liquidity_gap.values }) print(gap_df.to_string(index=False)) # Visualise fig, ax = plt.subplots(figsize=(12, 6)) x = np.arange(len(maturity_buckets)) width = 0.35 ax.bar(x - width/2, asset_gap.sum(axis=1), width, label='Assets', color='green', alpha=0.7) ax.bar(x + width/2, liability_gap.sum(axis=1), width, label='Liabilities', color='red', alpha=0.7) ax.set_xlabel('Maturity Bucket') ax.set_ylabel('Amount ($M)') ax.set_title('Liquidity Gap: Assets vs Liabilities by Maturity') ax.set_xticks(x) ax.set_xticklabels(maturity_buckets) ax.legend() ax.grid(True, alpha=0.3) plt.tight_layout() plt.savefig('liquidity_gap.png', dpi=300, bbox_inches='tight') plt.show() print("Liquidity gap visualisation saved as 'liquidity_gap.png'") # ---------------------------------------------------------------- # PART E: LIQUIDITY STRESS TESTING # ---------------------------------------------------------------- print("\n" + "-"*60) print("PART E: Liquidity Stress Testing") print("-"*60) # Define stress scenarios stress_scenarios = { 'Baseline': {'deposit_runoff': 0.05, 'funding_rollover': 0.1, 'asset_sale': 0.0}, 'Moderate Stress': {'deposit_runoff': 0.15, 'funding_rollover': 0.3, 'asset_sale': 0.05}, 'Severe Stress': {'deposit_runoff': 0.30, 'funding_rollover': 0.6, 'asset_sale': 0.10}, 'Extreme Stress': {'deposit_runoff': 0.50, 'funding_rollover': 0.9, 'asset_sale': 0.20} } def liquidity_gap_stress(scenario, assets, liabilities, hqla): """Calculate liquidity gap under stress.""" runoff = scenario['deposit_runoff'] * liabilities['Retail Deposits'] wholesale_runoff = scenario['funding_rollover'] * liabilities['Wholesale Deposits'] short_term_runoff = scenario['funding_rollover'] * liabilities['Short-term Borrowings'] total_outflows = runoff + wholesale_runoff + short_term_runoff # Asset sales at discount asset_sale_need = max(0, total_outflows - hqla) if asset_sale_need > 0: sale_proceeds = asset_sale_need * (1 - scenario['asset_sale']) liquidity_gap = total_outflows - hqla - sale_proceeds else: liquidity_gap = total_outflows - hqla return liquidity_gap stress_results = [] for name, scenario in stress_scenarios.items(): gap = liquidity_gap_stress(scenario, assets, liabilities, hqla_total) stress_results.append({ 'Scenario': name, 'Liquidity Gap': gap, 'LCR': hqla_total / max(0.1, (gap + hqla_total)) }) stress_df = pd.DataFrame(stress_results) print("Liquidity Stress Test Results:") print(stress_df.to_string(index=False)) # Visualise fig, ax = plt.subplots(figsize=(10, 6)) x = np.arange(len(stress_df)) bars = ax.bar(x, stress_df['Liquidity Gap'], color=['green', 'yellow', 'orange', 'red'], alpha=0.7) ax.axhline(y=0, color='black', linestyle='-', alpha=0.5) ax.set_xticks(x) ax.set_xticklabels(stress_df['Scenario']) ax.set_ylabel('Liquidity Gap ($M)') ax.set_title('Liquidity Stress Testing – Gap under Scenarios') for bar, val in zip(bars, stress_df['Liquidity Gap']): ax.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 5, f'${val:.0f}M', ha='center', va='bottom') ax.grid(True, alpha=0.3) plt.tight_layout() plt.savefig('liquidity_stress.png', dpi=300, bbox_inches='tight') plt.show() print("Liquidity stress visualisation saved as 'liquidity_stress.png'") # ---------------------------------------------------------------- # PART F: LIQUIDITY METRICS DASHBOARD # ---------------------------------------------------------------- print("\n" + "-"*60) print("PART F: Liquidity Metrics Dashboard") print("-"*60) liquidity_metrics = pd.DataFrame({ 'Metric': [ 'Liquidity Coverage Ratio (LCR)', 'Net Stable Funding Ratio (NSFR)', 'Liquidity Gap (1-month)', 'Concentration Ratio', 'HQLA Buffer', 'Deposit Volatility', 'Funding Diversification', 'Stress Test Pass Rate' ], 'Current Value': [ f'{lcr*100:.0f}%', f'{nsfr*100:.0f}%', f'${liquidity_gap[0]:.0f}M', '18%', f'${hqla_total:.0f}M', '12%', '72%', '75%' ], 'Target Value': [ '> 100%', '> 100%', '> $0M', '< 20%', '> $500M', '< 10%', '> 80%', '> 90%' ], 'Status': ['🟢', '🟢', '🟢', '🟢', '🟢', '🟡', '🟡', '🟡'] }) print("Liquidity Metrics Dashboard:") print(liquidity_metrics.to_string(index=False)) # ---------------------------------------------------------------- # PART G: SUMMARY AND RECOMMENDATIONS # ---------------------------------------------------------------- print("\n" + "="*70) print("PART G: Summary and Recommendations") print("="*70) print(""" Liquidity Risk Management – Key Takeaways: 1. Liquidity risk includes funding and market liquidity risk. 2. LCR measures 30-day liquidity (HQLA / Net Cash Outflows). 3. NSFR measures 1-year structural funding (ASF / RSF). 4. Liquidity gap analysis identifies maturity mismatches. 5. Stress testing evaluates resilience under adverse scenarios. 6. Regulatory framework: Basel III (LCR, NSFR). 7. Key metrics: LCR, NSFR, liquidity gap, concentration ratio. Recommendations: - Maintain LCR > 100% and NSFR > 100%. - Conduct regular liquidity stress testing. - Diversify funding sources. - Maintain HQLA buffer. - Develop contingency funding plan. - Monitor liquidity metrics continuously. """) print("="*70) print("END OF LESSON 5 – MODULE 8") print("="*70)
SECTION 7: SUMMARY FOR THE DATA PRACTITIONER
-
Liquidity risk includes funding liquidity risk and market liquidity risk.
-
LCR measures short-term liquidity (30-day horizon) with HQLA / Net Cash Outflows > 100%.
-
NSFR measures structural funding (1-year horizon) with ASF / RSF > 100%.
-
Liquidity gap analysis identifies maturity mismatches between assets and liabilities.
-
Stress testing evaluates liquidity resilience under adverse scenarios (idiosyncratic, market-wide, combined).
-
Regulatory framework includes Basel III (LCR, NSFR) and EBA guidelines.
-
Key metrics include LCR, NSFR, liquidity gap, concentration ratio, HQLA buffer, and deposit volatility.
SECTION 8: RECOMMENDED NEXT STEPS
-
Maintain LCR > 100% and NSFR > 100%.
-
Conduct regular liquidity stress testing.
-
Diversify funding sources.
-
Maintain HQLA buffer.
-
Develop contingency funding plan.
-
Monitor liquidity metrics continuously.
-
Prepare for Lesson 6: Model Risk and AI Risk Management.
[END OF LESSON 5 – MODULE 8]