Complete methodology and software specification for a production-style quantitative risk model validation project. The framework implements Parametric VaR, Historical Simulation VaR and Monte Carlo VaR, followed by out-of-sample backtesting, statistical testing, stress testing, robustness analysis and model-risk assessment.
This project must be designed as a miniature quantitative model-validation exercise, not merely as a project that calculates VaR.
All three models will use the same rolling, one-day-ahead, out-of-sample framework.
For forecast date t: 1. Take only observations available before date t. 2. Select the previous N observations as the estimation window. 3. Estimate the parameters required by the model. 4. Produce VaR for date t. 5. Observe the realized portfolio return on date t. 6. Convert the realized return into a loss. 7. Compare realized loss against VaR. 8. Record whether an exception occurred. 9. Move the estimation window forward by one observation. 10. Repeat until the entire backtest period is evaluated.
| Parameter | Baseline | Reason |
|---|---|---|
| Return frequency | Daily | Natural frequency for the baseline market-risk experiment. |
| VaR horizon | 1 trading day | Clear and reproducible forecast horizon. |
| Confidence levels | 95% and 99% | Allows comparison between standard and more extreme tails. |
| Estimation window | 250 trading observations | Approximately one trading year. |
| Backtest period | At least 500 observations where data availability permits | Provides a more meaningful exception sample. |
| Portfolio | Four-asset diversified portfolio | Makes covariance and cross-asset dependence meaningful. |
| Loss convention | Loss = negative portfolio return | Positive values represent losses. |
| VaR convention | Positive loss threshold | Simple and consistent exception logic. |
| Monte Carlo simulations | 100,000 | Baseline simulation size. |
| Random seed | Fixed | Ensures reproducibility. |
All three models must be evaluated against exactly the same realized portfolio-loss observations and the same forecast dates.
The implementation must distinguish clearly between:
The code and documentation must never rely on ambiguous variable names
such as alpha without documenting whether alpha represents
confidence or tail probability.
The baseline portfolio will contain four risk buckets.
| Risk Bucket | Suggested Proxy | Purpose |
|---|---|---|
| Equity | Broad equity index proxy | Equity market risk. |
| Rates | Government bond / Treasury proxy | Rates exposure. |
| Commodity | Gold or broad commodity proxy | Commodity diversification. |
| FX | Major currency proxy | Foreign-exchange risk. |
config.yaml before the final empirical run.
Equity = 40% Rates = 30% Commodity = 15% FX = 15%
The weights must sum to 100%. They remain fixed during the primary model comparison.
data/raw/ date equity_price rates_price commodity_price fx_price
The first return observation is removed because it has no preceding price observation.
Positive values represent losses.
The baseline Parametric VaR model assumes that portfolio returns can be represented using a Normal distribution characterized by an estimated mean and variance.
mu = mean(return_matrix)
Sigma = covariance(return_matrix)
Historical Simulation does not impose a Normal distribution. It uses the empirical distribution of historical portfolio losses.
Rolling historical returns
|
v
Calculate portfolio returns
|
v
Convert returns to losses
|
v
Sort historical losses
|
v
Calculate empirical quantile
|
v
Historical VaR
Monte Carlo VaR generates a simulated distribution of portfolio returns and obtains VaR from the simulated loss distribution.
Historical estimation window
|
v
Estimate mu and Sigma
|
v
Generate z ~ N(0,I)
|
v
Calculate Cholesky factor L
|
v
r = mu + Lz
|
v
Portfolio return = w'r
|
v
Portfolio loss = -return
|
v
Tail quantile
|
v
Monte Carlo VaR
where z is a vector of independent standard Normal random variables.
| Parameter | Baseline | Robustness Test |
|---|---|---|
| Simulation count | 100,000 | 10k / 25k / 50k / 100k / 250k |
| Random seed | Fixed | Multiple seeds |
| Distribution | Multivariate Normal | Alternative distribution later |
| Dependence | Sample covariance | EWMA extension |
date realized_return realized_loss parametric_var_95 historical_var_95 monte_carlo_var_95 parametric_exception_95 historical_exception_95 monte_carlo_exception_95 parametric_var_99 historical_var_99 monte_carlo_var_99 parametric_exception_99 historical_exception_99 monte_carlo_exception_99
The Kupiec test evaluates whether the observed exception frequency is consistent with the expected exception probability.
where c is the stated VaR confidence level.
The project must also determine whether exceptions occur independently or cluster together.
A transition-count approach will be used to evaluate the occurrence of exceptions following non-exception and exception observations.
The final backtesting assessment should combine:
Identify extreme observed market periods from the dataset and evaluate model behavior around those periods.
The stress-selection methodology must be documented and reproducible.
| Scenario | Shock | Purpose |
|---|---|---|
| Equity Crash | Large negative equity return | Tail sensitivity. |
| Rates Shock | Large adverse rates/yield movement | Rates risk. |
| Commodity Shock | Large adverse commodity movement | Commodity exposure. |
| FX Shock | Large adverse currency movement | FX exposure. |
| Correlated Sell-Off | Multiple assets move adversely together | Dependence/model risk. |
| Volatility Shock | Increase volatility assumptions | Parameter sensitivity. |
| Dimension | Baseline | Alternative | Validation Question |
|---|---|---|---|
| Confidence level | 95%, 99% | 97.5% | Does the conclusion change across tail levels? |
| Historical window | 250 days | 125 / 500 days | How dependent is the model on historical window? |
| MC simulations | 100,000 | 10k to 250k | Has the simulation converged? |
| MC random seed | Fixed | Several seeds | How large is simulation noise? |
| Volatility model | Sample volatility | EWMA | How important is volatility specification? |
| Stress severity | Baseline scenario | More severe scenarios | Does behavior remain plausible? |
Calculate Monte Carlo VaR using increasing simulation counts.
10,000 25,000 50,000 100,000 250,000
Plot simulation count against VaR. Calculate absolute and relative changes between successive estimates.
Run the same Monte Carlo model using multiple random seeds.
Report:
Run the models using:
125-day window 250-day window 500-day window
Compare both VaR estimates and backtesting performance.
The baseline Parametric VaR model will use sample volatility. An EWMA volatility extension will then be used to test the sensitivity of results to volatility dynamics.
| File | Responsibility |
|---|---|
README.md |
GitHub-facing project overview. Explains objective, methodology, setup, execution, results and model-validation conclusions. |
requirements.txt |
Lists Python dependencies required to reproduce the project. |
config.yaml |
Stores assets, portfolio weights, dates, confidence levels, estimation window, simulation count, seeds and stress scenarios. |
data_loader.py |
Loads or downloads market data and converts it into a standardized price DataFrame. |
preprocessing.py |
Performs data-quality checks, alignment and cleaning. |
returns.py |
Calculates asset returns and validates the resulting time series. |
portfolio.py |
Validates weights and calculates portfolio returns and losses. |
parametric.py |
Contains Parametric Normal VaR implementation and rolling forecasts. |
historical.py |
Contains Historical Simulation VaR and rolling forecasts. |
monte_carlo.py |
Contains multivariate simulation, Cholesky decomposition, portfolio simulation and Monte Carlo VaR. |
exceptions.py |
Identifies VaR exceptions and calculates exception-level statistics. |
kupiec.py |
Implements the Kupiec unconditional coverage test. |
independence.py |
Implements exception independence and clustering analysis. |
evaluator.py |
Provides one standardized interface for evaluating each model. |
scenarios.py |
Defines and applies historical and hypothetical stress scenarios. |
metrics.py |
Contains validation metrics and model-performance measures. |
comparison.py |
Compares all models using a standardized validation framework. |
robustness.py |
Runs parameter sensitivity, window sensitivity, seed sensitivity and Monte Carlo convergence tests. |
visualization.py |
Contains reusable chart-generation functions. |
methodology.md |
Documents mathematical methodology, assumptions, definitions and validation philosophy. |
load_market_data(
tickers,
start_date,
end_date,
source
)
validate_price_data(
prices
)
align_market_data(
prices
)
save_processed_data(
data,
path
)
calculate_returns(
prices,
method="simple"
)
validate_returns(
returns
)
validate_weights(
weights
)
calculate_portfolio_returns(
returns,
weights
)
calculate_losses(
portfolio_returns
)
estimate_mean_covariance(
returns
)
portfolio_mean_variance(
mean_vector,
covariance_matrix,
weights
)
parametric_var(
returns,
weights,
confidence_level
)
rolling_parametric_var(
returns,
weights,
window,
confidence_level
)
historical_var(
portfolio_losses,
confidence_level
)
rolling_historical_var(
portfolio_returns,
window,
confidence_level
)
validate_covariance_matrix(
covariance_matrix
)
cholesky_factor(
covariance_matrix
)
simulate_multivariate_returns(
mean_vector,
covariance_matrix,
n_simulations,
random_seed
)
simulate_portfolio_losses(
simulated_returns,
weights
)
monte_carlo_var(
returns,
weights,
confidence_level,
n_simulations,
random_seed
)
rolling_monte_carlo_var(
returns,
weights,
window,
confidence_level,
n_simulations,
random_seed
)
identify_exceptions(
realized_losses,
var_series
)
exception_rate(
exceptions
)
backtest_summary(
realized_losses,
var_series,
confidence_level
)
kupiec_test(
exceptions,
confidence_level
)
independence_test(
exceptions
)
conditional_coverage_test(
exceptions,
confidence_level
)
evaluate_model(
realized_losses,
var_series,
confidence_level
)
create_historical_stress_scenarios(
returns,
n_scenarios
)
create_hypothetical_scenario(
shocks
)
apply_scenario(
asset_returns,
scenario
)
calculate_stressed_loss(
asset_returns,
weights,
scenario
)
run_stress_test(
returns,
weights,
scenarios
)
run_window_sensitivity(
returns,
weights,
windows,
confidence_level
)
run_mc_convergence(
returns,
weights,
simulation_counts,
confidence_level
)
run_seed_sensitivity(
returns,
weights,
seeds,
confidence_level,
n_simulations
)
run_confidence_sensitivity(
returns,
weights,
confidence_levels
)
run_volatility_sensitivity(
returns,
weights,
confidence_level
)
compare_models(
evaluation_results
)
generate_validation_summary(
model_results,
stress_results,
robustness_results
)
plot_returns_distribution(...) plot_rolling_volatility(...) plot_var_vs_realized_losses(...) plot_exceptions(...) plot_exception_rate(...) plot_model_var_comparison(...) plot_stress_results(...) plot_mc_convergence(...) plot_window_sensitivity(...)
Purpose: understand and validate the dataset before modelling.
This is the most important notebook from a model-risk perspective.
The final comparison must not rank models simply according to their numerical VaR.
| Validation Dimension | Parametric | Historical | Monte Carlo |
|---|---|---|---|
| Conceptual assumptions | |||
| Distributional assumptions | |||
| Dependence assumptions | |||
| Exception rate | |||
| Kupiec result | |||
| Independence result | |||
| Conditional coverage | |||
| Stress behavior | |||
| Window sensitivity | |||
| Simulation sensitivity | N/A | N/A | |
| Computational cost | |||
| Principal model risk | |||
| Validation assessment |
The final conclusion must distinguish between:
The final conclusion must answer all of the following:
STEP 1
Freeze configuration
|
v
STEP 2
Acquire raw market data
|
v
STEP 3
Run data-quality checks
|
v
STEP 4
Align asset observations
|
v
STEP 5
Calculate daily returns
|
v
STEP 6
Construct portfolio returns
|
v
STEP 7
Construct realized losses
|
v
STEP 8
Run rolling Parametric VaR
|
v
STEP 9
Run rolling Historical VaR
|
v
STEP 10
Run rolling Monte Carlo VaR
|
v
STEP 11
Align all forecasts
|
v
STEP 12
Calculate exceptions
|
v
STEP 13
Run Kupiec test
|
v
STEP 14
Run independence test
|
v
STEP 15
Run conditional coverage analysis
|
v
STEP 16
Run historical stress tests
|
v
STEP 17
Run hypothetical stress tests
|
v
STEP 18
Run robustness analysis
|
v
STEP 19
Compare all models
|
v
STEP 20
Generate charts and tables
|
v
STEP 21
Generate validation report
|
v
STEP 22
Write final model-risk conclusion
All experiment parameters should live in configuration rather than being hard-coded throughout the Python modules.
config.yaml
project:
name: "Quantitative Risk Model Backtesting & Validation Framework"
data:
source: "TO_BE_FINALIZED"
start_date: "TO_BE_FINALIZED"
end_date: "TO_BE_FINALIZED"
frequency: "daily"
portfolio:
equity: 0.40
rates: 0.30
commodity: 0.15
fx: 0.15
risk:
horizon_days: 1
confidence_levels:
- 0.95
- 0.99
model:
estimation_window: 250
monte_carlo:
simulations: 100000
random_seed: 42
robustness:
windows:
- 125
- 250
- 500
simulations:
- 10000
- 25000
- 50000
- 100000
- 250000
seeds:
- 42
- 123
- 456
- 789
stress:
equity_crash: -0.10
commodity_shock: -0.10
fx_shock: -0.05
The exact empirical values will be finalized before the implementation stage.
| Library | Purpose | Importance |
|---|---|---|
| NumPy | Numerical computing, arrays, matrices, covariance and simulation. | ★★★★★ Essential |
| Pandas | Time-series data, returns, rolling windows and result tables. | ★★★★★ Essential |
| SciPy | Probability distributions, quantiles and statistical tests. | ★★★★★ Essential |
| Matplotlib | Validation charts and model-performance visualization. | ★★★★☆ Very Valuable |
| Statsmodels | Additional statistical and time-series functionality. | ★★★★☆ Very Valuable |
| PyYAML | Reading configuration files. | ★★★☆☆ Helpful |
| pytest | Automated unit and integration testing. | ★★★★★ Essential |
The core VaR calculations should be implemented transparently using NumPy/SciPy rather than hidden behind a specialized VaR package.
★★★★★ Essential
★★★★★ Essential
★★★★★ Essential
★★★★★ Essential
★★★★★ Essential
★★★★☆ Very Valuable
Before the project is considered finished, you must personally be able to explain:
This project is deliberately aligned with the target Quant Model Risk / Model Validation career path.
| Target Role Requirement | Project Component |
|---|---|
| Conceptual soundness | Explicit model assumptions and methodology review. |
| Alternative model benchmarks | Three competing VaR methodologies. |
| Model-performance metrics | Exception rate, coverage, independence and severity metrics. |
| Regular model evaluation | Rolling out-of-sample backtesting. |
| Probability and statistics | Distribution modelling and statistical backtesting. |
| Numerical analysis | Monte Carlo simulation and numerical quantile estimation. |
| Python | Entire framework implemented in modular Python. |
| Risk modelling | VaR, stress testing and model-risk assessment. |
| Model limitations | Dedicated robustness and limitation analysis. |
| Stage | Task | Priority |
|---|---|---|
| 1 | Freeze assets, data source, dates, weights and configuration. | ★★★★★ |
| 2 | Build data acquisition and validation pipeline. | ★★★★★ |
| 3 | Build return and portfolio engine. | ★★★★★ |
| 4 | Implement Parametric VaR. | ★★★★★ |
| 5 | Implement Historical Simulation VaR. | ★★★★★ |
| 6 | Implement Monte Carlo VaR. | ★★★★★ |
| 7 | Build rolling out-of-sample forecast engine. | ★★★★★ |
| 8 | Build exception engine. | ★★★★★ |
| 9 | Implement Kupiec and independence tests. | ★★★★★ |
| 10 | Build stress-testing engine. | ★★★★★ |
| 11 | Build robustness framework. | ★★★★★ |
| 12 | Build model-comparison framework. | ★★★★★ |
| 13 | Build automated tests. | ★★★★★ |
| 14 | Generate charts and tables. | ★★★★☆ |
| 15 | Write final validation report. | ★★★★★ |
| 16 | Polish GitHub README and repository. | ★★★★☆ |
| 17 | Prepare interview defense. | ★★★★★ |