Skip to main content

mrv-lib: Model Risk Validator

CI PyPI Python

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.

Metadata

Release files for mrv-lib 0.7.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for mrv-lib 0.7.0
File Size Uploaded
mrv_lib-0.7.0.tar.gz 98.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for mrv-lib 0.7.0
File Interpreter ABI Platform
mrv_lib-0.7.0-py3-none-any.whl Python 3 none any Details

Total release size: 190.4 kB

Release files / mrv_lib-0.7.0.tar.gz

Download URL mrv_lib-0.7.0.tar.gz
Size 98.3 kB
Tags Source
SHA-256 checksum
How to use checksums
8c35145be865ed6d4a6b496f90dc3e6539a115590b628b595751f05e24c694f4
BLAKE2b-256 checksum
How to use checksums
a9e7499796e611002eaaedf60ea0899f5513096b30d2756967ceecc0b06add26
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 8, 2026.

Transparency log

Release files / mrv_lib-0.7.0-py3-none-any.whl

Download URL mrv_lib-0.7.0-py3-none-any.whl
Size 92.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
8e5bc7b23f77dc594f14ebbff13b7e74673ea5f6ce094717df7aa4349d7c46f5
BLAKE2b-256 checksum
How to use checksums
b87352693753d3085a8dee5d00f1d36d18eaa7521bf92f17b3daec1b025bd001
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 8, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.7.0 This release

2 release files

0.6.1

2 release files

0.1.0

2 release files

0.0.2

2 release files

0.0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page