A Python library for credit, market, and operational risk modelling with model evaluation.
Project description
modelrisk
modelrisk is a Python library for credit, market, and operational risk modelling with a built-in model evaluation and explainability suite.
Package Structure
graph TD
A[modelrisk]
A --> B[credit] A --> C[market] A --> D[risk engine] A --> E[evaluation] A --> F[utils]
B --> B1[IFRS9 Models] B --> B2[Scorecards] B --> B3[IRB]
C --> C1[VaR / CVaR] C --> C2[Volatility] C --> C3[Simulation]
D --> D1[LDA] D --> D2[Scenarios] D --> D3[EVT]
E --> E1[Classification Metrics] E --> E2[Regression Metrics] E --> E3[Calibration] E --> E4[Explainability]
F --> F1[Distributions] F --> F2[Simulation] F --> F3[Plotting]
Architecture
Full package map — five subpackages (
credit,market,operational,evaluation,utils), the IFRS 9 and IRB regulatory pipelines, the high-levelScenarioManager, and all cross-package dependencies.
Features
Credit Risk
- PD Models — Logistic regression PD with WoE encoding; Merton structural model
- LGD Models — Beta regression and linear regression for loss given default
- Scorecards — Weight of Evidence (WoE), Information Value (IV), and points-based scoring
Market Risk
- VaR — Historical simulation, parametric (Normal and Student-t), Monte Carlo
- CVaR / Expected Shortfall — All three methods; Basel III 97.5% ES ready
- Volatility — EWMA (RiskMetrics) and GARCH(1,1) by MLE with multi-step forecasting
Operational Risk
- Loss Distribution Approach (LDA) — Frequency/severity fitting + Monte Carlo convolution
- Scenario Analysis — Expert-elicited scenarios with Poisson-lognormal simulation
- Extreme Value Theory — Generalised Pareto Distribution (GPD) via Peaks Over Threshold
Model Evaluation
| Category | Metrics |
|---|---|
| Classification | AUC-ROC, Gini, KS statistic, F1, Precision, Recall, Specificity, Balanced Accuracy, MCC, Brier Score, Log Loss, Lift@Decile, CAP curve |
| Regression | RMSE, MSE, MAE, R², Adjusted R², MAPE, Median AE, Max Error, Mean Bias |
| Calibration | Hosmer-Lemeshow test, Reliability diagram, ECE, Portfolio rate ratio, EVA decile table |
| Explainability | SHAP values (TreeExplainer / KernelExplainer / fallback), LIME (tabular / fallback), Permutation importance, Feature importance summary |
Installation
# Core package
pip install modelrisk
# With SHAP and LIME explainability
pip install "modelrisk[explainability]"
# Full install including dev tools
pip install "modelrisk[all]"
Quick Start
Credit Risk — PD Model
from modelrisk.credit import LogisticPD
from modelrisk.evaluation import ClassificationMetrics
model = LogisticPD(scale_features=True)
model.fit(X_train, y_train)
y_pred = model.predict_proba(X_test)
# Full evaluation suite
metrics = ClassificationMetrics(y_test, y_pred)
print(metrics.summary())
# metric value interpretation
# 0 AUC-ROC 0.823 Discrimination; >0.7 acceptable, >0.8 good
# 1 Gini 0.646 2*AUC - 1; >0.4 acceptable for credit
# 2 KS statistic 0.512 >0.3 acceptable; measures score separation
# ...
Credit Risk — Scorecard with WoE/IV
from modelrisk.credit import Scorecard
sc = Scorecard(pdo=20, base_score=600)
sc.fit(X_binned, y) # X_binned: pre-binned with pd.cut / pd.qcut
print(sc.information_value_summary())
scores = sc.score(X_test) # Integer scorecard points, higher = lower risk
Market Risk — VaR and CVaR
from modelrisk.market import HistoricalVaR, CVaR
# 1-day 99% historical VaR
var = HistoricalVaR(confidence_level=0.99).fit(returns).var()
# 10-day 97.5% CVaR (Basel III ES)
es = CVaR(confidence_level=0.975, method="historical", holding_period=10)
es.fit(returns)
print(es.summary())
Market Risk — GARCH Volatility
from modelrisk.market import GARCHVolatility
garch = GARCHVolatility().fit(returns)
print(garch.parameter_summary())
# 10-day annualised volatility forecast
forecasts = garch.forecast(horizon=10)
Operational Risk — LDA Capital
from modelrisk.operational import LossDistributionApproach
lda = LossDistributionApproach(
frequency_dist="negative_binomial",
severity_dist="lognormal",
n_simulations=200_000,
)
lda.fit(annual_frequencies, individual_losses)
capital = lda.capital_estimate()
print(f"99.9% VaR capital: {capital['var_capital']:,.0f}")
print(f"99.9% CVaR capital: {capital['cvar_capital']:,.0f}")
Model Calibration
from modelrisk.evaluation import CalibrationMetrics
cal = CalibrationMetrics(y_true, y_pred_proba, n_bins=10)
# Hosmer-Lemeshow goodness-of-fit
hl = cal.hosmer_lemeshow()
print(f"HL p-value: {hl['p_value']:.4f} — {hl['interpretation']}")
# Decile-level Expected vs Actual table
print(cal.expected_vs_actual())
Explainability — SHAP and LIME
from modelrisk.evaluation import Explainer
# Works with or without the shap/lime packages installed
explainer = Explainer(
model,
feature_names=feature_names,
background_data=X_train,
)
# SHAP values for the test set
shap_df = explainer.shap_values(X_test)
# LIME explanation for a single instance
lime_df = explainer.lime_explain(X_test.iloc[0], X_train, top_n=10)
# Permutation feature importance
perm_df = explainer.permutation_importance(X_test, y_test)
# Combined summary
summary = explainer.feature_importance_summary(X_test, y_test)
print(summary)
Plotting
from modelrisk.utils import RiskPlotter
plotter = RiskPlotter()
fig = plotter.roc_curve(y_true, y_score)
fig = plotter.cap_curve(y_true, y_score)
fig = plotter.reliability_diagram(y_true, y_score)
fig = plotter.loss_distribution(simulated_losses, var_level=0.99, cvar_level=0.975)
fig = plotter.shap_summary(shap_df)
fig.savefig("shap_summary.png", dpi=150)
IFRS 9 — ScenarioManager (no-code entry point)
from modelrisk.credit import ScenarioManager
# Option A — Python API (method chaining)
mgr = (
ScenarioManager(discount_rate=0.05)
.add_scenario("base", weight=0.50, pd_scalar=1.0, label="Central")
.add_scenario("downside", weight=0.30, pd_scalar=1.80, label="Adverse")
.add_scenario("upside", weight=0.20, pd_scalar=0.70, label="Benign")
.attach_portfolio(pd=df.pit_pd, lgd=df.lgd, ead=df.ead,
stage=df.stage, lifetime_pd=df.lifetime_pd)
)
# Option B — YAML config (no Python required for scenario changes)
mgr = ScenarioManager.from_yaml("scenarios.yaml")
mgr.attach_portfolio(pd=df.pit_pd, lgd=df.lgd, ead=df.ead, stage=df.stage)
results = mgr.run_all()
print(mgr.weighted_ecl()) # probability-weighted ECL
print(mgr.scenario_ecl_table()) # per-scenario ECL + weighted contribution
print(mgr.summary_report()) # audit-ready stage x scenario breakdown
mgr.to_yaml("scenarios_q4.yaml") # export config for version control
IRB — Through-the-cycle calibration and capital
from modelrisk.credit.irb import TTCCalibrator, IRBCapital, IRBValidator
ttc = TTCCalibrator(min_pd=0.0003).fit(annual_default_rates)
irb = IRBCapital(asset_class="retail_mortgage")
rwa = irb.rwa_portfolio(pd_array=ttc_pds, lgd_array=lgds, ead_array=eads)
val = IRBValidator()
val.traffic_light_test(predicted_pd=0.008, observed_dr=0.015, n_obligors=500)
Project Structure
modelrisk/
├── credit/
│ ├── base_pd.py # BasePDModel abstract class
│ ├── pd.py # LogisticPD, RandomForestPD, XGBoostPD, MertonPD
│ ├── lgd.py # BetaLGD, LinearLGD
│ ├── scorecard.py # Scorecard (WoE, IV, points)
│ ├── scenario_manager.py # ScenarioManager — IFRS 9 orchestration
│ ├── ifrs9/
│ │ ├── pit_pd.py # PITCalibrator (scalar, isotonic, Platt, time weights)
│ │ ├── staging.py # StagingClassifier (dual SICR detection)
│ │ ├── forward_pd.py # ForwardPDCurve (hazard-rate term structure)
│ │ ├── lifetime_pd.py # LifetimePDCurve (discounted ECL per period)
│ │ ├── macro_overlay.py # MacroOverlay (OLS sensitivity + logit scenarios)
│ │ └── ecl.py # ECLCalculator (portfolio ECL aggregation)
│ └── irb/
│ ├── ttc_pd.py # TTCCalibrator (long-run average, Basel floor)
│ ├── smoothing.py # CycleAdjuster (scalar, moving avg, HP filter)
│ ├── dr_mapping.py # RatingMasterScale (log-spaced grade assignment)
│ ├── pit_to_ttc.py # PITtoTTCBridge (scalar and logit-offset)
│ ├── capital.py # IRBCapital (Basel ASRF RWA formula)
│ └── validation.py # IRBValidator (traffic light + binomial backtest)
├── market/
│ ├── var.py # HistoricalVaR, ParametricVaR, MonteCarloVaR
│ ├── cvar.py # CVaR / Expected Shortfall
│ └── volatility.py # EWMAVolatility, GARCHVolatility
├── operational/
│ ├── lda.py # LossDistributionApproach
│ ├── scenarios.py # ScenarioAnalysis, ExtremeValueModel
│ └── evt.py
├── evaluation/
│ ├── classification.py # ClassificationMetrics
│ ├── regression.py # RegressionMetrics
│ ├── calibration.py # CalibrationMetrics
│ └── explainability.py # Explainer (SHAP, LIME, permutation)
└── utils/
├── distributions.py # DistributionFitter
├── simulation.py # MonteCarloEngine
└── plotting.py # RiskPlotter
Publishing a New Release
- Bump the version in
pyproject.tomlandmodelrisk/__init__.py - Commit and tag:
git tag v0.2.0 && git push origin v0.2.0 - The GitHub Actions
publish.ymlworkflow builds and publishes to PyPI automatically via OIDC trusted publishing (no API key needed — configure once at pypi.org/manage/account/publishing/)
Development
git clone https://github.com/saranshb1/modelRisk
cd modelRisk
pip install -e ".[all]"
pytest tests/ -v --cov=modelrisk
ruff check modelrisk/
License
MIT — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file modelrisk-0.1.0.tar.gz.
File metadata
- Download URL: modelrisk-0.1.0.tar.gz
- Upload date:
- Size: 632.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d38de7c9a7a65e89d2c0c96335027977dc8db98345f28485aef31306d4af5afe
|
|
| MD5 |
cc52a7d210f3dcfa1aba877331f40b95
|
|
| BLAKE2b-256 |
9db8e0ec3938735d025a6860860199e102cb99ac56347318e6fe57e79e18a39d
|
File details
Details for the file modelrisk-0.1.0-py3-none-any.whl.
File metadata
- Download URL: modelrisk-0.1.0-py3-none-any.whl
- Upload date:
- Size: 78.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
984457074ae2b1e7e076c740358f18dbfbed8226e6e97ecc06885f4b86ae74cb
|
|
| MD5 |
4bf03848ad2d7bf2a65d7e749d6bb900
|
|
| BLAKE2b-256 |
35b218c3633280202ef9334f0edad61acba5b43863aa8690981a79f6c4e29d3f
|