mrv-lib 0.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mrv_lib-0.0.1/LICENSE +21 -0
- mrv_lib-0.0.1/PKG-INFO +131 -0
- mrv_lib-0.0.1/README.md +104 -0
- mrv_lib-0.0.1/pyproject.toml +44 -0
- mrv_lib-0.0.1/setup.cfg +4 -0
- mrv_lib-0.0.1/src/mrv_lib/__init__.py +25 -0
- mrv_lib-0.0.1/src/mrv_lib/core.py +353 -0
- mrv_lib-0.0.1/src/mrv_lib.egg-info/PKG-INFO +131 -0
- mrv_lib-0.0.1/src/mrv_lib.egg-info/SOURCES.txt +12 -0
- mrv_lib-0.0.1/src/mrv_lib.egg-info/dependency_links.txt +1 -0
- mrv_lib-0.0.1/src/mrv_lib.egg-info/entry_points.txt +2 -0
- mrv_lib-0.0.1/src/mrv_lib.egg-info/requires.txt +10 -0
- mrv_lib-0.0.1/src/mrv_lib.egg-info/top_level.txt +1 -0
- mrv_lib-0.0.1/tests/test_core.py +69 -0
mrv_lib-0.0.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 modelguard-lab
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
mrv_lib-0.0.1/PKG-INFO
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mrv-lib
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Market Regime Validity Library for Model Risk Governance
|
|
5
|
+
Author-email: Kai Zheng <kaizhengnz@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://mrv-lib.org
|
|
8
|
+
Project-URL: Bug Tracker, https://github.com/modelguard-lab/mrv-lib/issues
|
|
9
|
+
Project-URL: Repository, https://github.com/modelguard-lab/mrv-lib
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Topic :: Office/Business :: Financial
|
|
14
|
+
Requires-Python: >=3.8
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
License-File: LICENSE
|
|
17
|
+
Requires-Dist: numpy
|
|
18
|
+
Requires-Dist: pandas
|
|
19
|
+
Requires-Dist: scipy
|
|
20
|
+
Requires-Dist: scikit-learn
|
|
21
|
+
Provides-Extra: dev
|
|
22
|
+
Requires-Dist: pytest; extra == "dev"
|
|
23
|
+
Requires-Dist: pytest-cov; extra == "dev"
|
|
24
|
+
Requires-Dist: black; extra == "dev"
|
|
25
|
+
Requires-Dist: ruff; extra == "dev"
|
|
26
|
+
Dynamic: license-file
|
|
27
|
+
|
|
28
|
+
# mrv-lib: Market Regime Validity Library
|
|
29
|
+
|
|
30
|
+
**The Gold Standard for Model Risk Diagnostics in Non-Stationary Markets.**
|
|
31
|
+
|
|
32
|
+
mrv-lib is an open-source Python library designed to quantify and diagnose the stability of market regime identification models. Built upon the theoretical framework of **Inference Collapse** and **Ordinal Robustness**, it provides financial institutions with a rigorous toolset to meet Basel IV and SR 11-7 model risk governance requirements.
|
|
33
|
+
|
|
34
|
+
## Why mrv-lib?
|
|
35
|
+
|
|
36
|
+
Traditional market regime models often suffer from **"Stability Illusions."** A model may appear robust at daily resolutions but fail to capture structural shifts during high-frequency intraday stress events. mrv-lib exposes these vulnerabilities by measuring:
|
|
37
|
+
|
|
38
|
+
- **Representation Sensitivity:** How sensitive are your regime labels to feature engineering and preprocessing?
|
|
39
|
+
- **Resolution Dissonance:** Does your model's daily output contradict its high-frequency signals?
|
|
40
|
+
- **Identifiability Boundaries:** Is the market currently in a "Zone of Collapse" where absolute labels are mathematically unreliable?
|
|
41
|
+
|
|
42
|
+
## Key Features
|
|
43
|
+
|
|
44
|
+
### 1. Sensitivity Diagnostic (RSS)
|
|
45
|
+
|
|
46
|
+
Automated stress-testing of regime labels across multiple feature sets (Representation) and temporal scales (Resolution). It calculates the **RSS (Representation Stability Score)** to quantify model robustness.
|
|
47
|
+
|
|
48
|
+
### 2. Identifiability Index
|
|
49
|
+
|
|
50
|
+
Calculates the **Identifiability Index** (\\(\mathcal{I}\\)) based on structural drift and regime separation. It identifies the "Phase Boundaries" where model inference begins to collapse.
|
|
51
|
+
|
|
52
|
+
### 3. Ordinal Robustness
|
|
53
|
+
|
|
54
|
+
When absolute labels (ARI) collapse, mrv-lib measures **Ordinal Consistency** (Spearman's Rho) to determine if the risk ranking remains valid for fail-safe hedging.
|
|
55
|
+
|
|
56
|
+
## Installation
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
pip install mrv-lib
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Quick Start
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
import mrv_lib as mrv
|
|
66
|
+
import pandas as pd
|
|
67
|
+
|
|
68
|
+
# Load your market data (OHLCV)
|
|
69
|
+
data = pd.read_csv("market_data.csv")
|
|
70
|
+
|
|
71
|
+
# Initialize the diagnostic scanner
|
|
72
|
+
scanner = mrv.Scanner(resolution=['5m', '1h', '1d'])
|
|
73
|
+
|
|
74
|
+
# Run representation stability test
|
|
75
|
+
results = scanner.run_representation_test(data, model="HMM")
|
|
76
|
+
|
|
77
|
+
# Get the RSS (Representation Stability Score)
|
|
78
|
+
print(f"Model RSS: {results.rss_score}")
|
|
79
|
+
|
|
80
|
+
# Detect Identifiability Boundaries
|
|
81
|
+
boundary = mrv.detect_boundary(data)
|
|
82
|
+
if boundary.is_collapsed:
|
|
83
|
+
print(f"Warning: Entering Inference Collapse Zone. Identifiability Index: {boundary.index}")
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Command-Line Interface
|
|
87
|
+
|
|
88
|
+
After installation, you can run diagnostics from the shell:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
mrv-lib market_data.csv --resolution 5m 1h 1d --model HMM
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Project Layout
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
mrv-lib/
|
|
98
|
+
├── src/
|
|
99
|
+
│ └── mrv_lib/
|
|
100
|
+
│ ├── __init__.py
|
|
101
|
+
│ └── core.py
|
|
102
|
+
├── tests/
|
|
103
|
+
├── README.md
|
|
104
|
+
├── LICENSE
|
|
105
|
+
└── pyproject.toml
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Theoretical Foundation
|
|
109
|
+
|
|
110
|
+
The methodology of mrv-lib is documented in a series of peer-reviewed research papers:
|
|
111
|
+
|
|
112
|
+
- **Regime Labels Are Not Representation-Invariant:** Evidence of instability across feature sets.
|
|
113
|
+
- **Regime Labels Are Not Resolution-Invariant:** Documentation of the 14-hour lag in daily risk reporting.
|
|
114
|
+
- **Inference Collapse and Ordinal Robustness:** Defining the phase boundaries of market state identification.
|
|
115
|
+
|
|
116
|
+
For academic citations, please refer to the [documentation](https://github.com/modelguard-lab/mrv-lib#readme).
|
|
117
|
+
|
|
118
|
+
## Commercial Support & SaaS
|
|
119
|
+
|
|
120
|
+
For enterprise-grade features including real-time alerting, Basel IV Compliance Reporting, and the Fail-Safe Actuator engine, please visit [ModelGuard.co.nz](https://modelguard.co.nz).
|
|
121
|
+
|
|
122
|
+
- **ModelGuard Sentinel:** Real-time monitoring for institutional trading desks.
|
|
123
|
+
- **ModelGuard Advisory:** Professional consulting for RBNZ/APRA regulatory alignment.
|
|
124
|
+
|
|
125
|
+
## Maintainers
|
|
126
|
+
|
|
127
|
+
Maintained by **ModelGuard Lab**. Lead Architect: **Kai Zheng**.
|
|
128
|
+
|
|
129
|
+
## License
|
|
130
|
+
|
|
131
|
+
mrv-lib is released under the **MIT License**. See [LICENSE](LICENSE) for details.
|
mrv_lib-0.0.1/README.md
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# mrv-lib: Market Regime Validity Library
|
|
2
|
+
|
|
3
|
+
**The Gold Standard for Model Risk Diagnostics in Non-Stationary Markets.**
|
|
4
|
+
|
|
5
|
+
mrv-lib is an open-source Python library designed to quantify and diagnose the stability of market regime identification models. Built upon the theoretical framework of **Inference Collapse** and **Ordinal Robustness**, it provides financial institutions with a rigorous toolset to meet Basel IV and SR 11-7 model risk governance requirements.
|
|
6
|
+
|
|
7
|
+
## Why mrv-lib?
|
|
8
|
+
|
|
9
|
+
Traditional market regime models often suffer from **"Stability Illusions."** A model may appear robust at daily resolutions but fail to capture structural shifts during high-frequency intraday stress events. mrv-lib exposes these vulnerabilities by measuring:
|
|
10
|
+
|
|
11
|
+
- **Representation Sensitivity:** How sensitive are your regime labels to feature engineering and preprocessing?
|
|
12
|
+
- **Resolution Dissonance:** Does your model's daily output contradict its high-frequency signals?
|
|
13
|
+
- **Identifiability Boundaries:** Is the market currently in a "Zone of Collapse" where absolute labels are mathematically unreliable?
|
|
14
|
+
|
|
15
|
+
## Key Features
|
|
16
|
+
|
|
17
|
+
### 1. Sensitivity Diagnostic (RSS)
|
|
18
|
+
|
|
19
|
+
Automated stress-testing of regime labels across multiple feature sets (Representation) and temporal scales (Resolution). It calculates the **RSS (Representation Stability Score)** to quantify model robustness.
|
|
20
|
+
|
|
21
|
+
### 2. Identifiability Index
|
|
22
|
+
|
|
23
|
+
Calculates the **Identifiability Index** (\\(\mathcal{I}\\)) based on structural drift and regime separation. It identifies the "Phase Boundaries" where model inference begins to collapse.
|
|
24
|
+
|
|
25
|
+
### 3. Ordinal Robustness
|
|
26
|
+
|
|
27
|
+
When absolute labels (ARI) collapse, mrv-lib measures **Ordinal Consistency** (Spearman's Rho) to determine if the risk ranking remains valid for fail-safe hedging.
|
|
28
|
+
|
|
29
|
+
## Installation
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pip install mrv-lib
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Quick Start
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
import mrv_lib as mrv
|
|
39
|
+
import pandas as pd
|
|
40
|
+
|
|
41
|
+
# Load your market data (OHLCV)
|
|
42
|
+
data = pd.read_csv("market_data.csv")
|
|
43
|
+
|
|
44
|
+
# Initialize the diagnostic scanner
|
|
45
|
+
scanner = mrv.Scanner(resolution=['5m', '1h', '1d'])
|
|
46
|
+
|
|
47
|
+
# Run representation stability test
|
|
48
|
+
results = scanner.run_representation_test(data, model="HMM")
|
|
49
|
+
|
|
50
|
+
# Get the RSS (Representation Stability Score)
|
|
51
|
+
print(f"Model RSS: {results.rss_score}")
|
|
52
|
+
|
|
53
|
+
# Detect Identifiability Boundaries
|
|
54
|
+
boundary = mrv.detect_boundary(data)
|
|
55
|
+
if boundary.is_collapsed:
|
|
56
|
+
print(f"Warning: Entering Inference Collapse Zone. Identifiability Index: {boundary.index}")
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Command-Line Interface
|
|
60
|
+
|
|
61
|
+
After installation, you can run diagnostics from the shell:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
mrv-lib market_data.csv --resolution 5m 1h 1d --model HMM
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Project Layout
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
mrv-lib/
|
|
71
|
+
├── src/
|
|
72
|
+
│ └── mrv_lib/
|
|
73
|
+
│ ├── __init__.py
|
|
74
|
+
│ └── core.py
|
|
75
|
+
├── tests/
|
|
76
|
+
├── README.md
|
|
77
|
+
├── LICENSE
|
|
78
|
+
└── pyproject.toml
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Theoretical Foundation
|
|
82
|
+
|
|
83
|
+
The methodology of mrv-lib is documented in a series of peer-reviewed research papers:
|
|
84
|
+
|
|
85
|
+
- **Regime Labels Are Not Representation-Invariant:** Evidence of instability across feature sets.
|
|
86
|
+
- **Regime Labels Are Not Resolution-Invariant:** Documentation of the 14-hour lag in daily risk reporting.
|
|
87
|
+
- **Inference Collapse and Ordinal Robustness:** Defining the phase boundaries of market state identification.
|
|
88
|
+
|
|
89
|
+
For academic citations, please refer to the [documentation](https://github.com/modelguard-lab/mrv-lib#readme).
|
|
90
|
+
|
|
91
|
+
## Commercial Support & SaaS
|
|
92
|
+
|
|
93
|
+
For enterprise-grade features including real-time alerting, Basel IV Compliance Reporting, and the Fail-Safe Actuator engine, please visit [ModelGuard.co.nz](https://modelguard.co.nz).
|
|
94
|
+
|
|
95
|
+
- **ModelGuard Sentinel:** Real-time monitoring for institutional trading desks.
|
|
96
|
+
- **ModelGuard Advisory:** Professional consulting for RBNZ/APRA regulatory alignment.
|
|
97
|
+
|
|
98
|
+
## Maintainers
|
|
99
|
+
|
|
100
|
+
Maintained by **ModelGuard Lab**. Lead Architect: **Kai Zheng**.
|
|
101
|
+
|
|
102
|
+
## License
|
|
103
|
+
|
|
104
|
+
mrv-lib is released under the **MIT License**. See [LICENSE](LICENSE) for details.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "mrv-lib"
|
|
7
|
+
version = "0.0.1"
|
|
8
|
+
authors = [
|
|
9
|
+
{ name = "Kai Zheng", email = "kaizhengnz@gmail.com" },
|
|
10
|
+
]
|
|
11
|
+
description = "Market Regime Validity Library for Model Risk Governance"
|
|
12
|
+
readme = "README.md"
|
|
13
|
+
requires-python = ">=3.8"
|
|
14
|
+
license = { text = "MIT" }
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Programming Language :: Python :: 3",
|
|
17
|
+
"License :: OSI Approved :: MIT License",
|
|
18
|
+
"Operating System :: OS Independent",
|
|
19
|
+
"Topic :: Office/Business :: Financial",
|
|
20
|
+
]
|
|
21
|
+
dependencies = [
|
|
22
|
+
"numpy",
|
|
23
|
+
"pandas",
|
|
24
|
+
"scipy",
|
|
25
|
+
"scikit-learn",
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
[project.optional-dependencies]
|
|
29
|
+
dev = ["pytest", "pytest-cov", "black", "ruff"]
|
|
30
|
+
|
|
31
|
+
[project.urls]
|
|
32
|
+
Homepage = "https://mrv-lib.org"
|
|
33
|
+
"Bug Tracker" = "https://github.com/modelguard-lab/mrv-lib/issues"
|
|
34
|
+
Repository = "https://github.com/modelguard-lab/mrv-lib"
|
|
35
|
+
|
|
36
|
+
[project.scripts]
|
|
37
|
+
mrv-lib = "mrv_lib.core:main"
|
|
38
|
+
|
|
39
|
+
[tool.setuptools.packages.find]
|
|
40
|
+
where = ["src"]
|
|
41
|
+
|
|
42
|
+
[tool.pytest.ini_options]
|
|
43
|
+
pythonpath = ["src"]
|
|
44
|
+
testpaths = ["tests"]
|
mrv_lib-0.0.1/setup.cfg
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""
|
|
2
|
+
mrv-lib: Market Regime Validity Library.
|
|
3
|
+
|
|
4
|
+
Model risk diagnostics in non-stationary markets — Representation Stability,
|
|
5
|
+
Identifiability Boundaries, and Ordinal Robustness.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from mrv_lib.core import (
|
|
9
|
+
BoundaryResult,
|
|
10
|
+
RepresentationTestResult,
|
|
11
|
+
Scanner,
|
|
12
|
+
detect_boundary,
|
|
13
|
+
ordinal_consistency,
|
|
14
|
+
ari_score,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
"Scanner",
|
|
19
|
+
"RepresentationTestResult",
|
|
20
|
+
"detect_boundary",
|
|
21
|
+
"BoundaryResult",
|
|
22
|
+
"ordinal_consistency",
|
|
23
|
+
"ari_score",
|
|
24
|
+
]
|
|
25
|
+
__version__ = "0.0.1"
|
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
"""
|
|
2
|
+
mrv_lib.core — Sensitivity diagnostics, identifiability boundary, ordinal metrics, and CLI.
|
|
3
|
+
|
|
4
|
+
- Scanner / RepresentationTestResult: representation and resolution stability (RSS).
|
|
5
|
+
- detect_boundary / BoundaryResult: Identifiability Index and collapse zone.
|
|
6
|
+
- ordinal_consistency / ari_score: ordinal robustness when ARI collapses.
|
|
7
|
+
- main: command-line entry point (mrv-lib).
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import argparse
|
|
11
|
+
from dataclasses import dataclass
|
|
12
|
+
from typing import List, Optional, Union
|
|
13
|
+
|
|
14
|
+
import numpy as np
|
|
15
|
+
import pandas as pd
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# ---------------------------------------------------------------------------
|
|
19
|
+
# Representation stability (RSS)
|
|
20
|
+
# ---------------------------------------------------------------------------
|
|
21
|
+
|
|
22
|
+
@dataclass
|
|
23
|
+
class RepresentationTestResult:
|
|
24
|
+
"""Result of run_representation_test; holds RSS and per-resolution scores."""
|
|
25
|
+
|
|
26
|
+
rss_score: float
|
|
27
|
+
resolution_scores: dict
|
|
28
|
+
representation_scores: Optional[dict] = None
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class Scanner:
|
|
32
|
+
"""
|
|
33
|
+
Diagnostic scanner for regime label stability across resolutions and representations.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
def __init__(
|
|
37
|
+
self,
|
|
38
|
+
resolution: Optional[List[str]] = None,
|
|
39
|
+
feature_sets: Optional[List[str]] = None,
|
|
40
|
+
):
|
|
41
|
+
"""
|
|
42
|
+
Parameters
|
|
43
|
+
----------
|
|
44
|
+
resolution : list of str, optional
|
|
45
|
+
Temporal resolutions to test, e.g. ['5m', '1h', '1d'].
|
|
46
|
+
feature_sets : list of str, optional
|
|
47
|
+
Feature set names for representation sensitivity; defaults to standard OHLCV-derived sets.
|
|
48
|
+
"""
|
|
49
|
+
self.resolution = resolution or ["1h", "1d"]
|
|
50
|
+
self.feature_sets = feature_sets or ["returns", "volatility", "volume"]
|
|
51
|
+
|
|
52
|
+
def run_representation_test(
|
|
53
|
+
self,
|
|
54
|
+
data: pd.DataFrame,
|
|
55
|
+
model: str = "HMM",
|
|
56
|
+
**kwargs,
|
|
57
|
+
) -> RepresentationTestResult:
|
|
58
|
+
"""
|
|
59
|
+
Run representation stability test across resolutions and feature sets.
|
|
60
|
+
|
|
61
|
+
Parameters
|
|
62
|
+
----------
|
|
63
|
+
data : DataFrame
|
|
64
|
+
OHLCV market data (columns expected: open, high, low, close, volume or similar).
|
|
65
|
+
model : str
|
|
66
|
+
Regime model type, e.g. "HMM". Used for extensibility.
|
|
67
|
+
**kwargs
|
|
68
|
+
Passed to internal model fit (e.g. n_states for HMM).
|
|
69
|
+
|
|
70
|
+
Returns
|
|
71
|
+
-------
|
|
72
|
+
RepresentationTestResult
|
|
73
|
+
Contains rss_score and per-resolution / per-representation scores.
|
|
74
|
+
"""
|
|
75
|
+
data = _ensure_ohlcv(data)
|
|
76
|
+
resolution_scores = {}
|
|
77
|
+
for res in self.resolution:
|
|
78
|
+
resolution_scores[res] = _score_at_resolution(data, res, model, **kwargs)
|
|
79
|
+
representation_scores = {}
|
|
80
|
+
for fs in self.feature_sets:
|
|
81
|
+
representation_scores[fs] = _score_representation(data, fs, model, **kwargs)
|
|
82
|
+
|
|
83
|
+
rss = _compute_rss(resolution_scores, representation_scores)
|
|
84
|
+
return RepresentationTestResult(
|
|
85
|
+
rss_score=rss,
|
|
86
|
+
resolution_scores=resolution_scores,
|
|
87
|
+
representation_scores=representation_scores,
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _ensure_ohlcv(data: pd.DataFrame) -> pd.DataFrame:
|
|
92
|
+
"""Normalize column names to lower-case open/high/low/close/volume if present."""
|
|
93
|
+
df = data.copy()
|
|
94
|
+
cols = {c.lower(): c for c in df.columns}
|
|
95
|
+
for name in ["open", "high", "low", "close", "volume"]:
|
|
96
|
+
if name in cols and df.columns[df.columns.get_loc(cols[name])] != name:
|
|
97
|
+
df = df.rename(columns={cols[name]: name})
|
|
98
|
+
if "close" not in df.columns and len(df.columns) >= 4:
|
|
99
|
+
df.columns = ["open", "high", "low", "close"] + list(df.columns[4:])
|
|
100
|
+
return df
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _score_at_resolution(
|
|
104
|
+
data: pd.DataFrame,
|
|
105
|
+
resolution: str,
|
|
106
|
+
model: str,
|
|
107
|
+
**kwargs,
|
|
108
|
+
) -> float:
|
|
109
|
+
"""
|
|
110
|
+
Placeholder: score stability at a given temporal resolution.
|
|
111
|
+
In production, resample data to resolution and compare regime consistency.
|
|
112
|
+
"""
|
|
113
|
+
close = data["close"] if "close" in data.columns else data.iloc[:, 3]
|
|
114
|
+
returns = close.pct_change().dropna()
|
|
115
|
+
if returns.empty or returns.std() == 0:
|
|
116
|
+
return 0.0
|
|
117
|
+
vol = returns.std()
|
|
118
|
+
vol_norm = min(vol * 10, 1.0)
|
|
119
|
+
return float(1.0 - vol_norm)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def _score_representation(
|
|
123
|
+
data: pd.DataFrame,
|
|
124
|
+
feature_set: str,
|
|
125
|
+
model: str,
|
|
126
|
+
**kwargs,
|
|
127
|
+
) -> float:
|
|
128
|
+
"""
|
|
129
|
+
Placeholder: score stability across feature set (representation).
|
|
130
|
+
In production, fit regime model on different feature sets and compare labels.
|
|
131
|
+
"""
|
|
132
|
+
return 0.75
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def _compute_rss(
|
|
136
|
+
resolution_scores: dict,
|
|
137
|
+
representation_scores: dict,
|
|
138
|
+
) -> float:
|
|
139
|
+
"""
|
|
140
|
+
Representation Stability Score: aggregate of resolution and representation stability.
|
|
141
|
+
RSS in [0, 1]; higher = more robust.
|
|
142
|
+
"""
|
|
143
|
+
r_scores = list(resolution_scores.values()) if resolution_scores else [0.0]
|
|
144
|
+
rep_scores = list(representation_scores.values()) if representation_scores else [0.0]
|
|
145
|
+
all_scores = r_scores + rep_scores
|
|
146
|
+
return float(np.clip(np.mean(all_scores), 0.0, 1.0))
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
# ---------------------------------------------------------------------------
|
|
150
|
+
# Identifiability boundary
|
|
151
|
+
# ---------------------------------------------------------------------------
|
|
152
|
+
|
|
153
|
+
DEFAULT_COLLAPSE_THRESHOLD = 0.3
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
@dataclass
|
|
157
|
+
class BoundaryResult:
|
|
158
|
+
"""
|
|
159
|
+
Result of identifiability boundary detection.
|
|
160
|
+
|
|
161
|
+
Attributes
|
|
162
|
+
----------
|
|
163
|
+
index : float
|
|
164
|
+
Identifiability Index (I); higher = more identifiable regime.
|
|
165
|
+
is_collapsed : bool
|
|
166
|
+
True if index below threshold (Inference Collapse Zone).
|
|
167
|
+
threshold : float
|
|
168
|
+
Threshold used for is_collapsed.
|
|
169
|
+
"""
|
|
170
|
+
|
|
171
|
+
index: float
|
|
172
|
+
is_collapsed: bool
|
|
173
|
+
threshold: float = DEFAULT_COLLAPSE_THRESHOLD
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def detect_boundary(
|
|
177
|
+
data: pd.DataFrame,
|
|
178
|
+
threshold: float = DEFAULT_COLLAPSE_THRESHOLD,
|
|
179
|
+
window: Optional[int] = None,
|
|
180
|
+
) -> BoundaryResult:
|
|
181
|
+
"""
|
|
182
|
+
Detect identifiability boundary: compute Identifiability Index (I) and
|
|
183
|
+
whether the market is in a "Zone of Collapse".
|
|
184
|
+
|
|
185
|
+
Parameters
|
|
186
|
+
----------
|
|
187
|
+
data : DataFrame
|
|
188
|
+
OHLCV market data.
|
|
189
|
+
threshold : float
|
|
190
|
+
Index below this value is considered collapsed (default 0.3).
|
|
191
|
+
window : int, optional
|
|
192
|
+
Rolling window for drift/separation; default uses 20% of length.
|
|
193
|
+
|
|
194
|
+
Returns
|
|
195
|
+
-------
|
|
196
|
+
BoundaryResult
|
|
197
|
+
.index (Identifiability Index), .is_collapsed, .threshold.
|
|
198
|
+
"""
|
|
199
|
+
df = data.copy()
|
|
200
|
+
if "close" not in df.columns and len(df.columns) >= 4:
|
|
201
|
+
df = df.rename(columns={df.columns[3]: "close"})
|
|
202
|
+
close = df["close"] if "close" in df.columns else df.iloc[:, 3]
|
|
203
|
+
close = pd.Series(close).dropna()
|
|
204
|
+
n = len(close)
|
|
205
|
+
if n < 2:
|
|
206
|
+
return BoundaryResult(index=0.0, is_collapsed=True, threshold=threshold)
|
|
207
|
+
|
|
208
|
+
w = window or max(int(0.2 * n), 2)
|
|
209
|
+
returns = close.pct_change().dropna()
|
|
210
|
+
if len(returns) < w:
|
|
211
|
+
return BoundaryResult(index=0.0, is_collapsed=True, threshold=threshold)
|
|
212
|
+
|
|
213
|
+
vol = returns.rolling(w).std().dropna()
|
|
214
|
+
if vol.empty or vol.iloc[-1] == 0:
|
|
215
|
+
drift = 0.0
|
|
216
|
+
else:
|
|
217
|
+
vol_early = vol.iloc[: len(vol) // 2].mean()
|
|
218
|
+
vol_late = vol.iloc[len(vol) // 2 :].mean()
|
|
219
|
+
drift = abs(vol_late - vol_early) / (vol.mean() + 1e-10)
|
|
220
|
+
drift = min(drift, 1.0)
|
|
221
|
+
|
|
222
|
+
identifiability_index = float(np.clip(1.0 - drift, 0.0, 1.0))
|
|
223
|
+
if np.isnan(identifiability_index):
|
|
224
|
+
identifiability_index = 0.0
|
|
225
|
+
is_collapsed = identifiability_index < threshold
|
|
226
|
+
|
|
227
|
+
return BoundaryResult(
|
|
228
|
+
index=identifiability_index,
|
|
229
|
+
is_collapsed=is_collapsed,
|
|
230
|
+
threshold=threshold,
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
# ---------------------------------------------------------------------------
|
|
235
|
+
# Ordinal robustness (metrics)
|
|
236
|
+
# ---------------------------------------------------------------------------
|
|
237
|
+
|
|
238
|
+
def ordinal_consistency(
|
|
239
|
+
y_true: Union[np.ndarray, pd.Series],
|
|
240
|
+
y_pred: Union[np.ndarray, pd.Series],
|
|
241
|
+
) -> float:
|
|
242
|
+
"""
|
|
243
|
+
Ordinal Consistency: Spearman's rank correlation between true and predicted
|
|
244
|
+
regime order. Valid when ARI is low but ranking for hedging remains meaningful.
|
|
245
|
+
|
|
246
|
+
Parameters
|
|
247
|
+
----------
|
|
248
|
+
y_true : array-like
|
|
249
|
+
True regime labels or risk ordering (numeric).
|
|
250
|
+
y_pred : array-like
|
|
251
|
+
Predicted regime labels or risk ordering (numeric).
|
|
252
|
+
|
|
253
|
+
Returns
|
|
254
|
+
-------
|
|
255
|
+
float
|
|
256
|
+
Spearman's Rho in [-1, 1]; higher = more ordinal consistency.
|
|
257
|
+
"""
|
|
258
|
+
from scipy.stats import spearmanr
|
|
259
|
+
|
|
260
|
+
y_true = np.asarray(y_true).ravel()
|
|
261
|
+
y_pred = np.asarray(y_pred).ravel()
|
|
262
|
+
if len(y_true) != len(y_pred) or len(y_true) < 2:
|
|
263
|
+
return 0.0
|
|
264
|
+
rho, _ = spearmanr(y_true, y_pred)
|
|
265
|
+
return float(np.clip(rho, -1.0, 1.0) if not np.isnan(rho) else 0.0)
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def ari_score(
|
|
269
|
+
y_true: Union[np.ndarray, pd.Series],
|
|
270
|
+
y_pred: Union[np.ndarray, pd.Series],
|
|
271
|
+
) -> float:
|
|
272
|
+
"""
|
|
273
|
+
Adjusted Rand Index for absolute label agreement.
|
|
274
|
+
Use ordinal_consistency when ARI collapses but ranking still matters.
|
|
275
|
+
|
|
276
|
+
Parameters
|
|
277
|
+
----------
|
|
278
|
+
y_true : array-like
|
|
279
|
+
True cluster/regime labels (integer).
|
|
280
|
+
y_pred : array-like
|
|
281
|
+
Predicted cluster/regime labels (integer).
|
|
282
|
+
|
|
283
|
+
Returns
|
|
284
|
+
-------
|
|
285
|
+
float
|
|
286
|
+
ARI in [-1, 1]; 1 = perfect match.
|
|
287
|
+
"""
|
|
288
|
+
try:
|
|
289
|
+
from sklearn.metrics import adjusted_rand_score
|
|
290
|
+
except ImportError:
|
|
291
|
+
return 0.0
|
|
292
|
+
y_true = np.asarray(y_true).ravel().astype(int)
|
|
293
|
+
y_pred = np.asarray(y_pred).ravel().astype(int)
|
|
294
|
+
if len(y_true) != len(y_pred) or len(y_true) < 2:
|
|
295
|
+
return 0.0
|
|
296
|
+
return float(adjusted_rand_score(y_true, y_pred))
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
# ---------------------------------------------------------------------------
|
|
300
|
+
# CLI
|
|
301
|
+
# ---------------------------------------------------------------------------
|
|
302
|
+
|
|
303
|
+
def _parse_args(argv: Optional[List[str]] = None) -> argparse.Namespace:
|
|
304
|
+
parser = argparse.ArgumentParser(
|
|
305
|
+
prog="mrv-lib",
|
|
306
|
+
description=(
|
|
307
|
+
"Market Regime Validity diagnostics: "
|
|
308
|
+
"Representation Stability (RSS) and Identifiability Index."
|
|
309
|
+
),
|
|
310
|
+
)
|
|
311
|
+
parser.add_argument(
|
|
312
|
+
"csv",
|
|
313
|
+
help="Path to OHLCV market data CSV file.",
|
|
314
|
+
)
|
|
315
|
+
parser.add_argument(
|
|
316
|
+
"-r",
|
|
317
|
+
"--resolution",
|
|
318
|
+
nargs="+",
|
|
319
|
+
default=["5m", "1h", "1d"],
|
|
320
|
+
help="Temporal resolutions to test, e.g. 5m 1h 1d (default: 5m 1h 1d).",
|
|
321
|
+
)
|
|
322
|
+
parser.add_argument(
|
|
323
|
+
"-m",
|
|
324
|
+
"--model",
|
|
325
|
+
default="HMM",
|
|
326
|
+
help="Regime model type label (passed through to Scanner; default: HMM).",
|
|
327
|
+
)
|
|
328
|
+
return parser.parse_args(argv)
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
def main(argv: Optional[List[str]] = None) -> None:
|
|
332
|
+
"""Command-line entry point. Example: mrv-lib market_data.csv --resolution 5m 1h 1d --model HMM."""
|
|
333
|
+
args = _parse_args(argv)
|
|
334
|
+
|
|
335
|
+
data = pd.read_csv(args.csv)
|
|
336
|
+
|
|
337
|
+
scanner = Scanner(resolution=args.resolution)
|
|
338
|
+
results = scanner.run_representation_test(data, model=args.model)
|
|
339
|
+
|
|
340
|
+
boundary = detect_boundary(data)
|
|
341
|
+
|
|
342
|
+
print(f"File: {args.csv}")
|
|
343
|
+
print(f"Resolutions: {args.resolution}")
|
|
344
|
+
print(f"Model: {args.model}")
|
|
345
|
+
print(f"RSS (Representation Stability Score): {results.rss_score:.4f}")
|
|
346
|
+
print(
|
|
347
|
+
f"Identifiability Index: {boundary.index:.4f} "
|
|
348
|
+
f"(threshold={boundary.threshold:.2f})"
|
|
349
|
+
)
|
|
350
|
+
if boundary.is_collapsed:
|
|
351
|
+
print("Status: COLLAPSED (Inference Collapse Zone)")
|
|
352
|
+
else:
|
|
353
|
+
print("Status: STABLE (outside collapse zone)")
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mrv-lib
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Market Regime Validity Library for Model Risk Governance
|
|
5
|
+
Author-email: Kai Zheng <kaizhengnz@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://mrv-lib.org
|
|
8
|
+
Project-URL: Bug Tracker, https://github.com/modelguard-lab/mrv-lib/issues
|
|
9
|
+
Project-URL: Repository, https://github.com/modelguard-lab/mrv-lib
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Topic :: Office/Business :: Financial
|
|
14
|
+
Requires-Python: >=3.8
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
License-File: LICENSE
|
|
17
|
+
Requires-Dist: numpy
|
|
18
|
+
Requires-Dist: pandas
|
|
19
|
+
Requires-Dist: scipy
|
|
20
|
+
Requires-Dist: scikit-learn
|
|
21
|
+
Provides-Extra: dev
|
|
22
|
+
Requires-Dist: pytest; extra == "dev"
|
|
23
|
+
Requires-Dist: pytest-cov; extra == "dev"
|
|
24
|
+
Requires-Dist: black; extra == "dev"
|
|
25
|
+
Requires-Dist: ruff; extra == "dev"
|
|
26
|
+
Dynamic: license-file
|
|
27
|
+
|
|
28
|
+
# mrv-lib: Market Regime Validity Library
|
|
29
|
+
|
|
30
|
+
**The Gold Standard for Model Risk Diagnostics in Non-Stationary Markets.**
|
|
31
|
+
|
|
32
|
+
mrv-lib is an open-source Python library designed to quantify and diagnose the stability of market regime identification models. Built upon the theoretical framework of **Inference Collapse** and **Ordinal Robustness**, it provides financial institutions with a rigorous toolset to meet Basel IV and SR 11-7 model risk governance requirements.
|
|
33
|
+
|
|
34
|
+
## Why mrv-lib?
|
|
35
|
+
|
|
36
|
+
Traditional market regime models often suffer from **"Stability Illusions."** A model may appear robust at daily resolutions but fail to capture structural shifts during high-frequency intraday stress events. mrv-lib exposes these vulnerabilities by measuring:
|
|
37
|
+
|
|
38
|
+
- **Representation Sensitivity:** How sensitive are your regime labels to feature engineering and preprocessing?
|
|
39
|
+
- **Resolution Dissonance:** Does your model's daily output contradict its high-frequency signals?
|
|
40
|
+
- **Identifiability Boundaries:** Is the market currently in a "Zone of Collapse" where absolute labels are mathematically unreliable?
|
|
41
|
+
|
|
42
|
+
## Key Features
|
|
43
|
+
|
|
44
|
+
### 1. Sensitivity Diagnostic (RSS)
|
|
45
|
+
|
|
46
|
+
Automated stress-testing of regime labels across multiple feature sets (Representation) and temporal scales (Resolution). It calculates the **RSS (Representation Stability Score)** to quantify model robustness.
|
|
47
|
+
|
|
48
|
+
### 2. Identifiability Index
|
|
49
|
+
|
|
50
|
+
Calculates the **Identifiability Index** (\\(\mathcal{I}\\)) based on structural drift and regime separation. It identifies the "Phase Boundaries" where model inference begins to collapse.
|
|
51
|
+
|
|
52
|
+
### 3. Ordinal Robustness
|
|
53
|
+
|
|
54
|
+
When absolute labels (ARI) collapse, mrv-lib measures **Ordinal Consistency** (Spearman's Rho) to determine if the risk ranking remains valid for fail-safe hedging.
|
|
55
|
+
|
|
56
|
+
## Installation
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
pip install mrv-lib
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Quick Start
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
import mrv_lib as mrv
|
|
66
|
+
import pandas as pd
|
|
67
|
+
|
|
68
|
+
# Load your market data (OHLCV)
|
|
69
|
+
data = pd.read_csv("market_data.csv")
|
|
70
|
+
|
|
71
|
+
# Initialize the diagnostic scanner
|
|
72
|
+
scanner = mrv.Scanner(resolution=['5m', '1h', '1d'])
|
|
73
|
+
|
|
74
|
+
# Run representation stability test
|
|
75
|
+
results = scanner.run_representation_test(data, model="HMM")
|
|
76
|
+
|
|
77
|
+
# Get the RSS (Representation Stability Score)
|
|
78
|
+
print(f"Model RSS: {results.rss_score}")
|
|
79
|
+
|
|
80
|
+
# Detect Identifiability Boundaries
|
|
81
|
+
boundary = mrv.detect_boundary(data)
|
|
82
|
+
if boundary.is_collapsed:
|
|
83
|
+
print(f"Warning: Entering Inference Collapse Zone. Identifiability Index: {boundary.index}")
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Command-Line Interface
|
|
87
|
+
|
|
88
|
+
After installation, you can run diagnostics from the shell:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
mrv-lib market_data.csv --resolution 5m 1h 1d --model HMM
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Project Layout
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
mrv-lib/
|
|
98
|
+
├── src/
|
|
99
|
+
│ └── mrv_lib/
|
|
100
|
+
│ ├── __init__.py
|
|
101
|
+
│ └── core.py
|
|
102
|
+
├── tests/
|
|
103
|
+
├── README.md
|
|
104
|
+
├── LICENSE
|
|
105
|
+
└── pyproject.toml
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Theoretical Foundation
|
|
109
|
+
|
|
110
|
+
The methodology of mrv-lib is documented in a series of peer-reviewed research papers:
|
|
111
|
+
|
|
112
|
+
- **Regime Labels Are Not Representation-Invariant:** Evidence of instability across feature sets.
|
|
113
|
+
- **Regime Labels Are Not Resolution-Invariant:** Documentation of the 14-hour lag in daily risk reporting.
|
|
114
|
+
- **Inference Collapse and Ordinal Robustness:** Defining the phase boundaries of market state identification.
|
|
115
|
+
|
|
116
|
+
For academic citations, please refer to the [documentation](https://github.com/modelguard-lab/mrv-lib#readme).
|
|
117
|
+
|
|
118
|
+
## Commercial Support & SaaS
|
|
119
|
+
|
|
120
|
+
For enterprise-grade features including real-time alerting, Basel IV Compliance Reporting, and the Fail-Safe Actuator engine, please visit [ModelGuard.co.nz](https://modelguard.co.nz).
|
|
121
|
+
|
|
122
|
+
- **ModelGuard Sentinel:** Real-time monitoring for institutional trading desks.
|
|
123
|
+
- **ModelGuard Advisory:** Professional consulting for RBNZ/APRA regulatory alignment.
|
|
124
|
+
|
|
125
|
+
## Maintainers
|
|
126
|
+
|
|
127
|
+
Maintained by **ModelGuard Lab**. Lead Architect: **Kai Zheng**.
|
|
128
|
+
|
|
129
|
+
## License
|
|
130
|
+
|
|
131
|
+
mrv-lib is released under the **MIT License**. See [LICENSE](LICENSE) for details.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
src/mrv_lib/__init__.py
|
|
5
|
+
src/mrv_lib/core.py
|
|
6
|
+
src/mrv_lib.egg-info/PKG-INFO
|
|
7
|
+
src/mrv_lib.egg-info/SOURCES.txt
|
|
8
|
+
src/mrv_lib.egg-info/dependency_links.txt
|
|
9
|
+
src/mrv_lib.egg-info/entry_points.txt
|
|
10
|
+
src/mrv_lib.egg-info/requires.txt
|
|
11
|
+
src/mrv_lib.egg-info/top_level.txt
|
|
12
|
+
tests/test_core.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
mrv_lib
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""Tests for mrv_lib.core: Scanner, detect_boundary, ordinal_consistency."""
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
import pandas as pd
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
from mrv_lib import (
|
|
8
|
+
BoundaryResult,
|
|
9
|
+
RepresentationTestResult,
|
|
10
|
+
Scanner,
|
|
11
|
+
detect_boundary,
|
|
12
|
+
ordinal_consistency,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _ohlcv_df(n: int = 100) -> pd.DataFrame:
|
|
17
|
+
np.random.seed(42)
|
|
18
|
+
close = np.cumsum(np.random.randn(n)) + 100.0
|
|
19
|
+
open_ = np.roll(close, 1)
|
|
20
|
+
open_[0] = close[0]
|
|
21
|
+
high = np.maximum(open_, close) + np.abs(np.random.randn(n)) * 0.5
|
|
22
|
+
low = np.minimum(open_, close) - np.abs(np.random.randn(n)) * 0.5
|
|
23
|
+
volume = np.random.randint(1000, 10000, n)
|
|
24
|
+
return pd.DataFrame({
|
|
25
|
+
"open": open_,
|
|
26
|
+
"high": high,
|
|
27
|
+
"low": low,
|
|
28
|
+
"close": close,
|
|
29
|
+
"volume": volume,
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def test_scanner_run_representation_test_returns_result():
|
|
34
|
+
data = _ohlcv_df(50)
|
|
35
|
+
scanner = Scanner(resolution=["1h", "1d"])
|
|
36
|
+
result = scanner.run_representation_test(data, model="HMM")
|
|
37
|
+
assert isinstance(result, RepresentationTestResult)
|
|
38
|
+
assert 0 <= result.rss_score <= 1
|
|
39
|
+
assert "1h" in result.resolution_scores
|
|
40
|
+
assert "1d" in result.resolution_scores
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def test_detect_boundary_returns_boundary_result():
|
|
44
|
+
data = _ohlcv_df(50)
|
|
45
|
+
boundary = detect_boundary(data)
|
|
46
|
+
assert isinstance(boundary, BoundaryResult)
|
|
47
|
+
assert 0 <= boundary.index <= 1
|
|
48
|
+
assert boundary.is_collapsed == (boundary.index < boundary.threshold)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def test_detect_boundary_short_series_collapsed():
|
|
52
|
+
data = _ohlcv_df(3)
|
|
53
|
+
boundary = detect_boundary(data)
|
|
54
|
+
assert boundary.is_collapsed is True
|
|
55
|
+
assert boundary.index == 0.0
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def test_ordinal_consistency_perfect_correlation():
|
|
59
|
+
y = np.array([1, 2, 3, 4, 5])
|
|
60
|
+
assert ordinal_consistency(y, y) == pytest.approx(1.0)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def test_ordinal_consistency_reverse_correlation():
|
|
64
|
+
y = np.array([1, 2, 3, 4, 5])
|
|
65
|
+
assert ordinal_consistency(y, -y) == pytest.approx(-1.0)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def test_ordinal_consistency_short_returns_zero():
|
|
69
|
+
assert ordinal_consistency([1], [2]) == 0.0
|