mrv.validator – Regime diagnostics¶
mrv.validator – Regime diagnostics.
Validators¶
RepValidator: Representation Invariance (Paper 1)
ResValidator: Resolution Invariance (Paper 2)
Base class: BaseValidator – subclass to create custom tests.
- class mrv.validator.BaseValidator(cfg=None, impact_fn=None)[source]¶
Bases:
ABCBase class for all validators.
Validators accept user-provided labels and compute invariance metrics. No model fitting is performed – users supply their own labels.
- Parameters:
cfg (
Optional[Dict[str,Any]]) – Configuration dict (for report paths, thresholds, etc.). Not required for core validation – only needed for report generation.impact_fn (
Optional[Callable[[ndarray,Series],float]]) –(labels: ndarray, prices: Series) -> float. When provided, computes a business impact matrix across specifications.
- class mrv.validator.RepValidator(cfg=None, impact_fn=None)[source]¶
Bases:
BaseValidatorRepresentation Invariance validator.
Measures whether regime labels agree across different feature representations. Users provide their own labels – no model fitting is performed.
- validate(labels, risk_proxy=None, prices=None)[source]¶
Run representation invariance test on user-provided labels.
- Parameters:
labels (
Dict[str,Dict[str,ndarray]]) –{asset_name: {spec_label: ndarray}}. Each asset must have >= 2 specifications. Each ndarray is a 1-D integer array of regime labels.risk_proxy (
Optional[Dict[str,ndarray]]) –{asset_name: ndarray}. A 1-D risk measure (e.g. rolling volatility) used for ordering consistency (Spearman). If not provided, ordering consistency is skipped.prices (
Optional[Dict[str,Series]]) –{asset_name: price_series}. Only used for business impact computation (ifimpact_fnwas set).
- Return type:
- class mrv.validator.ResValidator(cfg=None, impact_fn=None)[source]¶
Bases:
BaseValidatorResolution Invariance validator.
Measures whether regime labels agree across time frequencies. Users provide their own labels – no model fitting is performed.
- validate(labels, event_window=None, calm_window=None)[source]¶
Run resolution invariance test on user-provided labels.
- Parameters:
labels (
Dict[str,Dict[str,Series]]) –{asset_name: {freq: pd.Series}}. Each Series must have a DatetimeIndex and contain integer labels. At least 2 frequencies per asset are required.event_window (
Optional[Tuple[str,str]]) –(start_date, end_date)for event-period analysis.calm_window (
Optional[Tuple[str,str]]) –(start_date, end_date)for calm-period analysis.
- Return type:
- mrv.validator.generate_report(json_path, template=None, cfg=None)[source]¶
Generate a PDF report from validator JSON + LaTeX template.
Works for both representation (Paper 1) and resolution (Paper 2) result JSON. The
.texfile is always written; the PDF is produced only whenpdflatexis available onPATH(otherwiseNoneis returned).
Sub-modules¶
Base validator¶
mrv.validator.base – Abstract base class for validators.
- class mrv.validator.base.BaseValidator(cfg=None, impact_fn=None)[source]¶
Bases:
ABCBase class for all validators.
Validators accept user-provided labels and compute invariance metrics. No model fitting is performed – users supply their own labels.
- Parameters:
cfg (
Optional[Dict[str,Any]]) – Configuration dict (for report paths, thresholds, etc.). Not required for core validation – only needed for report generation.impact_fn (
Optional[Callable[[ndarray,Series],float]]) –(labels: ndarray, prices: Series) -> float. When provided, computes a business impact matrix across specifications.
Representation validator (Paper 1)¶
mrv.validator.rep – Representation Invariance validator.
Validates whether regime labels remain stable across different feature representations (Paper 1). Users supply pre-computed labels from their own models – this module only measures agreement.
Usage:
from mrv.validator.rep import RepValidator
v = RepValidator()
result = v.validate(labels={
"SPY": {
"vol+dd+var": labels_a, # ndarray of regime labels
"vol+var+cvar": labels_b,
"skew+vol+var": labels_c,
}
})
# With ordering consistency (requires a risk proxy per asset)
result = v.validate(
labels={"SPY": {"rep_a": la, "rep_b": lb}},
risk_proxy={"SPY": volatility_array},
)
- class mrv.validator.rep.RepValidator(cfg=None, impact_fn=None)[source]¶
Bases:
BaseValidatorRepresentation Invariance validator.
Measures whether regime labels agree across different feature representations. Users provide their own labels – no model fitting is performed.
- validate(labels, risk_proxy=None, prices=None)[source]¶
Run representation invariance test on user-provided labels.
- Parameters:
labels (
Dict[str,Dict[str,ndarray]]) –{asset_name: {spec_label: ndarray}}. Each asset must have >= 2 specifications. Each ndarray is a 1-D integer array of regime labels.risk_proxy (
Optional[Dict[str,ndarray]]) –{asset_name: ndarray}. A 1-D risk measure (e.g. rolling volatility) used for ordering consistency (Spearman). If not provided, ordering consistency is skipped.prices (
Optional[Dict[str,Series]]) –{asset_name: price_series}. Only used for business impact computation (ifimpact_fnwas set).
- Return type:
Resolution validator (Paper 2)¶
mrv.validator.res – Resolution Invariance validator.
Validates whether regime labels agree across time frequencies (e.g. 5m / 15m / 1h / 1d). Users supply pre-computed labels at each frequency – this module only measures cross-frequency agreement.
Usage:
from mrv.validator.res import ResValidator
v = ResValidator()
result = v.validate(labels={
"SPY": {
"5m": labels_5m, # pd.Series with DatetimeIndex
"15m": labels_15m,
"1h": labels_1h,
"1d": labels_1d,
}
})
- mrv.validator.res.align_labels_to_finest(labels_by_freq)[source]¶
Forward-fill each frequency’s labels onto the finest-resolution index.
The finest resolution is determined by the series with the most observations. All other series are forward-filled onto that index.
- mrv.validator.res.compute_ari_matrix(aligned, index_subset=None)[source]¶
Compute cross-frequency ARI matrix.
- Return type:
- mrv.validator.res.compute_all_metrics(aligned, index_subset=None)[source]¶
Compute ARI, AMI, and VI matrices in a single pass.
- mrv.validator.res.permute_pvalue_mean_offdiag_ari(aligned, index_subset=None, n_perm=500, seed=42)[source]¶
Permutation test for mean off-diagonal ARI.
- mrv.validator.res.subset_index_by_dates(index, start_date, end_date, tz='America/New_York')[source]¶
Select timestamps whose calendar date is in [start, end].
Uses
ambiguous="NaT"to avoid AmbiguousTimeError on DST transitions (autumn clock-change produces duplicate hour thatambiguous="infer"cannot always resolve for sub-minute data). NaT entries are dropped before masking.- Return type:
- mrv.validator.res.compute_daily_outputs(aligned, rolling_days=7, tz='America/New_York')[source]¶
Build daily summaries and rolling ARI tables.
- mrv.validator.res.analyze_labels(asset_name, labels_by_freq, rolling_days=7, event_window=None, calm_window=None)[source]¶
Run cross-frequency analysis on pre-computed labels.
- Parameters:
asset_name (
str) – Name of the asset (for logging / output).labels_by_freq (
Dict[str,Series]) –{freq: pd.Series}– regime labels at each frequency. Each Series must have a DatetimeIndex.rolling_days (
int) – Window for rolling ARI summary.event_window (
Optional[Tuple[str,str]]) – If provided, compute ARI separately for event/calm periods.calm_window (
Optional[Tuple[str,str]]) – If provided, compute ARI separately for event/calm periods.
- Return type:
- class mrv.validator.res.ResValidator(cfg=None, impact_fn=None)[source]¶
Bases:
BaseValidatorResolution Invariance validator.
Measures whether regime labels agree across time frequencies. Users provide their own labels – no model fitting is performed.
- validate(labels, event_window=None, calm_window=None)[source]¶
Run resolution invariance test on user-provided labels.
- Parameters:
labels (
Dict[str,Dict[str,Series]]) –{asset_name: {freq: pd.Series}}. Each Series must have a DatetimeIndex and contain integer labels. At least 2 frequencies per asset are required.event_window (
Optional[Tuple[str,str]]) –(start_date, end_date)for event-period analysis.calm_window (
Optional[Tuple[str,str]]) –(start_date, end_date)for calm-period analysis.
- Return type:
Metrics¶
mrv.validator.metrics – Label comparison metrics for regime diagnostics.
Standard statistical measures – not extensible by users.
- mrv.validator.metrics.ari(labels_a, labels_b)[source]¶
Adjusted Rand Index. Range [-1,1]; 1=perfect, ~0=random.
- Return type:
- mrv.validator.metrics.ami(labels_a, labels_b)[source]¶
Adjusted Mutual Information. Range [0,1]; 1=perfect.
- Return type:
- mrv.validator.metrics.nmi(labels_a, labels_b)[source]¶
Normalized Mutual Information. Range [0,1]; 1=perfect.
- Return type:
- mrv.validator.metrics.ordering_consistency(labels_a, labels_b, features)[source]¶
Ordinal ordering consistency between two label sets.
Each representation’s states are ranked by mean risk (mean feature value). Each observation is then mapped to its state’s risk rank (0=lowest, K-1=highest). Returns Spearman correlation of these risk-rank sequences.
This measures whether the two representations agree on the relative risk ordering of observations, even if the exact partition boundaries differ.
Threshold: Spearman >= 0.85 indicates stable risk ordering.
- Return type:
Attribution¶
mrv.validator.attribution – Disagreement attribution & root cause analysis.
Three attribution methods: 1. Leave-one-out factor attribution (rep validator) 2. Frequency-pair decomposition (res validator) 3. Temporal hotspot detection (both validators)
- mrv.validator.attribution.loo_factor_attribution(labels_dict, baseline_mean_ari)[source]¶
Leave-one-out factor attribution for representation invariance.
For each factor set i, remove it and recompute mean pairwise ARI from the remaining sets.
Returns:
{ "baseline_mean_ari": 0.45, "scores": {"set_label": delta_ari, ...}, "worst_contributor": "set_label", "summary": "..." }
A positive delta means removing set i improves ARI → set i is a disagreement driver.
- mrv.validator.attribution.freq_pair_attribution(ari_matrix)[source]¶
Rank frequency pairs by pairwise ARI (ascending = worst first).
Returns a list of dicts with keys: freq_a, freq_b, ari, rank.
- mrv.validator.attribution.temporal_attribution(labels_a, labels_b, window='1D', ari_threshold=0.3)[source]¶
Per-window ARI between two label sequences.
Groups timestamps by window (default: 1 calendar day), computes ARI per group, and flags hotspots where ARI <
ari_threshold.Returns DataFrame: window_start, n_obs, ari, is_hotspot.
- Return type:
Findings engine¶
mrv.validator.findings – Auto-generate SR 26-2 / OCC Bulletin 2026-13 findings from validation results. SR 11-7 (Federal Reserve, 2011) was superseded by SR 26-2 / OCC Bulletin 2026-13 on 2026-04-17; this module targets the new standard.
Severity levels: - Critical: overall_mean_ari < 0.1 - High: overall_mean_ari < ARI_THRESHOLD - Medium: any min_pairwise_ari < ARI_THRESHOLD - Low: any fallback triggered - Informational: all pass
- class mrv.validator.findings.Finding(id, severity, title, description, evidence='', recommendation='', remediation_owner='', deadline='', management_response='')[source]¶
Bases:
objectA single validation finding in SR 26-2 / OCC Bulletin 2026-13 format.
- mrv.validator.findings.classify_severity(overall_mean_ari, min_pairwise_ari=None, any_fallback=False)[source]¶
Classify finding severity based on empirical rules.
- Return type:
- mrv.validator.findings.generate_findings(results, validator_type, overrides_path=None)[source]¶
Auto-generate findings from validation results.
Continuous monitoring¶
mrv.validator.monitor – Continuous monitoring with alerting.
Provides monitor() for incremental daily validation with persistent
history tracking and configurable alerts (file + webhook).
Usage:
from mrv.validator.monitor import monitor
monitor("config.yaml", "rep", mode="init") # baseline
monitor("config.yaml", "rep", mode="incremental") # daily
- mrv.validator.monitor.monitor(config=None, validator='rep', mode='incremental', cfg=None, impact_fn=None)[source]¶
Run monitoring cycle: validate -> update history -> check alerts.
- Parameters:
validator (
str) – Only"rep"(representation invariance) is supported for continuous monitoring. Resolution invariance requires the caller to supply pre-fitted labels at each frequency; usevalidate_res()directly and build your own history loop.mode (
str) –"init"for first-run baseline,"incremental"for daily append.cfg (
Optional[Dict[str,Any]]) – Pre-loaded config dict. Overridesconfigwhen supplied.impact_fn (
callable, optional) – Optional business-impact callback passed through to the validator.
- Raises:
ValueError – If
validatoris not"rep".- Return type:
Report generation¶
mrv.validator.report – Fill LaTeX template with JSON data and compile to PDF.
All text lives in the template. This module only:
1. Replaces {{KEY}} placeholders with data values
2. Evaluates %% IF_xxx / %% ELSE / %% ELIF_xxx / %% ENDIF conditionals
3. Expands %% BEGIN_ASSET / %% END_ASSET blocks per asset
4. Compiles .tex -> .pdf via pdflatex
Both representation (Paper 1) and resolution (Paper 2) validator JSON are
supported. The renderer branches on data["test"]: the resolution case is
rendered from overall_mean_ari (the Spearman / factor-set sections are
omitted, since resolution invariance has no ordering layer).
- mrv.validator.report.generate_report(json_path, template=None, cfg=None)[source]¶
Generate a PDF report from validator JSON + LaTeX template.
Works for both representation (Paper 1) and resolution (Paper 2) result JSON. The
.texfile is always written; the PDF is produced only whenpdflatexis available onPATH(otherwiseNoneis returned).