mrv-lib: Model Risk Validator
Your model might be producing different outputs depending on which features you feed it, which seed you use, or how you bin the data: and your current validation doesn't catch this. mrv-lib tests whether your model outputs are stable across admissible specification choices, or silently depend on arbitrary modelling decisions.
mrv is a pure validation library: you supply labels from your own models, mrv measures how stable they are. Bank model risk management (OCC Bulletin 2026-13 -- the 2026-04-17 Revised Model Risk Management Guidance that supersedes SR 11-7) is the anchor application; the same tests deploy equally to production ML monitoring (route to fallback or human-in-the-loop when labels are unstable, regardless of domain).
What it does
| Test | Question | Status |
|---|---|---|
| Representation Invariance | Do labels change when you use different feature representations? | v0.1.0 |
| Resolution Invariance | Do labels agree across 5m / 15m / 1h / 1d frequencies? | v0.2.1 |
Also includes: a business impact function (impact_fn), disagreement attribution (LOO / frequency-pair / temporal), and a specification-invariance report generator (report(): result JSON to LaTeX to PDF) covering both the representation and resolution tests.
Install
pip install mrv-lib
Quick start
The recommended public Python API lives at the top level of the mrv package.
You supply a model_fn (features to integer labels) plus the admissible set of
specifications, and mrv returns a typed result. mrv only measures agreement; it
never fits a model itself.
Representation invariance (Paper 1) across feature representations:
import numpy as np
import mrv
rng = np.random.default_rng(42)
n = 200
base = rng.integers(0, 3, n)
labels_a = base.copy()
labels_b = base.copy()
flip = rng.random(n) < 0.10
labels_b[flip] = rng.integers(0, 3, flip.sum()) # small perturbation
returns = rng.standard_normal(n) * 0.01
result = mrv.rep_invariance_validator(
model_fn=lambda x: x, # passthrough: supply pre-computed labels directly
admissible_class={"vol+dd+var": labels_a, "vol+var+cvar": labels_b},
returns=returns, # optional: enables the Spearman ordering check
K=3, # number of regime states
)
print(result.summary())
print("mean ARI:", result.mean_ari["asset"])
print("partition passes:", result.passes_partition["asset"])
Already fit your own regime model? Wrap the labels with the passthrough
model_fn=lambda x: x as above, or pass a real callable that maps a feature
matrix to integer labels.
Resolution invariance (Paper 2) across frequencies:
import pandas as pd
import numpy as np
import mrv
rng = np.random.default_rng(0)
idx = pd.date_range("2026-01-05 09:30", periods=480, freq="5min",
tz="America/New_York")
labels_5m = pd.Series(rng.integers(0, 2, 480), index=idx, dtype=int)
labels_15m = labels_5m.iloc[::3].copy()
result = mrv.res_invariance_validator(
model_fn=lambda s: s, # passthrough: supply pre-computed labels per frequency
resolution_set={"SPY": {"5m": labels_5m, "15m": labels_15m}},
spec=mrv.ResolutionSpec(freqs=("5m", "15m"), intraday_freqs=("5m", "15m")),
run_permutation=False,
)
print(result.summary())
print(result.ari_matrix["SPY"].round(3))
print("overall mean ARI:", result.overall_mean_ari["SPY"])
The typed results (RepInvarianceResult / ResInvarianceResult) expose
.summary() plus attributes such as .ari_matrix, .overall_mean_ari,
.passes_partition, and .intraday_overall_ari_gap. To feed a real model, pass
a model_fn that fits your regime model and returns integer labels. See
examples/paper1_representation_invariance.ipynb and
examples/paper2_resolution_invariance.ipynb for end-to-end walkthroughs.
Logging
mrv-lib uses Python's standard logging module with hierarchical names
(mrv.validator.rep, mrv.validator.res, etc.). By default nothing is emitted.
import logging
# Show all mrv INFO+ messages
logging.basicConfig(level=logging.INFO)
# Show DEBUG for the representation validator only
logging.getLogger("mrv.validator.rep").setLevel(logging.DEBUG)
# Route mrv logs to a file
handler = logging.FileHandler("mrv_run.log")
logging.getLogger("mrv").addHandler(handler)
See src/mrv/utils/log.py and src/mrv/default_config.yaml for the YAML-based
logging configuration used by the convenience pipeline.
Project layout
mrv-lib/
|-- config.yaml # Configuration (for convenience pipeline)
|-- examples/
| |-- quickstart.ipynb
| |-- paper1_representation_invariance.ipynb
| |-- paper2_resolution_invariance.ipynb
| `-- example_california_housing.ipynb
|-- src/mrv/
| |-- invariance/ # Recommended public Python API + typed results (rep, res)
| |-- pipeline.py # Internal labels-first backend behind the `mrv` CLI
| |-- data/ # Data modules (optional)
| | |-- reader.py # CSV / OHLCV loading
| | |-- factors.py # Factor / feature engineering
| | |-- normalize.py # Normalization (rolling z-score, minmax)
| | `-- download_yahoo.py # Yahoo Finance data download
| |-- models/ # GMM/HMM fitting
| |-- templates/
| | `-- template.tex # Specification-invariance report template (rep + res)
| |-- validator/
| | |-- base.py # BaseValidator (subclass for custom tests)
| | |-- rep.py # Representation Invariance (Paper 1)
| | |-- res.py # Resolution Invariance (Paper 2)
| | |-- metrics.py # ARI, AMI, NMI, Spearman, VI
| | |-- attribution.py # LOO, frequency-pair, temporal hotspots
| | `-- report.py # JSON -> LaTeX -> PDF
| `-- utils/
| |-- config.py # YAML config loading
| |-- download_ib.py # IB data download
| `-- log.py # Logging setup
|-- reports/ # Output (gitignored)
`-- tests/
Output
Each run creates a timestamped directory under reports/:
- result.json -- Complete data (reusable for report regeneration)
- report.pdf -- Report with cover page, dashboard, heatmaps, and remediation plan
- summary.txt -- Plain text quick view
- {asset}_ari_heatmap.png -- ARI heatmap per asset
- {asset}_timeline.png -- Regime timeline (res validator)
- pipeline_summary.csv -- Summary metrics per asset
Research
Based on the following PhD research:
- Zheng, Low & Wang (2026). Regime Labels Are Not Representation-Invariant (Paper 1). Finance Research Letters.
- Zheng, Low & Wang (2026). Regime Labels Are Not Resolution-Invariant (Paper 2). Finance Research Letters.
License
Dual-licensed.
- Open source: GNU Affero General Public License v3.0 or later (AGPL-3.0-or-later). See LICENSE. Free for academic research, teaching, and personal use. Note that the AGPL's network-use clause requires any modified version offered over a network to also offer its complete source.
- Commercial: Organizations that wish to use mrv-lib in proprietary or closed-source systems, or otherwise cannot meet the AGPL obligations, require a separate commercial license. See COMMERCIAL-LICENSE.md.
Maintainers
ModelGuard Lab -- Author: Kai Zheng.
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 mrv_lib-0.7.0.tar.gz.
File metadata
- Download URL: mrv_lib-0.7.0.tar.gz
- Upload date:
- Size: 98.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8c35145be865ed6d4a6b496f90dc3e6539a115590b628b595751f05e24c694f4
|
|
| MD5 |
44a3d3164c4b4b15c6588b49e1e66fff
|
|
| BLAKE2b-256 |
a9e7499796e611002eaaedf60ea0899f5513096b30d2756967ceecc0b06add26
|
Provenance
The following attestation bundles were made for mrv_lib-0.7.0.tar.gz:
Publisher:
publish.yml on modelguard-lab/mrv-lib
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mrv_lib-0.7.0.tar.gz -
Subject digest:
8c35145be865ed6d4a6b496f90dc3e6539a115590b628b595751f05e24c694f4 - Sigstore transparency entry: 2114386688
- Sigstore integration time:
-
Permalink:
modelguard-lab/mrv-lib@e7045f68983f93e1f20da1afa14b7e458e419b17 -
Branch / Tag:
refs/tags/v0.7.0 - Owner: https://github.com/modelguard-lab
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@e7045f68983f93e1f20da1afa14b7e458e419b17 -
Trigger Event:
push
-
Statement type:
File details
Details for the file mrv_lib-0.7.0-py3-none-any.whl.
File metadata
- Download URL: mrv_lib-0.7.0-py3-none-any.whl
- Upload date:
- Size: 92.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8e5bc7b23f77dc594f14ebbff13b7e74673ea5f6ce094717df7aa4349d7c46f5
|
|
| MD5 |
d40dbe0f1ba443217cd6882f428fa9fe
|
|
| BLAKE2b-256 |
b87352693753d3085a8dee5d00f1d36d18eaa7521bf92f17b3daec1b025bd001
|
Provenance
The following attestation bundles were made for mrv_lib-0.7.0-py3-none-any.whl:
Publisher:
publish.yml on modelguard-lab/mrv-lib
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mrv_lib-0.7.0-py3-none-any.whl -
Subject digest:
8e5bc7b23f77dc594f14ebbff13b7e74673ea5f6ce094717df7aa4349d7c46f5 - Sigstore transparency entry: 2114386812
- Sigstore integration time:
-
Permalink:
modelguard-lab/mrv-lib@e7045f68983f93e1f20da1afa14b7e458e419b17 -
Branch / Tag:
refs/tags/v0.7.0 - Owner: https://github.com/modelguard-lab
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@e7045f68983f93e1f20da1afa14b7e458e419b17 -
Trigger Event:
push
-
Statement type: