- {
- “cells”: [
- {
“cell_type”: “markdown”, “id”: “iq6umm6jyv”, “metadata”: {}, “source”: [
“# Scenario Stress Testingn”, “n”, “This notebook demonstrates the stress-testing and performance utilities:n”, “n”, “1. Build a long-only mean-variance tangency portfolio from weekly ETF returns.n”, “2. Generate a performance report with VaR/CVaR at 95% confidence.n”, “3. Apply exponential-decay stress (half-life weighting).n”, “4. Apply kernel-focus stress targeting high-volatility regimes.n”, “5. Combine a linear scenario shock with reweighted probabilities.”
]
}, {
“cell_type”: “code”, “execution_count”: 1, “id”: “upc389g2p19”, “metadata”: {
- “execution”: {
“iopub.execute_input”: “2026-03-30T19:38:52.625735Z”, “iopub.status.busy”: “2026-03-30T19:38:52.625391Z”, “iopub.status.idle”: “2026-03-30T19:38:53.122010Z”, “shell.execute_reply”: “2026-03-30T19:38:53.121706Z”
}
}, “outputs”: [], “source”: [
“import numpy as npn”, “import pandas as pdn”, “from pyvallocation import PortfolioWrappern”, “from pyvallocation.stress import (n”, “ exp_decay_stress,n”, “ kernel_focus_stress,n”, “ linear_map,n”, “ stress_test,n”, “)n”, “from pyvallocation.utils.performance import performance_report”
]
}, {
“cell_type”: “markdown”, “id”: “59gwvkzk8w4”, “metadata”: {}, “source”: [
“## Load weekly returns and build a tangency portfolio”
]
}, {
“cell_type”: “code”, “execution_count”: 2, “id”: “nooyhjl0rd”, “metadata”: {
- “execution”: {
“iopub.execute_input”: “2026-03-30T19:38:53.123562Z”, “iopub.status.busy”: “2026-03-30T19:38:53.123463Z”, “iopub.status.idle”: “2026-03-30T19:38:53.138627Z”, “shell.execute_reply”: “2026-03-30T19:38:53.138412Z”
}
}, “outputs”: [
- {
“name”: “stdout”, “output_type”: “stream”, “text”: [
“Assets : [‘DBC’, ‘GLD’, ‘SPY’, ‘TLT’]n”, “Weeks : 1006n”
]
}
], “source”: [
“from pathlib import Pathn”, “n”, “_candidates = [n”, “ Path("examples/ETF_prices.csv"),n”, “ Path("../examples/ETF_prices.csv"),n”, “ Path("../../examples/ETF_prices.csv"),n”, “ Path("../../../examples/ETF_prices.csv"),n”, “]n”, “_csv = next((p for p in _candidates if p.exists()), None)n”, “if _csv is None:n”, “ raise FileNotFoundError("ETF_prices.csv not found")n”, “prices = pd.read_csv(_csv, index_col="Date", parse_dates=True)n”, “prices = prices.dropna(how="all").ffill()n”, “weekly = prices.resample("W-FRI").last().dropna(how="all")n”, “returns = weekly.pct_change().dropna()n”, “returns = returns.rename(columns=lambda c: c.replace(" ", "_"))n”, “n”, “print(f"Assets : {list(returns.columns)}")n”, “print(f"Weeks : {len(returns)}")”
]
}, {
“cell_type”: “code”, “execution_count”: 3, “id”: “gyac04d04v”, “metadata”: {
- “execution”: {
“iopub.execute_input”: “2026-03-30T19:38:53.139737Z”, “iopub.status.busy”: “2026-03-30T19:38:53.139669Z”, “iopub.status.idle”: “2026-03-30T19:38:53.314028Z”, “shell.execute_reply”: “2026-03-30T19:38:53.313797Z”
}
}, “outputs”: [
- {
“name”: “stdout”, “output_type”: “stream”, “text”: [
“Tangency portfolio weights:n”, “DBC 0.0n”, “GLD 0.0n”, “SPY 1.0n”, “TLT 0.0n”, “Name: Tangency Portfolio (rf=1.00%), dtype: float64n”
]
}
], “source”: [
“wrapper = PortfolioWrapper.from_moments(returns.mean(), returns.cov())n”, “frontier = wrapper.variance_frontier(num_portfolios=25)n”, “w_series, _, _ = frontier.tangency(risk_free_rate=0.01)n”, “n”, “# Ensure weights are a 1-D numpy array for all downstream stress callsn”, “weights = w_series.values.ravel()n”, “n”, “print("Tangency portfolio weights:")n”, “print(w_series.round(4))”
]
}, {
“cell_type”: “markdown”, “id”: “y6lhil1yqj”, “metadata”: {}, “source”: [
“## Nominal performance reportn”, “n”, “performance_report computes mean, volatility, VaR, and CVaR at then”, “specified confidence level under uniform scenario probabilities.”
]
}, {
“cell_type”: “code”, “execution_count”: 4, “id”: “n34ioeqvoi”, “metadata”: {
- “execution”: {
“iopub.execute_input”: “2026-03-30T19:38:53.315158Z”, “iopub.status.busy”: “2026-03-30T19:38:53.315096Z”, “iopub.status.idle”: “2026-03-30T19:38:53.317550Z”, “shell.execute_reply”: “2026-03-30T19:38:53.317357Z”
}
}, “outputs”: [
- {
“name”: “stdout”, “output_type”: “stream”, “text”: [
“=== Nominal Performance ===n”, “mean 0.0022n”, “stdev 0.0251n”, “VaR95 0.0390n”, “CVaR95 0.0603n”, “ENS 1006.0000n”, “dtype: float64n”
]
}
], “source”: [
“report = performance_report(weights, returns.values, confidence=0.95)n”, “n”, “print("=== Nominal Performance ===")n”, “print(report.round(4))”
]
}, {
“cell_type”: “markdown”, “id”: “t8gnejeega”, “metadata”: {}, “source”: [
“## Exponential-decay stressn”, “n”, “Recent scenarios receive more weight with a 60-week half-life, simulatingn”, “a regime where recent market conditions persist.”
]
}, {
“cell_type”: “code”, “execution_count”: 5, “id”: “dys95jajcm4”, “metadata”: {
- “execution”: {
“iopub.execute_input”: “2026-03-30T19:38:53.318540Z”, “iopub.status.busy”: “2026-03-30T19:38:53.318479Z”, “iopub.status.idle”: “2026-03-30T19:38:53.322000Z”, “shell.execute_reply”: “2026-03-30T19:38:53.321824Z”
}
}, “outputs”: [
- {
“name”: “stdout”, “output_type”: “stream”, “text”: [
“=== Half-Life Stress (60 weeks) ===n”, “ return_nom stdev_nom VaR95_nom CVaR95_nom ENS_nom \n”, “portfolio_0 0.0022 0.0251 0.039 0.0603 1006.0 n”, “n”, “ return_stress stdev_stress VaR95_stress CVaR95_stress \n”, “portfolio_0 0.0027 0.0246 0.0307 0.0546 n”, “n”, “ ENS_stress KL_q_p n”, “portfolio_0 235.2738 1.453 n”
]
}
], “source”: [
“df_half_life = exp_decay_stress(n”, “ weights, returns.values, half_life=60n”, “)n”, “n”, “print("=== Half-Life Stress (60 weeks) ===")n”, “print(df_half_life.round(4))”
]
}, {
“cell_type”: “markdown”, “id”: “xparq3usnya”, “metadata”: {}, “source”: [
“## Kernel-focus stressn”, “n”, “Focus on scenarios where SPY rolling volatility is at its maximum,n”, “tilting probabilities toward high-volatility regimes.”
]
}, {
“cell_type”: “code”, “execution_count”: 6, “id”: “tupnrg6d6l”, “metadata”: {
- “execution”: {
“iopub.execute_input”: “2026-03-30T19:38:53.323038Z”, “iopub.status.busy”: “2026-03-30T19:38:53.322954Z”, “iopub.status.idle”: “2026-03-30T19:38:53.326982Z”, “shell.execute_reply”: “2026-03-30T19:38:53.326779Z”
}
}, “outputs”: [
- {
“name”: “stdout”, “output_type”: “stream”, “text”: [
“=== Kernel Focus Stress (SPY high-vol regime) ===n”, “ return_nom stdev_nom VaR95_nom CVaR95_nom ENS_nom \n”, “portfolio_0 0.0022 0.0251 0.039 0.0603 1006.0 n”, “n”, “ return_stress stdev_stress VaR95_stress CVaR95_stress \n”, “portfolio_0 0.0233 0.0576 0.024 0.0241 n”, “n”, “ ENS_stress KL_q_p n”, “portfolio_0 5.1206 5.2805 n”
]
}
], “source”: [
“focus = returns["SPY"].rolling(12).std(ddof=0).bfill()n”, “n”, “df_kernel = kernel_focus_stress(n”, “ weights,n”, “ returns.values,n”, “ focus_series=focus.values,n”, “ bandwidth=None,n”, “ target=focus.values.max(),n”, “)n”, “n”, “print("=== Kernel Focus Stress (SPY high-vol regime) ===")n”, “print(df_kernel.round(4))”
]
}, {
“cell_type”: “markdown”, “id”: “0c9vcym7ejhg”, “metadata”: {}, “source”: [
“## Combined scenario shock with stress_testn”, “n”, “Apply a 25% scale-up to all scenarios and reweight probabilities ton”, “favour early (tail) scenarios.”
]
}, {
“cell_type”: “code”, “execution_count”: 7, “id”: “3o00pr7orul”, “metadata”: {
- “execution”: {
“iopub.execute_input”: “2026-03-30T19:38:53.327962Z”, “iopub.status.busy”: “2026-03-30T19:38:53.327907Z”, “iopub.status.idle”: “2026-03-30T19:38:53.331093Z”, “shell.execute_reply”: “2026-03-30T19:38:53.330923Z”
}
}, “outputs”: [
- {
“name”: “stdout”, “output_type”: “stream”, “text”: [
“=== Combined Shock + Reweight ===n”, “ return_nom stdev_nom VaR95_nom CVaR95_nom ENS_nom \n”, “portfolio_0 0.0022 0.0251 0.039 0.0603 1006.0 n”, “n”, “ return_stress stdev_stress VaR95_stress CVaR95_stress \n”, “portfolio_0 0.0029 0.031 0.0479 0.0741 n”, “n”, “ ENS_stress KL_q_p n”, “portfolio_0 987.2946 0.0188 n”
]
}
], “source”: [
“scale_up = linear_map(scale=1.25)n”, “n”, “tail_weights = np.linspace(1.0, 2.0, num=returns.shape[0])n”, “tail_weights /= tail_weights.sum()n”, “n”, “df_combo = stress_test(n”, “ weights,n”, “ returns.values,n”, “ stressed_probabilities=tail_weights,n”, “ transform=scale_up,n”, “)n”, “n”, “print("=== Combined Shock + Reweight ===")n”, “print(df_combo.round(4))”
]
}, {
“cell_type”: “markdown”, “id”: “e2xe289vxq9”, “metadata”: {}, “source”: [
“## Summary tablen”, “n”, “Collect all stress results into a single comparison table.”
]
}, {
“cell_type”: “code”, “execution_count”: 8, “id”: “2uam0d8w385”, “metadata”: {
- “execution”: {
“iopub.execute_input”: “2026-03-30T19:38:53.332110Z”, “iopub.status.busy”: “2026-03-30T19:38:53.332046Z”, “iopub.status.idle”: “2026-03-30T19:38:53.335931Z”, “shell.execute_reply”: “2026-03-30T19:38:53.335752Z”
}
}, “outputs”: [
- {
“name”: “stdout”, “output_type”: “stream”, “text”: [
“=== Summary ===n”, “Nominal:n”, “mean 0.0022n”, “stdev 0.0251n”, “VaR95 0.0390n”, “CVaR95 0.0603n”, “ENS 1006.0000n”, “dtype: float64n”, “n”, “Exp-Decay (HL=60):n”, “ return_nom stdev_nom VaR95_nom CVaR95_nom ENS_nom \n”, “portfolio_0 0.0022 0.0251 0.039 0.0603 1006.0 n”, “n”, “ return_stress stdev_stress VaR95_stress CVaR95_stress \n”, “portfolio_0 0.0027 0.0246 0.0307 0.0546 n”, “n”, “ ENS_stress KL_q_p n”, “portfolio_0 235.2738 1.453 n”, “n”, “Kernel Focus (SPY vol):n”, “ return_nom stdev_nom VaR95_nom CVaR95_nom ENS_nom \n”, “portfolio_0 0.0022 0.0251 0.039 0.0603 1006.0 n”, “n”, “ return_stress stdev_stress VaR95_stress CVaR95_stress \n”, “portfolio_0 0.0233 0.0576 0.024 0.0241 n”, “n”, “ ENS_stress KL_q_p n”, “portfolio_0 5.1206 5.2805 n”, “n”, “Shock + Reweight:n”, “ return_nom stdev_nom VaR95_nom CVaR95_nom ENS_nom \n”, “portfolio_0 0.0022 0.0251 0.039 0.0603 1006.0 n”, “n”, “ return_stress stdev_stress VaR95_stress CVaR95_stress \n”, “portfolio_0 0.0029 0.031 0.0479 0.0741 n”, “n”, “ ENS_stress KL_q_p n”, “portfolio_0 987.2946 0.0188 n”
]
}
], “source”: [
“print("=== Summary ===")n”, “print("Nominal:")n”, “print(report.round(4))n”, “print()n”, “print("Exp-Decay (HL=60):")n”, “print(df_half_life.round(4))n”, “print()n”, “print("Kernel Focus (SPY vol):")n”, “print(df_kernel.round(4))n”, “print()n”, “print("Shock + Reweight:")n”, “print(df_combo.round(4))”
]
}
], “metadata”: {
- “kernelspec”: {
“display_name”: “Python 3”, “language”: “python”, “name”: “python3”
}, “language_info”: {
- “codemirror_mode”: {
“name”: “ipython”, “version”: 3
}, “file_extension”: “.py”, “mimetype”: “text/x-python”, “name”: “python”, “nbconvert_exporter”: “python”, “pygments_lexer”: “ipython3”, “version”: “3.12.9”
}
}, “nbformat”: 4, “nbformat_minor”: 5
}