Update app.py
Browse files
app.py
CHANGED
|
@@ -201,7 +201,7 @@ def irr_sql(cols: List[str]) -> str:
|
|
| 201 |
# =========================
|
| 202 |
# Dashboard callback
|
| 203 |
# =========================
|
| 204 |
-
def run_dashboard(scenario: str) -> Tuple[str, str, str, str, str, Any, pd.DataFrame, pd.DataFrame, str, pd.DataFrame]:
|
| 205 |
"""
|
| 206 |
Returns:
|
| 207 |
status, as_of, a1_text, a2_text, a3_text, figure, ladder_df, irr_df,
|
|
@@ -213,14 +213,14 @@ def run_dashboard(scenario: str) -> Tuple[str, str, str, str, str, Any, pd.DataF
|
|
| 213 |
# --- Scenario Application ---
|
| 214 |
# Create a temporary view with scenario adjustments.
|
| 215 |
# Subsequent queries will use this stressed view.
|
| 216 |
-
stressed_view_fqn =
|
| 217 |
runoff_factor = 1.0
|
| 218 |
rate_shock_bps = 0.0
|
| 219 |
|
| 220 |
-
if scenario == "Liquidity Stress: High Deposit Runoff":
|
| 221 |
-
runoff_factor = 0
|
| 222 |
-
elif scenario == "IRR Stress: Rate Shock
|
| 223 |
-
rate_shock_bps =
|
| 224 |
|
| 225 |
scenario_sql = f"""
|
| 226 |
CREATE OR REPLACE TEMP VIEW {stressed_view_fqn} AS
|
|
@@ -311,7 +311,7 @@ def run_dashboard(scenario: str) -> Tuple[str, str, str, str, str, Any, pd.DataF
|
|
| 311 |
explain_text += "* **Seasonal Pattern:** Analysis not possible without relevant time-series features in the source data."
|
| 312 |
|
| 313 |
# Add scenario explanation for IRR stress
|
| 314 |
-
if scenario == "IRR Stress: Rate Shock
|
| 315 |
net_bpv = irr["BPV (DV01)"].sum()
|
| 316 |
eve_impact = net_bpv * rate_shock_bps
|
| 317 |
eve_impact_mn = eve_impact / 1_000_000
|
|
@@ -368,10 +368,22 @@ with gr.Blocks(title=APP_TITLE) as demo:
|
|
| 368 |
|
| 369 |
scenario_dd = gr.Dropdown(
|
| 370 |
label="Select Stress Scenario",
|
| 371 |
-
choices=["Baseline", "Liquidity Stress: High Deposit Runoff", "IRR Stress: Rate Shock
|
| 372 |
value="Baseline"
|
| 373 |
)
|
| 374 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 375 |
with gr.Row():
|
| 376 |
as_of = gr.Textbox(label="As of date", interactive=False)
|
| 377 |
|
|
@@ -396,7 +408,7 @@ with gr.Blocks(title=APP_TITLE) as demo:
|
|
| 396 |
|
| 397 |
refresh_btn.click(
|
| 398 |
fn=run_dashboard,
|
| 399 |
-
inputs=[scenario_dd],
|
| 400 |
outputs=[status, as_of, a1, a2, a3, chart, ladder_df, irr_df, explain_text, drivers_df],
|
| 401 |
)
|
| 402 |
|
|
|
|
| 201 |
# =========================
|
| 202 |
# Dashboard callback
|
| 203 |
# =========================
|
| 204 |
+
def run_dashboard(scenario: str, runoff_pct: float, rate_shock_bps_input: float) -> Tuple[str, str, str, str, str, Any, pd.DataFrame, pd.DataFrame, str, pd.DataFrame]:
|
| 205 |
"""
|
| 206 |
Returns:
|
| 207 |
status, as_of, a1_text, a2_text, a3_text, figure, ladder_df, irr_df,
|
|
|
|
| 213 |
# --- Scenario Application ---
|
| 214 |
# Create a temporary view with scenario adjustments.
|
| 215 |
# Subsequent queries will use this stressed view.
|
| 216 |
+
stressed_view_fqn = "positions_v_stressed"
|
| 217 |
runoff_factor = 1.0
|
| 218 |
rate_shock_bps = 0.0
|
| 219 |
|
| 220 |
+
if scenario == "Liquidity Stress: High Deposit Runoff" and runoff_pct > 0:
|
| 221 |
+
runoff_factor = (100.0 - runoff_pct) / 100.0
|
| 222 |
+
elif scenario == "IRR Stress: Rate Shock" and rate_shock_bps_input != 0:
|
| 223 |
+
rate_shock_bps = rate_shock_bps_input
|
| 224 |
|
| 225 |
scenario_sql = f"""
|
| 226 |
CREATE OR REPLACE TEMP VIEW {stressed_view_fqn} AS
|
|
|
|
| 311 |
explain_text += "* **Seasonal Pattern:** Analysis not possible without relevant time-series features in the source data."
|
| 312 |
|
| 313 |
# Add scenario explanation for IRR stress
|
| 314 |
+
if scenario == "IRR Stress: Rate Shock" and rate_shock_bps != 0 and not irr.empty:
|
| 315 |
net_bpv = irr["BPV (DV01)"].sum()
|
| 316 |
eve_impact = net_bpv * rate_shock_bps
|
| 317 |
eve_impact_mn = eve_impact / 1_000_000
|
|
|
|
| 368 |
|
| 369 |
scenario_dd = gr.Dropdown(
|
| 370 |
label="Select Stress Scenario",
|
| 371 |
+
choices=["Baseline", "Liquidity Stress: High Deposit Runoff", "IRR Stress: Rate Shock"],
|
| 372 |
value="Baseline"
|
| 373 |
)
|
| 374 |
|
| 375 |
+
with gr.Accordion("Stress Scenario Parameters", open=False):
|
| 376 |
+
runoff_slider = gr.Slider(
|
| 377 |
+
label="Deposit Runoff (%)",
|
| 378 |
+
minimum=0, maximum=100, step=1, value=20,
|
| 379 |
+
info="For Liquidity Stress: Percentage of key deposits that run off."
|
| 380 |
+
)
|
| 381 |
+
shock_slider = gr.Slider(
|
| 382 |
+
label="Rate Shock (bps)",
|
| 383 |
+
minimum=-500, maximum=500, step=25, value=200,
|
| 384 |
+
info="For IRR Stress: Parallel shift in the yield curve in basis points."
|
| 385 |
+
)
|
| 386 |
+
|
| 387 |
with gr.Row():
|
| 388 |
as_of = gr.Textbox(label="As of date", interactive=False)
|
| 389 |
|
|
|
|
| 408 |
|
| 409 |
refresh_btn.click(
|
| 410 |
fn=run_dashboard,
|
| 411 |
+
inputs=[scenario_dd, runoff_slider, shock_slider],
|
| 412 |
outputs=[status, as_of, a1, a2, a3, chart, ladder_df, irr_df, explain_text, drivers_df],
|
| 413 |
)
|
| 414 |
|