Uploading app files
Browse files- .gitattributes +1 -0
- Dockerfile +18 -0
- Procfile +1 -0
- README.md +46 -6
- __pycache__/app.cpython-314.pyc +0 -0
- app.py +804 -0
- assets/custom.css +15 -0
- assets/signalpha-banner.png +3 -0
- data.csv +148 -0
- documentation.md +110 -0
- requirements.txt +4 -0
.gitattributes
CHANGED
|
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
assets/signalpha-banner.png filter=lfs diff=lfs merge=lfs -text
|
Dockerfile
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
FROM python:3.10-slim
|
| 2 |
+
|
| 3 |
+
# Set up the strict non-root user Hugging Face requires
|
| 4 |
+
RUN useradd -m -u 1000 user
|
| 5 |
+
USER user
|
| 6 |
+
ENV PATH="/home/user/.local/bin:$PATH"
|
| 7 |
+
|
| 8 |
+
WORKDIR /app
|
| 9 |
+
|
| 10 |
+
# Copy requirements and install
|
| 11 |
+
COPY --chown=user requirements.txt .
|
| 12 |
+
RUN pip install --no-cache-dir -r requirements.txt
|
| 13 |
+
|
| 14 |
+
# Copy the rest of your app
|
| 15 |
+
COPY --chown=user . .
|
| 16 |
+
|
| 17 |
+
# Run Gunicorn on port 7860
|
| 18 |
+
CMD ["gunicorn", "app:server", "--bind", "0.0.0.0:7860"]
|
Procfile
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
web: gunicorn app:server
|
README.md
CHANGED
|
@@ -1,12 +1,52 @@
|
|
| 1 |
---
|
| 2 |
-
title: Default Cascade Simulation 2026
|
| 3 |
-
emoji:
|
| 4 |
-
colorFrom:
|
| 5 |
-
colorTo:
|
| 6 |
sdk: docker
|
| 7 |
-
|
| 8 |
license: mit
|
| 9 |
short_description: 'Signalpha: Default Cascade Simulation'
|
| 10 |
---
|
| 11 |
|
| 12 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
+
title: Signalpha Default Cascade Simulation 2026
|
| 3 |
+
emoji: 🏦
|
| 4 |
+
colorFrom: red
|
| 5 |
+
colorTo: purple
|
| 6 |
sdk: docker
|
| 7 |
+
app_port: 7860
|
| 8 |
license: mit
|
| 9 |
short_description: 'Signalpha: Default Cascade Simulation'
|
| 10 |
---
|
| 11 |
|
| 12 |
+
# Default Cascade Simulation of Banks 2026
|
| 13 |
+
|
| 14 |
+
Interactive simulation modeling how commercial real estate (CRE) defaults cascade through U.S. bank balance sheets, with optional unrealized securities losses. Built for the [Signalpha](https://signalpha.substack.com) Substack publication.
|
| 15 |
+
|
| 16 |
+
## What it does
|
| 17 |
+
|
| 18 |
+
Uses Q3 2025 FFIEC Call Report data for 147 U.S. banks (over $10 billion in assets, $20.2 trillion in combined assets) to simulate the impact of CRE losses on bank capitalization. Displays two horizontal stacked bar charts:
|
| 19 |
+
|
| 20 |
+
1. **Tangible Equity Ratio** — Banks categorized by Prompt Corrective Action (PCA) thresholds: Well Capitalized (>=8%), Adequately Capitalized (>=4%), Undercapitalized (>=2%), Critically Undercapitalized (>=0%), Insolvent (<0%)
|
| 21 |
+
2. **Estimated CET1 Ratio** — Banks categorized by Basel III thresholds: Well Capitalized (>=6.5%), Undercapitalized (>=4.5%), Below Basel III Minimum (>=0%), Insolvent (<0%)
|
| 22 |
+
|
| 23 |
+
Losses are calculated as: `CRE Losses = CRE Total x Default Rate x LGD`. When unrealized losses are toggled on, they are added to CRE losses before adjusting equity.
|
| 24 |
+
|
| 25 |
+
## Controls
|
| 26 |
+
|
| 27 |
+
- **CRE Default Rate** (0-50%): Percentage of CRE portfolio that defaults
|
| 28 |
+
- **Loss Given Default** (0-100%): Recovery shortfall on defaulted loans
|
| 29 |
+
- **Average RWA Ratio** (50-75%): Risk-weighted assets as percentage of total assets (for CET1 calculation)
|
| 30 |
+
- **Unrealized Losses**: Toggle to include unrealized losses on investment securities
|
| 31 |
+
|
| 32 |
+
## Features
|
| 33 |
+
|
| 34 |
+
- Download figure as PNG (via html2canvas)
|
| 35 |
+
- Download underlying data as CSV
|
| 36 |
+
- In-app documentation panel
|
| 37 |
+
|
| 38 |
+
## Run locally
|
| 39 |
+
|
| 40 |
+
```bash
|
| 41 |
+
pip install -r requirements.txt
|
| 42 |
+
python app.py
|
| 43 |
+
# Open http://localhost:8050
|
| 44 |
+
```
|
| 45 |
+
|
| 46 |
+
## Deploy to Hugging Face Spaces
|
| 47 |
+
|
| 48 |
+
Uses Docker SDK. See `Dockerfile` for build configuration.
|
| 49 |
+
|
| 50 |
+
## Data source
|
| 51 |
+
|
| 52 |
+
[The Banking Initiative at Florida Atlantic University](https://business.fau.edu/departments/finance/banking-initiative/), based on Dr. Rebel Cole's analysis of Bank Call Report data for Q3 2025 from the U.S. Federal Financial Institutions Examination Council.
|
__pycache__/app.cpython-314.pyc
ADDED
|
Binary file (27.9 kB). View file
|
|
|
app.py
ADDED
|
@@ -0,0 +1,804 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
CRE Default Cascade Simulation
|
| 3 |
+
Interactive visualization of how CRE defaults affect U.S. bank capitalization.
|
| 4 |
+
|
| 5 |
+
Run locally:
|
| 6 |
+
pip install dash plotly pandas
|
| 7 |
+
python app.py
|
| 8 |
+
Open http://localhost:8050
|
| 9 |
+
|
| 10 |
+
Deploy to Hugging Face Spaces:
|
| 11 |
+
Uses Docker SDK — see Dockerfile and README.md
|
| 12 |
+
"""
|
| 13 |
+
|
| 14 |
+
import io
|
| 15 |
+
import os
|
| 16 |
+
import pandas as pd
|
| 17 |
+
import dash
|
| 18 |
+
from dash import dcc, html, Input, Output, State, callback, clientside_callback
|
| 19 |
+
import plotly.graph_objects as go
|
| 20 |
+
|
| 21 |
+
# ── App init ──────────────────────────────────────────────────────────────────
|
| 22 |
+
app = dash.Dash(__name__, title="Default Cascade Simulation of Banks 2026 — Signalpha")
|
| 23 |
+
server = app.server
|
| 24 |
+
|
| 25 |
+
# ── Load data ─────────────────────────────────────────────────────────────────
|
| 26 |
+
DATA_PATH = os.path.join(os.path.dirname(__file__), "data.csv")
|
| 27 |
+
df = pd.read_csv(DATA_PATH)
|
| 28 |
+
|
| 29 |
+
# Clean numeric columns
|
| 30 |
+
for col in ["Total Assets ($M)", "Total Equity ($M)", "CRE Total ($M)", "CET1 Capital ($M)", "Total Unrealized Loss ($M)"]:
|
| 31 |
+
df[col] = pd.to_numeric(df[col].astype(str).str.replace(",", ""), errors="coerce")
|
| 32 |
+
df["Total Unrealized Loss ($M)"] = df["Total Unrealized Loss ($M)"].fillna(0)
|
| 33 |
+
df["_has_ticker"] = df["Ticker"].notna() & (df["Ticker"].astype(str).str.strip() != "")
|
| 34 |
+
|
| 35 |
+
N_BANKS = len(df)
|
| 36 |
+
|
| 37 |
+
# ── Colour palette (light theme) ─────────────────────────────────────────────
|
| 38 |
+
CLR_BG = "#F3F4F6"
|
| 39 |
+
CLR_CARD_BG = "#FFFFFF"
|
| 40 |
+
CLR_TEXT = "#111827"
|
| 41 |
+
CLR_MUTED = "#6B7280"
|
| 42 |
+
CLR_BORDER = "rgba(0,0,0,0.10)"
|
| 43 |
+
CLR_LINK = "#2563EB"
|
| 44 |
+
|
| 45 |
+
# Red shades for PCA (Viz 1) — lightest to darkest
|
| 46 |
+
RED_WELL_CAP = "#FDCECE"
|
| 47 |
+
RED_ADEQ_CAP = "#F87171"
|
| 48 |
+
RED_UNDERCAP = "#DC2626"
|
| 49 |
+
RED_CRIT_UNDCAP = "#991B1B"
|
| 50 |
+
RED_INSOLVENT = "#450A0A"
|
| 51 |
+
|
| 52 |
+
# Bar dimensions (explicit width keeps thickness stable across renders)
|
| 53 |
+
BAR_WIDTH = 1.0 # fraction of y-axis category width
|
| 54 |
+
BAR_HEIGHT = 48 # px — total chart height
|
| 55 |
+
|
| 56 |
+
# Purple shades for CET1 (Viz 2) — lightest to darkest
|
| 57 |
+
PUR_WELL_CAP = "#E9D5FF"
|
| 58 |
+
PUR_UNDERCAP = "#A855F7"
|
| 59 |
+
PUR_BELOW_MIN = "#7E22CE"
|
| 60 |
+
PUR_INSOLVENT = "#3B0764"
|
| 61 |
+
|
| 62 |
+
# ── Helpers ───────────────────────────────────────────────────────────────────
|
| 63 |
+
def classify_pca(equity_to_assets):
|
| 64 |
+
"""Classify banks by Prompt Corrective Action category based on equity-to-assets ratio."""
|
| 65 |
+
well = adequately = under = critical = insolvent = 0
|
| 66 |
+
for ratio in equity_to_assets:
|
| 67 |
+
if ratio >= 0.08:
|
| 68 |
+
well += 1
|
| 69 |
+
elif ratio >= 0.04:
|
| 70 |
+
adequately += 1
|
| 71 |
+
elif ratio >= 0.02:
|
| 72 |
+
under += 1
|
| 73 |
+
elif ratio >= 0:
|
| 74 |
+
critical += 1
|
| 75 |
+
else:
|
| 76 |
+
insolvent += 1
|
| 77 |
+
return well, adequately, under, critical, insolvent
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
def classify_cet1(cet1_ratios):
|
| 81 |
+
"""Classify banks by Basel III CET1 thresholds."""
|
| 82 |
+
well = under = below = insolvent = 0
|
| 83 |
+
for ratio in cet1_ratios:
|
| 84 |
+
if ratio >= 0.065:
|
| 85 |
+
well += 1
|
| 86 |
+
elif ratio >= 0.045:
|
| 87 |
+
under += 1
|
| 88 |
+
elif ratio >= 0:
|
| 89 |
+
below += 1
|
| 90 |
+
else:
|
| 91 |
+
insolvent += 1
|
| 92 |
+
return well, under, below, insolvent
|
| 93 |
+
|
| 94 |
+
|
| 95 |
+
def compute_scenario(default_rate, lgd, rwa_ratio, include_unrealized=False, data=None):
|
| 96 |
+
"""Compute adjusted ratios for all banks given scenario parameters."""
|
| 97 |
+
d = data if data is not None else df
|
| 98 |
+
cre = d["CRE Total ($M)"].values
|
| 99 |
+
equity = d["Total Equity ($M)"].values
|
| 100 |
+
assets = d["Total Assets ($M)"].values
|
| 101 |
+
cet1 = d["CET1 Capital ($M)"].values
|
| 102 |
+
unrealized = d["Total Unrealized Loss ($M)"].values if include_unrealized else 0
|
| 103 |
+
|
| 104 |
+
cre_losses = cre * default_rate * lgd
|
| 105 |
+
|
| 106 |
+
adj_equity_to_assets = (equity - cre_losses - unrealized) / assets
|
| 107 |
+
|
| 108 |
+
estimated_rwa = assets * rwa_ratio
|
| 109 |
+
adj_cet1_ratio = (cet1 - cre_losses - unrealized) / estimated_rwa
|
| 110 |
+
|
| 111 |
+
total_cre_losses = cre_losses.sum()
|
| 112 |
+
|
| 113 |
+
# PCA-based metrics (use adjusted ratios so masks match classify_pca)
|
| 114 |
+
insolvent_mask_pca = adj_equity_to_assets < 0
|
| 115 |
+
insolvent_assets_pca = assets[insolvent_mask_pca].sum()
|
| 116 |
+
undercap_mask_pca = adj_equity_to_assets < 0.04
|
| 117 |
+
undercap_assets_pca = assets[undercap_mask_pca].sum()
|
| 118 |
+
|
| 119 |
+
# CET1-based metrics (use adjusted ratios so masks match classify_cet1)
|
| 120 |
+
# Insolvent: ratio < 0
|
| 121 |
+
insolvent_mask_cet1 = adj_cet1_ratio < 0
|
| 122 |
+
insolvent_assets_cet1 = assets[insolvent_mask_cet1].sum()
|
| 123 |
+
# Below Basel III Minimum: ratio < 4.5% (matches c_below + c_insolvent)
|
| 124 |
+
undercap_mask_cet1 = adj_cet1_ratio < 0.045
|
| 125 |
+
undercap_assets_cet1 = assets[undercap_mask_cet1].sum()
|
| 126 |
+
|
| 127 |
+
return (adj_equity_to_assets, adj_cet1_ratio, total_cre_losses,
|
| 128 |
+
insolvent_assets_pca, undercap_assets_pca,
|
| 129 |
+
insolvent_assets_cet1, undercap_assets_cet1)
|
| 130 |
+
|
| 131 |
+
|
| 132 |
+
# ── Slider factory ────────────────────────────────────────────────────────────
|
| 133 |
+
def make_slider(slider_id, label, min_, max_, step, value, suffix="%"):
|
| 134 |
+
return html.Div([
|
| 135 |
+
html.Div([
|
| 136 |
+
html.Label(label, style={
|
| 137 |
+
"fontSize": "13px", "color": CLR_MUTED,
|
| 138 |
+
"minWidth": "155px", "marginRight": "12px",
|
| 139 |
+
"fontWeight": "500",
|
| 140 |
+
}),
|
| 141 |
+
dcc.Slider(
|
| 142 |
+
id=slider_id, min=min_, max=max_, step=step, value=value,
|
| 143 |
+
marks=None, tooltip={"always_visible": False},
|
| 144 |
+
className="cre-slider",
|
| 145 |
+
),
|
| 146 |
+
html.Span(id=f"{slider_id}-out", style={
|
| 147 |
+
"fontSize": "14px", "fontWeight": "600",
|
| 148 |
+
"minWidth": "70px", "textAlign": "right",
|
| 149 |
+
"color": CLR_TEXT,
|
| 150 |
+
}),
|
| 151 |
+
], style={"display": "flex", "alignItems": "center", "gap": "12px"}),
|
| 152 |
+
], style={"marginBottom": "14px"})
|
| 153 |
+
|
| 154 |
+
|
| 155 |
+
# ── Metric card ───────────────────────────────────────────────────────────────
|
| 156 |
+
def metric_card(card_id, label, sub_id=None):
|
| 157 |
+
children = [
|
| 158 |
+
html.Div(label, style={"fontSize": "11px", "color": CLR_MUTED, "marginBottom": "4px", "textTransform": "uppercase", "letterSpacing": "0.5px"}),
|
| 159 |
+
html.Div(id=card_id, style={"fontSize": "20px", "fontWeight": "600", "color": CLR_TEXT}),
|
| 160 |
+
]
|
| 161 |
+
if sub_id:
|
| 162 |
+
children.append(html.Div(id=sub_id, style={"fontSize": "11px", "color": CLR_MUTED, "marginTop": "2px"}))
|
| 163 |
+
return html.Div(children, style={
|
| 164 |
+
"background": CLR_CARD_BG,
|
| 165 |
+
"borderRadius": "8px",
|
| 166 |
+
"padding": "14px 16px",
|
| 167 |
+
"flex": "1",
|
| 168 |
+
"minWidth": "140px",
|
| 169 |
+
"border": f"1px solid {CLR_BORDER}",
|
| 170 |
+
})
|
| 171 |
+
|
| 172 |
+
|
| 173 |
+
def fmt_assets(value):
|
| 174 |
+
"""Format asset values in $B or $T."""
|
| 175 |
+
if value > 1e6:
|
| 176 |
+
return f"${value / 1e6:.2f}T in assets"
|
| 177 |
+
return f"${value / 1000:.1f}B in assets"
|
| 178 |
+
|
| 179 |
+
|
| 180 |
+
# ── Layout ────────────────────────────────────────────────────────────────────
|
| 181 |
+
app.layout = html.Div([
|
| 182 |
+
|
| 183 |
+
# Capturable content area with clear boundary
|
| 184 |
+
html.Div([
|
| 185 |
+
|
| 186 |
+
# Header + Banner
|
| 187 |
+
html.Div([
|
| 188 |
+
html.Div([
|
| 189 |
+
html.H1("Default Cascade Simulation of Banks 2026", style={
|
| 190 |
+
"fontSize": "24px", "fontWeight": "600",
|
| 191 |
+
"margin": "0 0 6px", "color": CLR_TEXT,
|
| 192 |
+
}),
|
| 193 |
+
html.P(
|
| 194 |
+
f"Impact of CRE defaults and unrealized losses on {N_BANKS} U.S. largest banks (more than $10 billion in assets)",
|
| 195 |
+
style={"fontSize": "14px", "color": CLR_MUTED, "margin": "0 0 4px"},
|
| 196 |
+
),
|
| 197 |
+
html.P([
|
| 198 |
+
"Source: ",
|
| 199 |
+
html.A("Signalpha", href="https://signalpha.substack.com/", target="_blank",
|
| 200 |
+
style={"color": CLR_LINK, "textDecoration": "none"}),
|
| 201 |
+
], style={"fontSize": "12px", "margin": "0 0 2px", "color": CLR_MUTED}),
|
| 202 |
+
html.P([
|
| 203 |
+
"Data: ",
|
| 204 |
+
html.A("The Banking Initiative at Florida Atlantic University",
|
| 205 |
+
href="https://business.fau.edu/departments/finance/banking-initiative/",
|
| 206 |
+
target="_blank",
|
| 207 |
+
style={"color": CLR_LINK, "textDecoration": "none"}),
|
| 208 |
+
], style={"fontSize": "12px", "margin": 0, "color": CLR_MUTED}),
|
| 209 |
+
], style={"flex": "1"}),
|
| 210 |
+
html.Img(src="/assets/signalpha-banner.png", style={
|
| 211 |
+
"height": "80px", "borderRadius": "6px", "alignSelf": "center",
|
| 212 |
+
}),
|
| 213 |
+
], style={"display": "flex", "alignItems": "flex-start", "gap": "20px", "marginBottom": "28px"}),
|
| 214 |
+
|
| 215 |
+
# Controls
|
| 216 |
+
html.Div([
|
| 217 |
+
html.Div("Scenario Parameters", style={
|
| 218 |
+
"fontSize": "13px", "fontWeight": "600", "color": CLR_TEXT,
|
| 219 |
+
"marginBottom": "14px", "textTransform": "uppercase", "letterSpacing": "0.5px",
|
| 220 |
+
}),
|
| 221 |
+
make_slider("default-rate", "CRE Default Rate", 0, 50, 0.5, 10, "%"),
|
| 222 |
+
make_slider("lgd", "Loss Given Default", 0, 100, 1, 50, "%"),
|
| 223 |
+
make_slider("rwa-ratio", "Average RWA Ratio", 50, 75, 0.5, 65, "%"),
|
| 224 |
+
make_slider("min-assets", "Total Assets", 10, 100, 1, 10, "$B"),
|
| 225 |
+
# Unrealized losses toggle
|
| 226 |
+
html.Div([
|
| 227 |
+
html.Label("Unrealized Losses", style={
|
| 228 |
+
"fontSize": "13px", "color": CLR_MUTED,
|
| 229 |
+
"minWidth": "155px", "marginRight": "12px",
|
| 230 |
+
"fontWeight": "500",
|
| 231 |
+
}),
|
| 232 |
+
dcc.Checklist(
|
| 233 |
+
id="unrealized-toggle",
|
| 234 |
+
options=[{"label": " Include Total Unrealized Losses on Investment Securities", "value": "on"}],
|
| 235 |
+
value=[],
|
| 236 |
+
style={"fontSize": "13px", "color": CLR_TEXT},
|
| 237 |
+
inputStyle={"marginRight": "6px"},
|
| 238 |
+
),
|
| 239 |
+
], style={"display": "flex", "alignItems": "center", "gap": "12px",
|
| 240 |
+
"marginTop": "4px"}),
|
| 241 |
+
# Has CRE Losses toggle
|
| 242 |
+
html.Div([
|
| 243 |
+
html.Label("Has CRE Losses", style={
|
| 244 |
+
"fontSize": "13px", "color": CLR_MUTED,
|
| 245 |
+
"minWidth": "155px", "marginRight": "12px",
|
| 246 |
+
"fontWeight": "500",
|
| 247 |
+
}),
|
| 248 |
+
dcc.Checklist(
|
| 249 |
+
id="cre-losses-toggle",
|
| 250 |
+
options=[{"label": " Exclude Banks Without CRE Losses", "value": "on"}],
|
| 251 |
+
value=[],
|
| 252 |
+
style={"fontSize": "13px", "color": CLR_TEXT},
|
| 253 |
+
inputStyle={"marginRight": "6px"},
|
| 254 |
+
),
|
| 255 |
+
], style={"display": "flex", "alignItems": "center", "gap": "12px",
|
| 256 |
+
"marginTop": "8px"}),
|
| 257 |
+
# Publicly traded only toggle
|
| 258 |
+
html.Div([
|
| 259 |
+
html.Label("Publicly Traded Only", style={
|
| 260 |
+
"fontSize": "13px", "color": CLR_MUTED,
|
| 261 |
+
"minWidth": "155px", "marginRight": "12px",
|
| 262 |
+
"fontWeight": "500",
|
| 263 |
+
}),
|
| 264 |
+
dcc.Checklist(
|
| 265 |
+
id="public-toggle",
|
| 266 |
+
options=[{"label": " Exclude Non-Publicly Traded Banks", "value": "on"}],
|
| 267 |
+
value=[],
|
| 268 |
+
style={"fontSize": "13px", "color": CLR_TEXT},
|
| 269 |
+
inputStyle={"marginRight": "6px"},
|
| 270 |
+
),
|
| 271 |
+
], style={"display": "flex", "alignItems": "center", "gap": "12px",
|
| 272 |
+
"marginTop": "8px"}),
|
| 273 |
+
], style={
|
| 274 |
+
"background": CLR_CARD_BG,
|
| 275 |
+
"borderRadius": "10px",
|
| 276 |
+
"padding": "20px 24px",
|
| 277 |
+
"marginBottom": "20px",
|
| 278 |
+
"border": f"1px solid {CLR_BORDER}",
|
| 279 |
+
}),
|
| 280 |
+
|
| 281 |
+
# ── Visualization 1: Tangible Equity Ratio ────────────────────────
|
| 282 |
+
html.Div([
|
| 283 |
+
html.Div([
|
| 284 |
+
html.Span("Tangible Equity Ratio", style={
|
| 285 |
+
"fontSize": "15px", "fontWeight": "600", "color": CLR_TEXT,
|
| 286 |
+
}),
|
| 287 |
+
html.Span(" — Prompt Corrective Action Categories", style={
|
| 288 |
+
"fontSize": "13px", "color": CLR_MUTED,
|
| 289 |
+
}),
|
| 290 |
+
], style={"marginBottom": "4px"}),
|
| 291 |
+
html.P(id="formula-pca", style={
|
| 292 |
+
"fontSize": "11px", "color": CLR_MUTED, "margin": "0 0 8px",
|
| 293 |
+
"fontFamily": "monospace",
|
| 294 |
+
}),
|
| 295 |
+
# Legend
|
| 296 |
+
html.Div([
|
| 297 |
+
html.Span([html.Span(style={"width": "10px", "height": "10px", "borderRadius": "2px",
|
| 298 |
+
"background": RED_WELL_CAP, "display": "inline-block", "marginRight": "4px"}),
|
| 299 |
+
"Well Capitalized (\u22658%)"], style={"fontSize": "11px", "color": CLR_MUTED, "marginRight": "12px"}),
|
| 300 |
+
html.Span([html.Span(style={"width": "10px", "height": "10px", "borderRadius": "2px",
|
| 301 |
+
"background": RED_ADEQ_CAP, "display": "inline-block", "marginRight": "4px"}),
|
| 302 |
+
"Adequately Capitalized (\u22654%)"], style={"fontSize": "11px", "color": CLR_MUTED, "marginRight": "12px"}),
|
| 303 |
+
html.Span([html.Span(style={"width": "10px", "height": "10px", "borderRadius": "2px",
|
| 304 |
+
"background": RED_UNDERCAP, "display": "inline-block", "marginRight": "4px"}),
|
| 305 |
+
"Undercapitalized (\u22652%)"], style={"fontSize": "11px", "color": CLR_MUTED, "marginRight": "12px"}),
|
| 306 |
+
html.Span([html.Span(style={"width": "10px", "height": "10px", "borderRadius": "2px",
|
| 307 |
+
"background": RED_CRIT_UNDCAP, "display": "inline-block", "marginRight": "4px"}),
|
| 308 |
+
"Critically Undercapitalized (<2%)"], style={"fontSize": "11px", "color": CLR_MUTED, "marginRight": "12px"}),
|
| 309 |
+
html.Span([html.Span(style={"width": "10px", "height": "10px", "borderRadius": "2px",
|
| 310 |
+
"background": RED_INSOLVENT, "display": "inline-block", "marginRight": "4px"}),
|
| 311 |
+
"Insolvent (<0%)"], style={"fontSize": "11px", "color": CLR_MUTED}),
|
| 312 |
+
], style={"marginBottom": "8px", "display": "flex", "flexWrap": "wrap", "gap": "4px"}),
|
| 313 |
+
dcc.Graph(id="chart-pca", config={"displayModeBar": False}, style={"height": "48px"}),
|
| 314 |
+
# PCA metric cards
|
| 315 |
+
html.Div([
|
| 316 |
+
metric_card("pca-total-losses", "Total Losses"),
|
| 317 |
+
metric_card("pca-banks-undercap", "Banks Undercapitalized", sub_id="pca-undercap-assets"),
|
| 318 |
+
metric_card("pca-banks-insolvent", "Banks Insolvent", sub_id="pca-insolvent-assets"),
|
| 319 |
+
], style={
|
| 320 |
+
"display": "flex", "gap": "10px",
|
| 321 |
+
"flexWrap": "wrap", "marginTop": "12px",
|
| 322 |
+
}),
|
| 323 |
+
], style={
|
| 324 |
+
"background": CLR_CARD_BG, "borderRadius": "10px",
|
| 325 |
+
"padding": "18px 24px", "marginBottom": "16px",
|
| 326 |
+
"border": f"1px solid {CLR_BORDER}",
|
| 327 |
+
}),
|
| 328 |
+
|
| 329 |
+
# ── Visualization 2: Estimated CET1 Ratio ────────────────────────
|
| 330 |
+
html.Div([
|
| 331 |
+
html.Div([
|
| 332 |
+
html.Span("Estimated CET1 Ratio", style={
|
| 333 |
+
"fontSize": "15px", "fontWeight": "600", "color": CLR_TEXT,
|
| 334 |
+
}),
|
| 335 |
+
html.Span(" — Basel III Capital Thresholds", style={
|
| 336 |
+
"fontSize": "13px", "color": CLR_MUTED,
|
| 337 |
+
}),
|
| 338 |
+
], style={"marginBottom": "4px"}),
|
| 339 |
+
html.P(id="formula-cet1", style={
|
| 340 |
+
"fontSize": "11px", "color": CLR_MUTED, "margin": "0 0 8px",
|
| 341 |
+
"fontFamily": "monospace",
|
| 342 |
+
}),
|
| 343 |
+
# Legend
|
| 344 |
+
html.Div([
|
| 345 |
+
html.Span([html.Span(style={"width": "10px", "height": "10px", "borderRadius": "2px",
|
| 346 |
+
"background": PUR_WELL_CAP, "display": "inline-block", "marginRight": "4px"}),
|
| 347 |
+
"Well Capitalized (\u22656.5%)"], style={"fontSize": "11px", "color": CLR_MUTED, "marginRight": "12px"}),
|
| 348 |
+
html.Span([html.Span(style={"width": "10px", "height": "10px", "borderRadius": "2px",
|
| 349 |
+
"background": PUR_UNDERCAP, "display": "inline-block", "marginRight": "4px"}),
|
| 350 |
+
"Undercapitalized (\u22654.5%)"], style={"fontSize": "11px", "color": CLR_MUTED, "marginRight": "12px"}),
|
| 351 |
+
html.Span([html.Span(style={"width": "10px", "height": "10px", "borderRadius": "2px",
|
| 352 |
+
"background": PUR_BELOW_MIN, "display": "inline-block", "marginRight": "4px"}),
|
| 353 |
+
"Below Basel III Minimum (<4.5%)"], style={"fontSize": "11px", "color": CLR_MUTED, "marginRight": "12px"}),
|
| 354 |
+
html.Span([html.Span(style={"width": "10px", "height": "10px", "borderRadius": "2px",
|
| 355 |
+
"background": PUR_INSOLVENT, "display": "inline-block", "marginRight": "4px"}),
|
| 356 |
+
"Insolvent (<0%)"], style={"fontSize": "11px", "color": CLR_MUTED}),
|
| 357 |
+
], style={"marginBottom": "8px", "display": "flex", "flexWrap": "wrap", "gap": "4px"}),
|
| 358 |
+
dcc.Graph(id="chart-cet1", config={"displayModeBar": False}, style={"height": "48px"}),
|
| 359 |
+
# CET1 metric cards
|
| 360 |
+
html.Div([
|
| 361 |
+
metric_card("cet1-total-losses", "Total Losses"),
|
| 362 |
+
metric_card("cet1-banks-undercap", "Banks Below Minimum", sub_id="cet1-undercap-assets"),
|
| 363 |
+
metric_card("cet1-banks-insolvent", "Banks Insolvent", sub_id="cet1-insolvent-assets"),
|
| 364 |
+
], style={
|
| 365 |
+
"display": "flex", "gap": "10px",
|
| 366 |
+
"flexWrap": "wrap", "marginTop": "12px",
|
| 367 |
+
}),
|
| 368 |
+
], style={
|
| 369 |
+
"background": CLR_CARD_BG, "borderRadius": "10px",
|
| 370 |
+
"padding": "18px 24px", "marginBottom": "16px",
|
| 371 |
+
"border": f"1px solid {CLR_BORDER}",
|
| 372 |
+
}),
|
| 373 |
+
|
| 374 |
+
], id="capture-area", style={
|
| 375 |
+
"maxWidth": "860px",
|
| 376 |
+
"margin": "0 auto",
|
| 377 |
+
"padding": "40px 28px",
|
| 378 |
+
"background": "#FFFFFF",
|
| 379 |
+
"border": f"1px solid {CLR_BORDER}",
|
| 380 |
+
"borderRadius": "12px",
|
| 381 |
+
}),
|
| 382 |
+
|
| 383 |
+
# Download buttons (outside capture area)
|
| 384 |
+
html.Div([
|
| 385 |
+
html.Button(
|
| 386 |
+
"Download Figure",
|
| 387 |
+
id="btn-download",
|
| 388 |
+
style={
|
| 389 |
+
"fontSize": "13px",
|
| 390 |
+
"fontWeight": "500",
|
| 391 |
+
"padding": "10px 24px",
|
| 392 |
+
"borderRadius": "8px",
|
| 393 |
+
"border": f"1px solid {CLR_BORDER}",
|
| 394 |
+
"background": CLR_CARD_BG,
|
| 395 |
+
"color": CLR_TEXT,
|
| 396 |
+
"cursor": "pointer",
|
| 397 |
+
"marginRight": "10px",
|
| 398 |
+
},
|
| 399 |
+
),
|
| 400 |
+
html.Button(
|
| 401 |
+
"Download Data (CSV)",
|
| 402 |
+
id="btn-download-csv",
|
| 403 |
+
style={
|
| 404 |
+
"fontSize": "13px",
|
| 405 |
+
"fontWeight": "500",
|
| 406 |
+
"padding": "10px 24px",
|
| 407 |
+
"borderRadius": "8px",
|
| 408 |
+
"border": f"1px solid {CLR_BORDER}",
|
| 409 |
+
"background": CLR_CARD_BG,
|
| 410 |
+
"color": CLR_TEXT,
|
| 411 |
+
"cursor": "pointer",
|
| 412 |
+
"marginRight": "10px",
|
| 413 |
+
},
|
| 414 |
+
),
|
| 415 |
+
html.A(
|
| 416 |
+
html.Button(
|
| 417 |
+
"Signalpha on Substack",
|
| 418 |
+
style={
|
| 419 |
+
"fontSize": "13px",
|
| 420 |
+
"fontWeight": "500",
|
| 421 |
+
"padding": "10px 24px",
|
| 422 |
+
"borderRadius": "8px",
|
| 423 |
+
"border": "none",
|
| 424 |
+
"background": "#7C3AED",
|
| 425 |
+
"color": "#FFFFFF",
|
| 426 |
+
"cursor": "pointer",
|
| 427 |
+
},
|
| 428 |
+
),
|
| 429 |
+
href="https://signalpha.substack.com/",
|
| 430 |
+
target="_blank",
|
| 431 |
+
style={"textDecoration": "none"},
|
| 432 |
+
),
|
| 433 |
+
], style={"maxWidth": "860px", "margin": "0 auto", "textAlign": "center",
|
| 434 |
+
"padding": "16px 28px 0"}),
|
| 435 |
+
|
| 436 |
+
# Disclaimer
|
| 437 |
+
html.Div([
|
| 438 |
+
html.P(
|
| 439 |
+
"Disclaimer: The information provided in this application is for educational and informational purposes only and does not constitute financial, investment, legal, or tax advice. While every effort is made to ensure the accuracy of the data and analysis presented, all content is provided \"as is\" and without warranty of any kind. Investing in financial markets involves significant risk, including the potential loss of principal. The author is not a registered investment advisor or broker-dealer, and the views expressed are personal opinions that may change without notice. You should consult with a qualified professional before making any financial decisions, and you agree that the author shall not be held liable for any losses or damages resulting from the use of this information.",
|
| 440 |
+
style={"fontSize": "11px", "lineHeight": "1.6", "color": CLR_MUTED, "margin": "0"},
|
| 441 |
+
),
|
| 442 |
+
], style={
|
| 443 |
+
"maxWidth": "860px", "margin": "20px auto 0",
|
| 444 |
+
"padding": "16px 28px",
|
| 445 |
+
"background": CLR_CARD_BG, "borderRadius": "10px",
|
| 446 |
+
"border": f"1px solid {CLR_BORDER}",
|
| 447 |
+
}),
|
| 448 |
+
|
| 449 |
+
# Hidden download components
|
| 450 |
+
dcc.Download(id="download-csv"),
|
| 451 |
+
|
| 452 |
+
# Documentation
|
| 453 |
+
html.Div([
|
| 454 |
+
html.Details([
|
| 455 |
+
html.Summary("Documentation", style={
|
| 456 |
+
"fontSize": "15px", "fontWeight": "600", "color": CLR_TEXT,
|
| 457 |
+
"cursor": "pointer", "padding": "12px 0",
|
| 458 |
+
}),
|
| 459 |
+
dcc.Markdown(
|
| 460 |
+
open(os.path.join(os.path.dirname(__file__), "documentation.md")).read(),
|
| 461 |
+
style={"fontSize": "13px", "lineHeight": "1.7", "color": CLR_TEXT},
|
| 462 |
+
),
|
| 463 |
+
], open=False),
|
| 464 |
+
], style={
|
| 465 |
+
"maxWidth": "860px", "margin": "24px auto 0",
|
| 466 |
+
"background": CLR_CARD_BG, "borderRadius": "10px",
|
| 467 |
+
"padding": "8px 28px 20px",
|
| 468 |
+
"border": f"1px solid {CLR_BORDER}",
|
| 469 |
+
}),
|
| 470 |
+
|
| 471 |
+
], style={
|
| 472 |
+
"padding": "40px 20px",
|
| 473 |
+
"fontFamily": "-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Inter', sans-serif",
|
| 474 |
+
"background": CLR_BG,
|
| 475 |
+
"minHeight": "100vh",
|
| 476 |
+
"color": CLR_TEXT,
|
| 477 |
+
})
|
| 478 |
+
|
| 479 |
+
# ── Client-side callback for image download via html2canvas ───────────────────
|
| 480 |
+
app.index_string = '''
|
| 481 |
+
<!DOCTYPE html>
|
| 482 |
+
<html>
|
| 483 |
+
<head>
|
| 484 |
+
{%metas%}
|
| 485 |
+
<title>{%title%}</title>
|
| 486 |
+
{%favicon%}
|
| 487 |
+
{%css%}
|
| 488 |
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
|
| 489 |
+
</head>
|
| 490 |
+
<body>
|
| 491 |
+
{%app_entry%}
|
| 492 |
+
<footer>
|
| 493 |
+
{%config%}
|
| 494 |
+
{%scripts%}
|
| 495 |
+
{%renderer%}
|
| 496 |
+
</footer>
|
| 497 |
+
</body>
|
| 498 |
+
</html>
|
| 499 |
+
'''
|
| 500 |
+
|
| 501 |
+
clientside_callback(
|
| 502 |
+
"""
|
| 503 |
+
function(n_clicks) {
|
| 504 |
+
if (!n_clicks) return window.dash_clientside.no_update;
|
| 505 |
+
var el = document.getElementById('capture-area');
|
| 506 |
+
if (!el) return window.dash_clientside.no_update;
|
| 507 |
+
html2canvas(el, {
|
| 508 |
+
backgroundColor: '#FFFFFF',
|
| 509 |
+
scale: 2,
|
| 510 |
+
useCORS: true,
|
| 511 |
+
logging: false,
|
| 512 |
+
}).then(function(canvas) {
|
| 513 |
+
var link = document.createElement('a');
|
| 514 |
+
link.download = 'default-cascade-simulation.png';
|
| 515 |
+
link.href = canvas.toDataURL('image/png');
|
| 516 |
+
link.click();
|
| 517 |
+
});
|
| 518 |
+
return window.dash_clientside.no_update;
|
| 519 |
+
}
|
| 520 |
+
""",
|
| 521 |
+
Output("btn-download", "n_clicks"),
|
| 522 |
+
Input("btn-download", "n_clicks"),
|
| 523 |
+
prevent_initial_call=True,
|
| 524 |
+
)
|
| 525 |
+
|
| 526 |
+
|
| 527 |
+
# ── Callbacks ─────────────────────────────────────────────────────────────────
|
| 528 |
+
@callback(
|
| 529 |
+
Output("default-rate-out", "children"),
|
| 530 |
+
Output("lgd-out", "children"),
|
| 531 |
+
Output("rwa-ratio-out", "children"),
|
| 532 |
+
Output("min-assets-out", "children"),
|
| 533 |
+
# Formulas
|
| 534 |
+
Output("formula-pca", "children"),
|
| 535 |
+
Output("formula-cet1", "children"),
|
| 536 |
+
# PCA metrics
|
| 537 |
+
Output("pca-total-losses", "children"),
|
| 538 |
+
Output("pca-banks-undercap", "children"),
|
| 539 |
+
Output("pca-undercap-assets", "children"),
|
| 540 |
+
Output("pca-banks-insolvent", "children"),
|
| 541 |
+
Output("pca-insolvent-assets", "children"),
|
| 542 |
+
# CET1 metrics
|
| 543 |
+
Output("cet1-total-losses", "children"),
|
| 544 |
+
Output("cet1-banks-undercap", "children"),
|
| 545 |
+
Output("cet1-undercap-assets", "children"),
|
| 546 |
+
Output("cet1-banks-insolvent", "children"),
|
| 547 |
+
Output("cet1-insolvent-assets", "children"),
|
| 548 |
+
# Charts
|
| 549 |
+
Output("chart-pca", "figure"),
|
| 550 |
+
Output("chart-cet1", "figure"),
|
| 551 |
+
Input("default-rate", "value"),
|
| 552 |
+
Input("lgd", "value"),
|
| 553 |
+
Input("rwa-ratio", "value"),
|
| 554 |
+
Input("min-assets", "value"),
|
| 555 |
+
Input("unrealized-toggle", "value"),
|
| 556 |
+
Input("cre-losses-toggle", "value"),
|
| 557 |
+
Input("public-toggle", "value"),
|
| 558 |
+
)
|
| 559 |
+
def update(default_rate, lgd, rwa_ratio, min_assets, unrealized_toggle, cre_losses_toggle, public_toggle):
|
| 560 |
+
default_rate = (default_rate or 10) / 100
|
| 561 |
+
lgd = (lgd or 50) / 100
|
| 562 |
+
rwa_ratio = (rwa_ratio or 65) / 100
|
| 563 |
+
min_assets_b = min_assets or 10 # in $B
|
| 564 |
+
include_unrealized = "on" in (unrealized_toggle or [])
|
| 565 |
+
has_cre_only = "on" in (cre_losses_toggle or [])
|
| 566 |
+
public_only = "on" in (public_toggle or [])
|
| 567 |
+
|
| 568 |
+
# Filter data
|
| 569 |
+
data = df.copy()
|
| 570 |
+
data = data[data["Total Assets ($M)"] >= min_assets_b * 1000]
|
| 571 |
+
if public_only:
|
| 572 |
+
data = data[data["_has_ticker"]]
|
| 573 |
+
if has_cre_only:
|
| 574 |
+
data = data[data["CRE Total ($M)"] > 0]
|
| 575 |
+
|
| 576 |
+
n_banks = len(data)
|
| 577 |
+
|
| 578 |
+
(adj_eq, adj_cet1, total_losses,
|
| 579 |
+
insolv_assets_pca, undercap_assets_pca,
|
| 580 |
+
insolv_assets_cet1, undercap_assets_cet1) = compute_scenario(
|
| 581 |
+
default_rate, lgd, rwa_ratio, include_unrealized, data=data
|
| 582 |
+
)
|
| 583 |
+
|
| 584 |
+
# Total losses displayed in metric cards (CRE losses + unrealized if toggled)
|
| 585 |
+
if include_unrealized:
|
| 586 |
+
display_total_losses = total_losses + data["Total Unrealized Loss ($M)"].values.sum()
|
| 587 |
+
else:
|
| 588 |
+
display_total_losses = total_losses
|
| 589 |
+
|
| 590 |
+
# Dynamic formula labels
|
| 591 |
+
if include_unrealized:
|
| 592 |
+
formula_pca = "Adjusted Equity-to-Assets = (Total Equity \u2212 CRE Losses \u2212 Unrealized Losses) / Total Assets"
|
| 593 |
+
formula_cet1 = "Adjusted CET1 = (CET1 Capital \u2212 CRE Losses \u2212 Unrealized Losses) / (Total Assets \u00d7 RWA Ratio)"
|
| 594 |
+
else:
|
| 595 |
+
formula_pca = "Adjusted Equity-to-Assets = (Total Equity \u2212 CRE Losses) / Total Assets"
|
| 596 |
+
formula_cet1 = "Adjusted CET1 = (CET1 Capital \u2212 CRE Losses) / (Total Assets \u00d7 RWA Ratio)"
|
| 597 |
+
|
| 598 |
+
# PCA classification
|
| 599 |
+
well, adequately, under, critical, insolvent = classify_pca(adj_eq)
|
| 600 |
+
|
| 601 |
+
# CET1 classification
|
| 602 |
+
c_well, c_under, c_below, c_insolvent = classify_cet1(adj_cet1)
|
| 603 |
+
|
| 604 |
+
# PCA counts
|
| 605 |
+
n_undercap_pca = under + critical + insolvent
|
| 606 |
+
n_insolvent_pca = insolvent
|
| 607 |
+
|
| 608 |
+
# CET1 counts
|
| 609 |
+
n_undercap_cet1 = c_below + c_insolvent
|
| 610 |
+
n_insolvent_cet1 = c_insolvent
|
| 611 |
+
|
| 612 |
+
# ── PCA bar chart ─────────────────────────────────────────────────────
|
| 613 |
+
fig_pca = go.Figure()
|
| 614 |
+
|
| 615 |
+
pca_data = [
|
| 616 |
+
("Well Capitalized", well, RED_WELL_CAP, "#1A1A1A"),
|
| 617 |
+
("Adequately Capitalized", adequately, RED_ADEQ_CAP, "#1A1A1A"),
|
| 618 |
+
("Undercapitalized", under, RED_UNDERCAP, "#FFFFFF"),
|
| 619 |
+
("Critically Undercapitalized", critical, RED_CRIT_UNDCAP, "#FFFFFF"),
|
| 620 |
+
("Insolvent", insolvent, RED_INSOLVENT, "#FFFFFF"),
|
| 621 |
+
]
|
| 622 |
+
|
| 623 |
+
for name, count, color, text_color in pca_data:
|
| 624 |
+
fig_pca.add_trace(go.Bar(
|
| 625 |
+
name=name,
|
| 626 |
+
y=["Banks"],
|
| 627 |
+
x=[count],
|
| 628 |
+
width=[BAR_WIDTH],
|
| 629 |
+
orientation="h",
|
| 630 |
+
marker_color=color,
|
| 631 |
+
text=[str(count) if count > 0 else ""],
|
| 632 |
+
textposition="inside",
|
| 633 |
+
textfont={"size": 13, "color": text_color, "family": "Inter, sans-serif"},
|
| 634 |
+
insidetextanchor="middle",
|
| 635 |
+
hovertemplate=f"{name}: %{{x}} banks<extra></extra>",
|
| 636 |
+
showlegend=False,
|
| 637 |
+
))
|
| 638 |
+
|
| 639 |
+
fig_pca.update_layout(
|
| 640 |
+
barmode="stack",
|
| 641 |
+
paper_bgcolor="rgba(0,0,0,0)",
|
| 642 |
+
plot_bgcolor="rgba(0,0,0,0)",
|
| 643 |
+
margin={"l": 0, "r": 0, "t": 0, "b": 0},
|
| 644 |
+
xaxis={"visible": False, "range": [0, n_banks]},
|
| 645 |
+
yaxis={"visible": False, "range": [-0.5, 0.5]},
|
| 646 |
+
height=BAR_HEIGHT,
|
| 647 |
+
hoverlabel={"bgcolor": "#FFFFFF", "bordercolor": CLR_BORDER, "font": {"color": CLR_TEXT}},
|
| 648 |
+
)
|
| 649 |
+
|
| 650 |
+
# ── CET1 bar chart ────────────────────────────────────────────────────
|
| 651 |
+
fig_cet1 = go.Figure()
|
| 652 |
+
|
| 653 |
+
cet1_data = [
|
| 654 |
+
("Well Capitalized", c_well, PUR_WELL_CAP, "#1A1A1A"),
|
| 655 |
+
("Undercapitalized", c_under, PUR_UNDERCAP, "#FFFFFF"),
|
| 656 |
+
("Below Basel III Minimum", c_below, PUR_BELOW_MIN, "#FFFFFF"),
|
| 657 |
+
("Insolvent", c_insolvent, PUR_INSOLVENT, "#FFFFFF"),
|
| 658 |
+
]
|
| 659 |
+
|
| 660 |
+
for name, count, color, text_color in cet1_data:
|
| 661 |
+
fig_cet1.add_trace(go.Bar(
|
| 662 |
+
name=name,
|
| 663 |
+
y=["Banks"],
|
| 664 |
+
x=[count],
|
| 665 |
+
width=[BAR_WIDTH],
|
| 666 |
+
orientation="h",
|
| 667 |
+
marker_color=color,
|
| 668 |
+
text=[str(count) if count > 0 else ""],
|
| 669 |
+
textposition="inside",
|
| 670 |
+
textfont={"size": 13, "color": text_color, "family": "Inter, sans-serif"},
|
| 671 |
+
insidetextanchor="middle",
|
| 672 |
+
hovertemplate=f"{name}: %{{x}} banks<extra></extra>",
|
| 673 |
+
showlegend=False,
|
| 674 |
+
))
|
| 675 |
+
|
| 676 |
+
fig_cet1.update_layout(
|
| 677 |
+
barmode="stack",
|
| 678 |
+
paper_bgcolor="rgba(0,0,0,0)",
|
| 679 |
+
plot_bgcolor="rgba(0,0,0,0)",
|
| 680 |
+
margin={"l": 0, "r": 0, "t": 0, "b": 0},
|
| 681 |
+
xaxis={"visible": False, "range": [0, n_banks]},
|
| 682 |
+
yaxis={"visible": False, "range": [-0.5, 0.5]},
|
| 683 |
+
height=BAR_HEIGHT,
|
| 684 |
+
hoverlabel={"bgcolor": "#FFFFFF", "bordercolor": CLR_BORDER, "font": {"color": CLR_TEXT}},
|
| 685 |
+
)
|
| 686 |
+
|
| 687 |
+
return (
|
| 688 |
+
f"{default_rate * 100:.1f}%",
|
| 689 |
+
f"{lgd * 100:.1f}%",
|
| 690 |
+
f"{rwa_ratio * 100:.1f}%",
|
| 691 |
+
f"${min_assets_b:.0f}B",
|
| 692 |
+
# Formulas
|
| 693 |
+
formula_pca,
|
| 694 |
+
formula_cet1,
|
| 695 |
+
# PCA metrics
|
| 696 |
+
f"${display_total_losses / 1000:.1f}B",
|
| 697 |
+
str(n_undercap_pca),
|
| 698 |
+
fmt_assets(undercap_assets_pca),
|
| 699 |
+
str(n_insolvent_pca),
|
| 700 |
+
fmt_assets(insolv_assets_pca),
|
| 701 |
+
# CET1 metrics
|
| 702 |
+
f"${display_total_losses / 1000:.1f}B",
|
| 703 |
+
str(n_undercap_cet1),
|
| 704 |
+
fmt_assets(undercap_assets_cet1),
|
| 705 |
+
str(n_insolvent_cet1),
|
| 706 |
+
fmt_assets(insolv_assets_cet1),
|
| 707 |
+
# Charts
|
| 708 |
+
fig_pca,
|
| 709 |
+
fig_cet1,
|
| 710 |
+
)
|
| 711 |
+
|
| 712 |
+
|
| 713 |
+
# ── CSV download callback ─────────────────────────────────────────────────────
|
| 714 |
+
def classify_pca_label(ratio):
|
| 715 |
+
if ratio >= 0.08:
|
| 716 |
+
return "Well Capitalized"
|
| 717 |
+
elif ratio >= 0.04:
|
| 718 |
+
return "Adequately Capitalized"
|
| 719 |
+
elif ratio >= 0.02:
|
| 720 |
+
return "Undercapitalized"
|
| 721 |
+
elif ratio >= 0:
|
| 722 |
+
return "Critically Undercapitalized"
|
| 723 |
+
return "Insolvent"
|
| 724 |
+
|
| 725 |
+
|
| 726 |
+
def classify_cet1_label(ratio):
|
| 727 |
+
if ratio >= 0.065:
|
| 728 |
+
return "Well Capitalized"
|
| 729 |
+
elif ratio >= 0.045:
|
| 730 |
+
return "Undercapitalized"
|
| 731 |
+
elif ratio >= 0:
|
| 732 |
+
return "Below Basel III Minimum"
|
| 733 |
+
return "Insolvent"
|
| 734 |
+
|
| 735 |
+
|
| 736 |
+
@callback(
|
| 737 |
+
Output("download-csv", "data"),
|
| 738 |
+
Input("btn-download-csv", "n_clicks"),
|
| 739 |
+
State("default-rate", "value"),
|
| 740 |
+
State("lgd", "value"),
|
| 741 |
+
State("rwa-ratio", "value"),
|
| 742 |
+
State("min-assets", "value"),
|
| 743 |
+
State("unrealized-toggle", "value"),
|
| 744 |
+
State("cre-losses-toggle", "value"),
|
| 745 |
+
State("public-toggle", "value"),
|
| 746 |
+
prevent_initial_call=True,
|
| 747 |
+
)
|
| 748 |
+
def download_csv(n_clicks, default_rate, lgd_val, rwa_ratio, min_assets, unrealized_toggle, cre_losses_toggle, public_toggle):
|
| 749 |
+
if not n_clicks:
|
| 750 |
+
return dash.no_update
|
| 751 |
+
|
| 752 |
+
dr = (default_rate or 10) / 100
|
| 753 |
+
lgd_v = (lgd_val or 50) / 100
|
| 754 |
+
rwa_r = (rwa_ratio or 65) / 100
|
| 755 |
+
min_assets_b = min_assets or 10
|
| 756 |
+
include_unrealized = "on" in (unrealized_toggle or [])
|
| 757 |
+
has_cre_only = "on" in (cre_losses_toggle or [])
|
| 758 |
+
public_only = "on" in (public_toggle or [])
|
| 759 |
+
|
| 760 |
+
data = df.copy()
|
| 761 |
+
data = data[data["Total Assets ($M)"] >= min_assets_b * 1000]
|
| 762 |
+
if public_only:
|
| 763 |
+
data = data[data["_has_ticker"]]
|
| 764 |
+
if has_cre_only:
|
| 765 |
+
data = data[data["CRE Total ($M)"] > 0]
|
| 766 |
+
|
| 767 |
+
cre = data["CRE Total ($M)"].values
|
| 768 |
+
equity = data["Total Equity ($M)"].values
|
| 769 |
+
assets = data["Total Assets ($M)"].values
|
| 770 |
+
cet1 = data["CET1 Capital ($M)"].values
|
| 771 |
+
unrealized = data["Total Unrealized Loss ($M)"].values if include_unrealized else 0
|
| 772 |
+
|
| 773 |
+
cre_losses = cre * dr * lgd_v
|
| 774 |
+
adj_eq = (equity - cre_losses - unrealized) / assets
|
| 775 |
+
estimated_rwa = assets * rwa_r
|
| 776 |
+
adj_cet1 = (cet1 - cre_losses - unrealized) / estimated_rwa
|
| 777 |
+
|
| 778 |
+
out = pd.DataFrame({
|
| 779 |
+
"Bank Name": data["Name"].values,
|
| 780 |
+
"State": data["ST"].values,
|
| 781 |
+
"Total Assets ($M)": assets,
|
| 782 |
+
"Total Equity ($M)": equity,
|
| 783 |
+
"CRE Total ($M)": cre,
|
| 784 |
+
"CET1 Capital ($M)": cet1,
|
| 785 |
+
"Total Unrealized Loss ($M)": data["Total Unrealized Loss ($M)"].values,
|
| 786 |
+
"CRE Losses ($M)": cre_losses.round(1),
|
| 787 |
+
"Adjusted Equity-to-Assets (%)": (adj_eq * 100).round(2),
|
| 788 |
+
"PCA Classification": [classify_pca_label(r) for r in adj_eq],
|
| 789 |
+
"Adjusted CET1 Ratio (%)": (adj_cet1 * 100).round(2),
|
| 790 |
+
"CET1 Classification": [classify_cet1_label(r) for r in adj_cet1],
|
| 791 |
+
})
|
| 792 |
+
|
| 793 |
+
buf = io.StringIO()
|
| 794 |
+
out.to_csv(buf, index=False)
|
| 795 |
+
|
| 796 |
+
unr_tag = "_UL" if include_unrealized else ""
|
| 797 |
+
pub_tag = "_PUB" if public_only else ""
|
| 798 |
+
filename = f"cre-simulation_DR{default_rate:.0f}_LGD{lgd_val:.0f}_RWA{rwa_ratio:.0f}{unr_tag}{pub_tag}.csv"
|
| 799 |
+
return dcc.send_string(buf.getvalue(), filename=filename)
|
| 800 |
+
|
| 801 |
+
|
| 802 |
+
# ── Entry point ───────────────────────────────────────────────────────────────
|
| 803 |
+
if __name__ == "__main__":
|
| 804 |
+
app.run(debug=False, host="0.0.0.0", port=8050)
|
assets/custom.css
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/* Force all slider input/handle boxes to uniform width */
|
| 2 |
+
.dash-slider input,
|
| 3 |
+
.rc-slider input,
|
| 4 |
+
[class*="slider"] input[type="number"],
|
| 5 |
+
[class*="Slider"] input[type="number"] {
|
| 6 |
+
min-width: 64px !important;
|
| 7 |
+
width: 64px !important;
|
| 8 |
+
text-align: center !important;
|
| 9 |
+
box-sizing: border-box !important;
|
| 10 |
+
}
|
| 11 |
+
|
| 12 |
+
.rc-slider-tooltip-inner {
|
| 13 |
+
min-width: 64px !important;
|
| 14 |
+
text-align: center !important;
|
| 15 |
+
}
|
assets/signalpha-banner.png
ADDED
|
Git LFS Details
|
data.csv
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Ticker,Name,ST,Total Assets ($M),Total Equity ($M),Equity to Assets,CRE Total ($M),CRE Total to Equity,Total Unrealized Loss ($M),CET1 Capital ($M),HTM Loss ($M),AFS Loss ($M),HTM+AFS Loss to CET1 Capital,Total Domestic Deposits ($M),Liquid Assets ($M),Uninsured Deposits ($M),Percent Uninsured,Uninsured Deposits Rank,Uninsured Deposits % of Liquid Assets
|
| 2 |
+
LOB,LIVE OAK BANKING COMPANY,NC,12856,970,7.5%,5634,580.6%,69.0,1100,0.0,69.0,6.2%,13427,2332,2190,16.3,135,93.9
|
| 3 |
+
DCOM,DIME COMMUNITY BANK,NY,14337,1579,11.0%,8982,569.0%,86.0,1520,63.0,24.0,5.7%,12152,3020,6211,51.1,32,205.6
|
| 4 |
+
EAGL,EAGLEBANK,MD,11080,1231,11.1%,6852,556.7%,173.0,1236,84.0,88.0,14.0%,9473,2834,2310,24.4,122,81.5
|
| 5 |
+
FLG,"FLAGSTAR BANK, NATIONAL ASSOCI",NY,100102,8579,8.6%,46444,541.4%,648.0,8832,0.0,648.0,7.3%,69628,24183,18450,26.5,119,76.3
|
| 6 |
+
OZK,BANK OZK,AR,38259,5706,14.9%,30619,536.6%,48.0,5124,0.0,48.0,0.9%,33985,5928,12012,35.3,95,202.6
|
| 7 |
+
SFBS,SERVISFIRST BANK,AL,17351,1676,9.7%,8830,526.9%,57.0,1831,49.0,8.0,3.1%,14131,3193,9459,66.9,8,296.2
|
| 8 |
+
,MERCHANTS BANK OF INDIANA,IN,18622,2074,11.1%,10592,510.7%,1.0,2171,0.0,-0.0,-0.0%,13998,2563,3736,26.7,118,145.8
|
| 9 |
+
PFS,PROVIDENT BANK,NJ,24008,2893,12.1%,14620,505.3%,126.0,2453,8.0,119.0,5.1%,19153,3855,10368,54.1,21,268.9
|
| 10 |
+
FFWM,FIRST FOUNDATION BANK,CA,12612,1189,9.4%,5627,473.4%,52.0,1066,50.0,2.0,4.9%,9321,3931,1901,20.4,129,48.3
|
| 11 |
+
,APPLE BANK,NY,17460,1503,8.6%,6867,457.0%,202.0,1478,50.0,152.0,13.6%,15406,4643,3562,23.1,125,76.7
|
| 12 |
+
,FIRST UNITED BANK AND TRUST CO,OK,16260,1427,8.8%,6465,453.2%,77.0,1479,0.0,77.0,5.2%,12808,1979,4194,32.7,103,211.9
|
| 13 |
+
FMBL,FARMERS AND MERCHANTS BANK OF,CA,11690,1369,11.7%,6186,451.9%,263.0,1397,261.0,2.0,18.8%,8872,5181,4823,54.4,20,93.1
|
| 14 |
+
,FIRSTBANK,CO,27247,1554,5.7%,7015,451.6%,767.0,2443,33.0,734.0,31.4%,23275,10180,7087,30.4,109,69.6
|
| 15 |
+
COLB,COLUMBIA BANK,NJ,10472,1045,10.0%,4622,442.1%,115.0,1039,31.0,84.0,11.1%,8331,1817,3180,38.2,83,175.0
|
| 16 |
+
BCI.SN,CITY NATIONAL BANK OF FLORIDA,FL,26507,2522,9.5%,11090,439.7%,692.0,3031,244.0,447.0,22.8%,21955,7198,10611,48.3,40,147.4
|
| 17 |
+
DSCT.TA,ISRAEL DISCOUNT BANK OF NEW YO,NY,13856,1311,9.5%,5655,431.4%,203.0,1488,97.0,105.0,13.6%,12409,3590,6221,50.1,36,173.3
|
| 18 |
+
ZION,"ZIONS BANCORPORATION, NATIONAL",UT,88775,6124,6.9%,25423,415.2%,1208.0,7734,0.0,1208.0,15.6%,75105,22400,34582,46.0,48,154.4
|
| 19 |
+
HOPE,BANK OF HOPE,CA,17049,2213,13.0%,9069,409.8%,207.0,1945,14.0,193.0,10.7%,15849,2914,6226,39.3,79,213.6
|
| 20 |
+
PNFP,SYNOVUS BANK,GA,60120,5178,8.6%,21211,409.7%,287.0,5957,0.0,287.0,4.8%,50536,12501,25737,50.9,35,205.9
|
| 21 |
+
BANR,BANNER BANK,WA,16211,1813,11.2%,7233,398.9%,430.0,1770,156.0,274.0,24.3%,14076,3938,4490,31.9,105,114.0
|
| 22 |
+
,WASHINGTON TRUST BANK,WA,10728,905,8.4%,3572,394.9%,262.0,984,242.0,20.0,26.6%,8872,2987,5047,56.9,16,169.0
|
| 23 |
+
VLY,VALLEY NATIONAL BANK,NJ,62447,7884,12.6%,31133,394.9%,496.0,6202,391.0,105.0,8.0%,51320,9119,19897,38.8,81,218.2
|
| 24 |
+
BPOP,POPULAR BANK,NY,14332,1985,13.8%,7786,392.3%,47.0,1590,0.0,47.0,2.9%,12162,2669,3674,30.2,111,137.6
|
| 25 |
+
,BELL BANK,ND,14534,1350,9.3%,5235,387.9%,6.0,1412,0.0,6.0,0.4%,12046,1176,2980,24.7,121,253.3
|
| 26 |
+
BANC,BANC OF CALIFORNIA,CA,33458,3919,11.7%,15123,385.9%,288.0,3485,80.0,208.0,8.3%,27339,7333,8081,29.6,113,110.2
|
| 27 |
+
OCFC,"OCEANFIRST BANK, NATIONAL ASSO",NJ,13332,1697,12.7%,6332,373.2%,75.0,1172,64.0,11.0,6.4%,10560,2459,6367,60.3,13,259.0
|
| 28 |
+
EFSC,ENTERPRISE BANK & TRUST,MO,15568,1825,11.7%,6775,371.3%,152.0,1658,50.0,102.0,9.2%,13714,4006,4233,30.9,108,105.7
|
| 29 |
+
,ARVEST BANK,AR,26895,1981,7.4%,7342,370.7%,913.0,2462,0.0,913.0,37.1%,24028,6878,6220,25.9,120,90.4
|
| 30 |
+
WAFD,WASHINGTON FEDERAL BANK,WA,27671,3007,10.9%,11118,369.7%,58.0,2506,33.0,25.0,2.3%,21585,4861,6454,29.9,112,132.8
|
| 31 |
+
UMBF,"UMB BANK, NATIONAL ASSOCIATION",MO,50150,3463,6.9%,12210,352.6%,903.0,5399,535.0,369.0,16.7%,60458,28343,39998,66.2,9,141.1
|
| 32 |
+
AX,AXOS BANK,CA,22841,2390,10.5%,8262,345.7%,0.0,2353,0.0,-0.0,-0.0%,22618,3765,3644,16.1,136,96.8
|
| 33 |
+
TOWN,TOWNEBANK,VA,17248,2139,12.4%,7338,343.0%,93.0,1769,3.0,89.0,5.2%,16627,4170,7695,46.3,44,184.5
|
| 34 |
+
FULT,"FULTON BANK, NATIONAL ASSOCIAT",PA,31916,3309,10.4%,11341,342.7%,419.0,3086,166.0,253.0,13.6%,26576,6087,10813,40.7,71,177.6
|
| 35 |
+
RNST,FIRST BANK,NC,12148,1521,12.5%,5200,341.8%,323.0,1385,72.0,252.0,23.4%,10901,3532,4387,40.2,73,124.2
|
| 36 |
+
CADE,STELLAR BANK,TX,10891,1608,14.8%,5429,337.6%,99.0,1143,0.0,99.0,8.6%,8887,2666,5021,56.5,17,188.3
|
| 37 |
+
AUB,ATLANTIC UNION BANK,VA,24469,3475,14.2%,11728,337.5%,363.0,3882,36.0,328.0,9.4%,30708,6265,10946,35.6,92,174.7
|
| 38 |
+
CMA,COMERICA BANK,TX,79332,6133,7.7%,20648,336.7%,2187.0,8366,0.0,2187.0,26.1%,63812,22051,34857,54.6,19,158.1
|
| 39 |
+
CATY,CATHAY BANK,CA,23036,2899,12.6%,9644,332.7%,85.0,2650,0.0,85.0,3.2%,20288,3036,9959,49.1,37,328.0
|
| 40 |
+
GBCI,GLACIER BANK,MT,27886,3178,11.4%,10567,332.5%,526.0,2638,266.0,260.0,19.9%,22062,8186,7134,32.3,104,87.2
|
| 41 |
+
,FIRSTBANK,TN,13119,1527,11.6%,5011,328.1%,56.0,1688,0.0,56.0,3.3%,13836,2479,5756,41.6,67,232.2
|
| 42 |
+
,"CENTRAL TRUST BANK, THE",MO,19230,1728,9.0%,5669,328.1%,71.0,1652,0.0,71.0,4.3%,15165,7068,5310,35.0,96,75.1
|
| 43 |
+
FIBK,FIRST INTERSTATE BANK,MT,29066,3264,11.2%,10661,326.6%,490.0,2498,226.0,264.0,19.6%,22914,9020,7887,34.4,97,87.4
|
| 44 |
+
VBTX,VERITEX COMMUNITY BANK,TX,12718,1756,13.8%,5714,325.5%,61.0,1368,20.0,41.0,4.4%,10815,2341,4927,45.6,50,210.5
|
| 45 |
+
HTH,PLAINSCAPITAL BANK,TX,13329,1469,11.0%,4745,322.9%,130.0,1323,58.0,71.0,9.8%,10747,3410,5726,53.3,24,167.9
|
| 46 |
+
SFNC,SIMMONS BANK,AR,26843,3570,13.3%,11504,322.2%,469.0,2030,0.0,469.0,23.1%,20742,4432,9566,46.1,46,215.8
|
| 47 |
+
CUBI,CUSTOMERS BANK,PA,22296,1826,8.2%,5757,315.2%,110.0,2122,44.0,65.0,5.2%,20622,7052,8700,42.2,64,123.4
|
| 48 |
+
CVBF,CITIZENS BUSINESS BANK,CA,15161,2163,14.3%,6742,311.7%,685.0,1743,362.0,323.0,39.3%,12175,5996,7417,60.9,11,123.7
|
| 49 |
+
ONB,OLD NATIONAL BANK,IN,53245,6159,11.6%,18637,302.6%,1118.0,5898,392.0,726.0,19.0%,55525,16720,26765,48.2,41,160.1
|
| 50 |
+
WSFS,WILMINGTON SAVINGS FUND SOCIET,DE,20722,2517,12.1%,7585,301.4%,621.0,2180,94.0,527.0,28.5%,17586,6575,6966,39.6,76,106.0
|
| 51 |
+
SSB,"SOUTHSTATE BANK, N.A.",FL,46370,6161,13.3%,18497,300.2%,757.0,6422,331.0,426.0,11.8%,54186,11695,20915,38.6,82,178.8
|
| 52 |
+
EWBC,EAST WEST BANK,CA,75842,7251,9.6%,21750,300.0%,861.0,7908,413.0,448.0,10.9%,62886,20372,33373,53.1,26,163.8
|
| 53 |
+
BOH,BANK OF HAWAII,HI,23568,1540,6.5%,4604,298.9%,809.0,1952,622.0,187.0,41.4%,19784,8790,10092,51.0,34,114.8
|
| 54 |
+
FHN,FIRST HORIZON BANK,TN,81866,8782,10.7%,26013,296.2%,886.0,8093,150.0,736.0,10.9%,66112,12208,27225,41.2,69,223.0
|
| 55 |
+
CFR,FROST BANK,Tx,52581,3822,7.3%,11224,293.7%,1311.0,4634,166.0,1144.0,28.3%,42943,29389,22028,51.3,31,75.0
|
| 56 |
+
UCB,UNITED COMMUNITY BANK,SC,27657,3282,11.9%,9615,293.0%,492.0,2583,337.0,155.0,19.0%,24413,6923,9853,40.4,72,142.3
|
| 57 |
+
INDB,ROCKLAND TRUST COMPANY,MA,19379,2966,15.3%,8619,290.6%,154.0,2683,97.0,57.0,5.7%,20418,4268,6740,33.0,101,157.9
|
| 58 |
+
PNFP,PINNACLE BANK,TN,52489,6517,12.4%,18840,289.1%,402.0,5246,214.0,188.0,7.7%,45988,12502,19559,42.5,61,156.4
|
| 59 |
+
RNST,RENASANT BANK,MS,18037,2695,14.9%,7783,288.8%,162.0,2526,75.0,87.0,6.4%,21791,4712,10024,46.0,49,212.7
|
| 60 |
+
WSBC,"WESBANCO BANK, INC.",WV,18649,2729,14.6%,7861,288.1%,310.0,2507,108.0,202.0,12.4%,21631,5626,7093,32.8,102,126.1
|
| 61 |
+
ABCB,AMERIS BANK,GA,26175,3790,14.5%,10821,285.5%,15.0,3001,15.0,0.0,0.5%,22432,3366,10371,46.2,45,308.1
|
| 62 |
+
FCF,FIRST COMMONWEALTH BANK,PA,11554,1370,11.9%,3909,285.3%,138.0,1187,52.0,87.0,11.7%,10317,1780,2988,29.0,115,167.8
|
| 63 |
+
RJF,TRISTATE CAPITAL BANK,PA,20969,1396,6.7%,3913,280.2%,138.0,1661,0.0,138.0,8.3%,20515,5417,7724,37.6,85,142.6
|
| 64 |
+
UBSI,UNITED BANK,VA,29925,5025,16.8%,14005,278.7%,224.0,3600,0.0,224.0,6.2%,27082,5757,10602,39.1,80,184.2
|
| 65 |
+
NBTB,"NBT BANK, NATIONAL ASSOCIATION",NY,13683,1488,10.9%,4116,276.6%,174.0,1429,65.0,109.0,12.2%,13791,3330,5959,43.2,59,178.9
|
| 66 |
+
WAL,WESTERN ALLIANCE BANK,AZ,80862,6903,8.5%,19011,275.4%,638.0,7319,165.0,473.0,8.7%,77520,24964,24392,31.5,107,97.7
|
| 67 |
+
CADE,CADENCE BANK,MS,47019,5570,11.8%,15327,275.2%,593.0,4772,0.0,593.0,12.4%,43921,12097,16397,37.3,86,135.5
|
| 68 |
+
CBU,"COMMUNITY BANK, NATIONAL ASSOC",NY,16130,1365,8.5%,3687,270.1%,380.0,1305,81.0,298.0,29.1%,14216,4817,4743,33.4,100,98.5
|
| 69 |
+
BKU,"BANKUNITED, NATIONAL ASSOCIATI",FL,35207,3233,9.2%,8720,269.7%,290.0,3324,0.0,290.0,8.7%,28878,10308,14940,51.7,28,144.9
|
| 70 |
+
BANF,BANCFIRST,OK,11405,1208,10.6%,3223,266.7%,15.0,1239,0.0,15.0,1.2%,10366,4399,2881,27.8,116,65.5
|
| 71 |
+
ASB,"ASSOCIATED BANK, NATIONAL ASSO",WI,42966,4453,10.4%,11698,262.7%,454.0,3602,454.0,0.0,12.6%,34945,10123,16098,46.1,47,159.0
|
| 72 |
+
BOKF,"BOKF, NATIONAL ASSOCIATION",OK,49466,5130,10.4%,13455,262.3%,339.0,4728,136.0,204.0,7.2%,38775,16986,20781,53.6,23,122.3
|
| 73 |
+
,MIDFIRST BANK,OK,39087,3849,9.8%,10077,261.8%,163.0,3628,12.0,152.0,4.5%,25291,6806,5782,22.9,126,84.9
|
| 74 |
+
EBC,EASTERN BANK,MA,25552,3418,13.4%,8838,258.6%,372.0,2768,28.0,345.0,13.5%,21387,5073,8911,41.7,66,175.6
|
| 75 |
+
BUSE,BUSEY BANK,IL,12011,1584,13.2%,4080,257.6%,278.0,2118,125.0,152.0,13.1%,15173,3422,6452,42.5,62,188.5
|
| 76 |
+
WBS,"WEBSTER BANK, NATIONAL ASSOCIA",CT,78957,9378,11.9%,24068,256.7%,1334.0,7162,837.0,497.0,18.6%,68697,21534,24485,35.6,93,113.7
|
| 77 |
+
SBCF,SEACOAST NATIONAL BANK,FL,15167,2181,14.4%,5583,255.9%,220.0,1649,100.0,120.0,13.4%,13097,4263,4969,37.9,84,116.6
|
| 78 |
+
,"DOLLAR BANK, FEDERAL SAVINGS B",PA,11640,1150,9.9%,2937,255.3%,154.0,1332,0.0,154.0,11.6%,10068,2235,3049,30.3,110,136.4
|
| 79 |
+
HOMB,CENTENNIAL BANK,AR,22418,3779,16.9%,9581,253.5%,346.0,2705,107.0,239.0,12.8%,17701,5225,8526,48.2,42,163.2
|
| 80 |
+
FHB,FIRST HAWAIIAN BANK,HI,23831,2604,10.9%,5969,229.2%,576.0,2113,384.0,191.0,27.2%,18607,7628,10429,56.0,18,136.7
|
| 81 |
+
CBSH,COMMERCE BANK,MO,31808,2698,8.5%,6151,228.0%,688.0,3554,0.0,688.0,19.4%,25833,12646,10220,39.6,77,80.8
|
| 82 |
+
,FIRST NATIONAL BANK OF OMAHA,NE,31986,3116,9.7%,7086,227.4%,196.0,3584,12.0,183.0,5.5%,26119,7582,8912,34.1,98,117.5
|
| 83 |
+
HWC,HANCOCK WHITNEY BANK,MS,35068,3995,11.4%,9046,226.4%,543.0,3803,133.0,410.0,14.3%,28930,9763,14200,49.1,38,145.4
|
| 84 |
+
UNH,"OPTUM BANK, INC.",UT,18649,2159,11.6%,4861,225.2%,472.0,2318,0.0,472.0,20.4%,15800,10497,319,2.0,151,3.0
|
| 85 |
+
FNB,FIRST NATIONAL BANK OF PENNSYL,PA,48447,6191,12.8%,13565,219.1%,281.0,4149,211.0,70.0,6.8%,38921,10149,15852,40.7,70,156.2
|
| 86 |
+
,MECHANICS BANK,CA,16494,2302,14.0%,5041,219.0%,183.0,2027,177.0,5.0,9.0%,19460,6304,6631,34.1,99,105.2
|
| 87 |
+
FFBC,FIRST FINANCIAL BANK,OH,18486,2413,13.1%,5023,208.2%,253.0,1791,6.0,247.0,14.1%,14610,4481,6073,41.6,68,135.5
|
| 88 |
+
FBP,FIRSTBANK PUERTO RICO,PR,19286,1694,8.8%,3511,207.3%,390.0,2070,4.0,386.0,18.8%,16660,6157,8830,53.0,27,143.4
|
| 89 |
+
FFIN,FIRST FINANCIAL BANK,TX,13925,1468,10.5%,3029,206.3%,389.0,1680,0.0,389.0,23.2%,12990,6242,5683,43.7,57,91.0
|
| 90 |
+
FRME,FIRST MERCHANTS BANK,IN,18299,2316,12.7%,4757,205.4%,486.0,1835,289.0,197.0,26.5%,14923,3836,6626,44.4,56,172.7
|
| 91 |
+
TCBI,TEXAS CAPITAL BANK,TX,30622,3599,11.8%,7271,202.0%,151.0,3510,81.0,70.0,4.3%,27943,7476,11821,42.3,63,158.1
|
| 92 |
+
,"EVERBANK, NATIONAL ASSOCIATION",FL,40802,3695,9.1%,7441,201.4%,178.0,3985,2.0,176.0,4.5%,35744,10732,4784,13.4,137,44.6
|
| 93 |
+
NWBI,NORTHWEST BANK,PA,14463,1591,11.0%,3159,198.5%,219.0,1526,84.0,135.0,14.3%,13934,2412,3747,26.9,117,155.3
|
| 94 |
+
FCNCA,FIRST-CITIZENS BANK & TRUST CO,NC,223543,21932,9.8%,43278,197.3%,1436.0,21876,1212.0,224.0,6.6%,163581,70619,59746,36.5,91,84.6
|
| 95 |
+
SF,STIFEL BANK AND TRUST,MO,19510,1364,7.0%,2560,187.7%,57.0,827,0.0,57.0,6.9%,11126,4477,2011,18.1,132,44.9
|
| 96 |
+
CM,CIBC BANK USA,IL,61517,10123,16.5%,18840,186.1%,66.0,8082,63.0,3.0,0.8%,48303,19680,31753,65.7,10,161.4
|
| 97 |
+
BPOP,BANCO POPULAR DE PUERTO RICO,PR,58456,3178,5.4%,5820,183.1%,883.0,4528,0.0,883.0,19.5%,54539,31554,31561,57.9,14,100.0
|
| 98 |
+
SAN,"SANTANDER BANK, NATIONAL ASSOC",DE,102701,12645,12.3%,22267,176.1%,1973.0,12678,1282.0,691.0,15.6%,74411,34167,26398,35.5,94,77.3
|
| 99 |
+
RY,CITY NATIONAL BANK,CA,92193,11372,12.3%,16504,145.1%,485.0,10683,485.0,0.0,4.5%,80218,26024,48538,60.5,12,186.5
|
| 100 |
+
RJF,RAYMOND JAMES BANK,FL,41241,2940,7.1%,4129,140.4%,456.0,3434,0.0,456.0,13.3%,38898,7512,3292,8.5,142,43.8
|
| 101 |
+
MTB,MANUFACTURERS AND TRADERS TRUS,NY,207556,27568,13.3%,38010,137.9%,789.0,19464,789.0,0.0,4.1%,167627,54332,81823,48.8,39,150.6
|
| 102 |
+
,NEXBANK,TX,13943,1328,9.5%,1826,137.4%,133.0,1299,0.0,133.0,10.2%,11256,4160,1278,11.4,139,30.7
|
| 103 |
+
CFG,"CITIZENS BANK, NATIONAL ASSOCI",RI,217179,24389,11.2%,32376,132.7%,2235.0,20823,829.0,1406.0,10.7%,183739,57084,82117,44.7,52,143.9
|
| 104 |
+
PB,PROSPERITY BANK,TX,39595,7419,18.7%,9173,123.6%,920.0,3881,918.0,2.0,23.7%,28161,12025,11309,40.2,74,94.0
|
| 105 |
+
OFG,ORIENTAL BANK,PR,11416,1189,10.4%,1413,118.9%,78.0,1239,47.0,31.0,6.3%,10015,3567,5321,53.1,25,149.2
|
| 106 |
+
KEY,KEYBANK NATIONAL ASSOCIATION,OH,184461,16770,9.1%,19627,117.0%,2894.0,18240,345.0,2549.0,15.9%,156170,65750,79754,51.1,33,121.3
|
| 107 |
+
HBAN,"HUNTINGTON NATIONAL BANK, THE",OH,203428,19701,9.7%,20400,103.5%,4084.0,17546,1727.0,2357.0,23.3%,169742,57728,53686,31.6,106,93.0
|
| 108 |
+
RF,REGIONS BANK,AL,155918,16174,10.4%,16085,99.5%,817.0,14575,53.0,764.0,5.6%,131484,45864,51982,39.5,78,113.3
|
| 109 |
+
FITB,"FIFTH THIRD BANK, NATIONAL ASS",OH,212197,22064,10.4%,20046,90.9%,3155.0,21470,0.0,3155.0,14.7%,169563,70310,75520,44.5,54,107.4
|
| 110 |
+
TFC,TRUIST BANK,NC,523132,60832,11.6%,54957,90.3%,13069.0,51130,8355.0,4714.0,25.6%,404056,154730,172826,42.8,60,111.7
|
| 111 |
+
PNC,"PNC BANK, NATIONAL ASSOCIATION",DE,556139,53455,9.6%,46773,87.5%,4737.0,51050,2552.0,2185.0,9.3%,439115,182489,198026,45.1,51,108.5
|
| 112 |
+
BMO,BMO BANK NATIONAL ASSOCIATION,IL,263653,34698,13.2%,30108,86.8%,1947.0,26463,77.0,1869.0,7.4%,194762,87537,100702,51.7,30,115.0
|
| 113 |
+
,SAFRA NATIONAL BANK OF NEW YOR,NY,11305,1254,11.1%,1017,81.1%,144.0,1078,0.0,144.0,13.4%,10549,8139,4211,39.9,75,51.7
|
| 114 |
+
WFC,"WELLS FARGO BANK, NATIONAL ASS",SD,1705538,166189,9.7%,133676,80.4%,36886.0,150610,33819.0,3067.0,24.5%,1418394,589946,663483,46.8,43,112.5
|
| 115 |
+
USB,U.S. BANK NATIONAL ASSOCIATION,OH,662906,63537,9.6%,49057,77.2%,14588.0,61013,9585.0,5003.0,23.9%,530919,236169,274656,51.7,29,116.3
|
| 116 |
+
TD,"TD BANK, N.A.",DE,372778,46599,12.5%,34490,74.0%,1160.0,37770,0.0,1160.0,3.1%,287103,159622,105014,36.6,90,65.8
|
| 117 |
+
NTRS,"NORTHERN TRUST COMPANY, THE",IL,154948,10798,7.0%,6791,62.9%,1221.0,10438,1136.0,85.0,11.7%,50955,112653,39699,77.9,5,35.2
|
| 118 |
+
JPM,"JPMORGAN CHASE BANK, NATIONAL",OH,3459261,312794,9.0%,174381,55.7%,20414.0,291288,18610.0,1804.0,7.0%,2116593,1092333,1223904,57.8,15,112.0
|
| 119 |
+
COF,"CAPITAL ONE, NATIONAL ASSOCIAT",VA,487193,55895,11.5%,28617,51.2%,7235.0,67633,0.0,7235.0,10.7%,498827,145919,107420,21.5,128,73.6
|
| 120 |
+
DB,DEUTSCHE BANK TRUST COMPANY AM,NY,39780,9671,24.3%,4632,47.9%,24.0,9665,0.0,24.0,0.2%,28141,16381,26328,93.6,3,160.7
|
| 121 |
+
ALLY,ALLY BANK,UT,181409,13776,7.6%,6577,47.7%,2773.0,17773,0.0,2773.0,15.6%,152626,35014,16930,11.1,141,48.4
|
| 122 |
+
MS,"MORGAN STANLEY BANK, N.A.",UT,230712,22090,9.6%,10192,46.1%,4275.0,25948,2704.0,1571.0,16.5%,201602,102287,40231,20.0,130,39.3
|
| 123 |
+
,BEAL BANK USA,NV,16216,3326,20.5%,1405,42.2%,0.0,3072,0.0,-0.0,-0.0%,5075,12104,3707,73.0,6,30.6
|
| 124 |
+
MS,"MORGAN STANLEY PRIVATE BANK, N",NY,221306,17084,7.7%,7165,41.9%,4853.0,17613,4342.0,511.0,27.6%,213792,69951,52133,24.4,123,74.5
|
| 125 |
+
LC,"LENDINGCLUB BANK, NATIONAL ASS",UT,10469,1175,11.2%,467,39.7%,28.0,1136,0.0,28.0,2.4%,9518,4577,1156,12.1,138,25.3
|
| 126 |
+
BAC,"BANK OF AMERICA, NATIONAL ASSO",NC,2589060,245482,9.5%,82102,33.4%,87273.0,196596,84904.0,2369.0,44.4%,1956667,1137275,869263,44.4,55,76.4
|
| 127 |
+
HSBC,"HSBC BANK USA, NATIONAL ASSOCI",VA,164820,15734,9.5%,4194,26.7%,2048.0,16020,235.0,1813.0,12.8%,129353,75108,87392,67.6,7,116.4
|
| 128 |
+
UBS,UBS BANK USA,UT,119112,9539,8.0%,2268,23.8%,295.0,9976,295.0,0.0,3.0%,99606,26642,29245,29.4,114,109.8
|
| 129 |
+
GS,GOLDMAN SACHS BANK USA,NY,558235,63033,11.3%,14019,22.2%,0.0,64251,0.0,-0.0,-0.0%,405207,205281,169605,41.9,65,82.6
|
| 130 |
+
BK,"BANK OF NEW YORK MELLON, THE",NY,335955,27960,8.3%,5806,20.8%,3699.0,22269,3025.0,674.0,16.6%,206711,267243,193864,93.8,2,72.5
|
| 131 |
+
BK,"BNY MELLON, NATIONAL ASSOCIATI",PA,30536,3593,11.8%,724,20.2%,11.0,1837,0.0,11.0,0.6%,26503,8177,14237,53.7,22,174.1
|
| 132 |
+
C,"CITIBANK, N.A.",SD,1696818,170939,10.1%,29115,17.0%,12214.0,158744,11586.0,628.0,7.7%,798104,742961,653192,81.8,4,87.9
|
| 133 |
+
STT,STATE STREET BANK AND TRUST CO,MA,348989,27504,7.9%,2869,10.4%,4280.0,20352,4280.0,0.0,21.0%,207154,236610,203205,98.1,1,85.9
|
| 134 |
+
SF,STIFEL BANK,MO,10790,704,6.5%,57,8.0%,57.0,827,0.0,57.0,6.9%,11126,4477,2011,18.1,132,44.9
|
| 135 |
+
TD,"TD BANK USA, NATIONAL ASSOCIAT",DE,35511,4303,12.1%,216,5.0%,0.0,4436,0.0,-0.0,-0.0%,28465,24462,336,1.2,154,1.4
|
| 136 |
+
SOFI,"SOFI BANK, NATIONAL ASSOCIATIO",UT,31088,4535,14.6%,143,3.2%,0.0,5575,0.0,-0.0,-0.0%,33768,5896,1546,4.6,148,26.2
|
| 137 |
+
BMWKY,BMW BANK OF NORTH AMERICA,UT,12243,1839,15.0%,42,2.3%,28.0,1833,0.0,28.0,1.5%,8069,3018,325,4.0,149,10.8
|
| 138 |
+
SLM,SALLIE MAE BANK,UT,30036,2682,8.9%,50,1.9%,65.0,3063,0.0,65.0,2.1%,20228,5558,1200,5.9,145,21.6
|
| 139 |
+
SYF,SYNCHRONY BANK,UT,111939,13983,12.5%,43,0.3%,8.0,13932,0.0,8.0,0.1%,82569,18289,9235,11.2,140,50.5
|
| 140 |
+
,THIRD FEDERAL SAVINGS & LOAN A,OH,17047,1719,10.1%,3,0.1%,21.0,1760,0.0,21.0,1.2%,10580,966,387,3.7,150,40.1
|
| 141 |
+
BCS,BARCLAYS BANK DELAWARE,DE,44672,5900,13.2%,8,0.1%,17.0,5242,0.0,17.0,0.3%,37033,9173,2176,5.9,146,23.7
|
| 142 |
+
,USAA FEDERAL SAVINGS BANK,AZ,109221,3918,3.6%,1,0.0%,3995.0,9166,2778.0,1217.0,43.6%,90797,60313,5481,6.0,144,9.1
|
| 143 |
+
AXP,AMERICAN EXPRESS NATIONAL BANK,UT,193062,18550,9.6%,0,0.0%,8.0,18166,0.0,8.0,-0.0%,168152,47971,37573,22.3,127,78.3
|
| 144 |
+
BAC,"BANK OF AMERICA CALIFORNIA, NA",CA,15691,1802,11.5%,0,0.0%,0.0,1862,0.0,-0.0,-0.0%,12625,4968,728,5.8,147,14.7
|
| 145 |
+
SCHW,"CHARLES SCHWAB PREMIER BANK, S",TX,26472,2159,8.2%,0,0.0%,675.0,3293,516.0,159.0,20.5%,22742,21138,371,1.6,152,1.8
|
| 146 |
+
SCHW,"CHARLES SCHWAB BANK, SSB",TX,275425,19700,7.2%,0,0.0%,11852.0,31514,7772.0,4080.0,37.6%,214260,189160,36451,17.0,134,19.3
|
| 147 |
+
BFH,COMENITY CAPITAL BANK,UT,13263,1855,14.0%,0,0.0%,10.0,1749,0.0,10.0,0.6%,9978,2255,621,6.2,143,27.5
|
| 148 |
+
AMP,"AMERIPRISE BANK, FSB",MN,23605,1137,4.8%,0,0.0%,376.0,1872,0.0,376.0,20.1%,22595,22714,326,1.4,153,1.4
|
documentation.md
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Default Cascade Simulation of Banks 2026
|
| 2 |
+
|
| 3 |
+
## Overview
|
| 4 |
+
|
| 5 |
+
Interactive Dash application that simulates the impact of commercial real estate (CRE) defaults on U.S. bank capitalization. The app classifies banks into regulatory categories under two frameworks — Prompt Corrective Action (PCA) based on Tangible Equity Ratio, and Basel III based on Estimated CET1 Ratio — and displays the results as stacked bar charts with summary metric cards.
|
| 6 |
+
|
| 7 |
+
## How It Works
|
| 8 |
+
|
| 9 |
+
### CRE Loss Calculation
|
| 10 |
+
|
| 11 |
+
Each bank's CRE losses are computed as:
|
| 12 |
+
|
| 13 |
+
- `CRE Losses = CRE Total x Default Rate x LGD`
|
| 14 |
+
|
| 15 |
+
When the Unrealized Losses toggle is ON, total losses also incorporate unrealized securities losses (positive values in the data, representing the magnitude of losses).
|
| 16 |
+
|
| 17 |
+
### Tangible Equity Ratio (PCA Framework)
|
| 18 |
+
|
| 19 |
+
Banks are classified by Adjusted Equity-to-Assets:
|
| 20 |
+
|
| 21 |
+
- `Adjusted Equity-to-Assets = (Total Equity - CRE Losses - Unrealized Losses) / Total Assets`
|
| 22 |
+
|
| 23 |
+
PCA categories:
|
| 24 |
+
|
| 25 |
+
| Category | Threshold |
|
| 26 |
+
|----------|-----------|
|
| 27 |
+
| Well Capitalized | >= 8% |
|
| 28 |
+
| Adequately Capitalized | >= 4% |
|
| 29 |
+
| Undercapitalized | >= 2% |
|
| 30 |
+
| Critically Undercapitalized | >= 0% |
|
| 31 |
+
| Insolvent | < 0% |
|
| 32 |
+
|
| 33 |
+
### Estimated CET1 Ratio (Basel III Framework)
|
| 34 |
+
|
| 35 |
+
Banks are also classified by Adjusted CET1 Ratio using an estimated Risk-Weighted Assets figure:
|
| 36 |
+
|
| 37 |
+
- `Estimated RWA = Total Assets x RWA Ratio`
|
| 38 |
+
- `Adjusted CET1 = (CET1 Capital - CRE Losses - Unrealized Losses) / Estimated RWA`
|
| 39 |
+
|
| 40 |
+
Basel III categories:
|
| 41 |
+
|
| 42 |
+
| Category | Threshold |
|
| 43 |
+
|----------|-----------|
|
| 44 |
+
| Well Capitalized | >= 6.5% |
|
| 45 |
+
| Undercapitalized | >= 4.5% |
|
| 46 |
+
| Below Basel III Minimum | >= 0% |
|
| 47 |
+
| Insolvent | < 0% |
|
| 48 |
+
|
| 49 |
+
## Controls
|
| 50 |
+
|
| 51 |
+
| Control | Range | Default | Description |
|
| 52 |
+
|---------|-------|---------|-------------|
|
| 53 |
+
| CRE Default Rate | 0-50% | 10% | Percentage of CRE portfolio that defaults |
|
| 54 |
+
| Loss Given Default | 0-100% | 50% | Loss severity on defaulted loans |
|
| 55 |
+
| Average RWA Ratio | 50-75% | 65% | Assumed ratio of risk-weighted assets to total assets |
|
| 56 |
+
| Total Assets | $10-100B | $10B | Minimum bank size filter |
|
| 57 |
+
| Unrealized Losses | ON/OFF | OFF | Include unrealized securities losses in stress calculation |
|
| 58 |
+
| Has CRE Losses | ON/OFF | OFF | Exclude banks with zero CRE exposure |
|
| 59 |
+
| Publicly Traded Only | ON/OFF | OFF | Exclude non-publicly traded banks |
|
| 60 |
+
|
| 61 |
+
## Display
|
| 62 |
+
|
| 63 |
+
### Stacked Bar Charts
|
| 64 |
+
|
| 65 |
+
Two horizontal stacked bar charts show the count of banks in each capitalization category:
|
| 66 |
+
|
| 67 |
+
- **Tangible Equity Ratio** — Five-shade red bar (PCA categories)
|
| 68 |
+
- **Estimated CET1 Ratio** — Four-shade purple bar (Basel III categories)
|
| 69 |
+
|
| 70 |
+
### Metric Cards
|
| 71 |
+
|
| 72 |
+
Each framework displays three summary cards:
|
| 73 |
+
|
| 74 |
+
| Metric | Description |
|
| 75 |
+
|--------|-------------|
|
| 76 |
+
| Total Losses | Aggregate CRE losses (plus unrealized if toggled) across all filtered banks, in $B |
|
| 77 |
+
| Banks Undercapitalized / Below Minimum | Count of banks falling below the adequately capitalized threshold, with their combined total assets |
|
| 78 |
+
| Banks Insolvent | Count of banks with negative adjusted ratios, with their combined total assets |
|
| 79 |
+
|
| 80 |
+
## Data Source
|
| 81 |
+
|
| 82 |
+
[The Banking Initiative at Florida Atlantic University](https://business.fau.edu/departments/finance/banking-initiative/) — Q3 2025 FFIEC Call Reports.
|
| 83 |
+
|
| 84 |
+
## Run Locally
|
| 85 |
+
|
| 86 |
+
```bash
|
| 87 |
+
pip install -r requirements.txt
|
| 88 |
+
python app.py
|
| 89 |
+
# Open http://localhost:8050
|
| 90 |
+
```
|
| 91 |
+
|
| 92 |
+
## Customization
|
| 93 |
+
|
| 94 |
+
You can use this app with your own data by replacing the `data.csv` file. Your CSV must include the following columns in the same format:
|
| 95 |
+
|
| 96 |
+
| Column | Description | Example |
|
| 97 |
+
|--------|-------------|---------|
|
| 98 |
+
| `Ticker` | Stock ticker symbol (blank for non-public banks) | `JPM` |
|
| 99 |
+
| `Name` | Bank name | `JPMORGAN CHASE BANK` |
|
| 100 |
+
| `ST` | State abbreviation | `NY` |
|
| 101 |
+
| `Total Assets ($M)` | Total assets in millions | `3459261` |
|
| 102 |
+
| `Total Equity ($M)` | Total equity in millions | `312794` |
|
| 103 |
+
| `CRE Total ($M)` | Commercial real estate exposure in millions | `174381` |
|
| 104 |
+
| `CET1 Capital ($M)` | Common Equity Tier 1 capital in millions | `291288` |
|
| 105 |
+
| `Total Unrealized Loss ($M)` | Unrealized loss on investment securities in millions (positive values) | `1842` |
|
| 106 |
+
| `Price` | Latest stock closing price in dollars | `293.70` |
|
| 107 |
+
| `Volume` | Average 30-day trading volume | `44276127` |
|
| 108 |
+
| `Volatility` | 30-day annualized volatility as percentage | `26.0` |
|
| 109 |
+
|
| 110 |
+
Only banks with a non-blank `Ticker` and valid `Price` and `Volatility` values will be included in the simulation. All other columns in the CSV are ignored by this app but may be used by companion apps in the Signalpha suite.
|
requirements.txt
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
dash>=2.17
|
| 2 |
+
plotly>=5.22
|
| 3 |
+
pandas>=2.0
|
| 4 |
+
gunicorn>=22.0
|