macrostress 0.1.0__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.
Files changed (126) hide show
  1. macrostress-0.1.0/LICENSE +21 -0
  2. macrostress-0.1.0/PKG-INFO +154 -0
  3. macrostress-0.1.0/README.md +117 -0
  4. macrostress-0.1.0/pyproject.toml +87 -0
  5. macrostress-0.1.0/pyproject.toml.orig +85 -0
  6. macrostress-0.1.0/src/mst/README.md +32 -0
  7. macrostress-0.1.0/src/mst/__init__.py +5 -0
  8. macrostress-0.1.0/src/mst/contracts.py +48 -0
  9. macrostress-0.1.0/src/mst/corpus/README.md +35 -0
  10. macrostress-0.1.0/src/mst/corpus/__init__.py +1 -0
  11. macrostress-0.1.0/src/mst/corpus/__main__.py +7 -0
  12. macrostress-0.1.0/src/mst/corpus/cli.py +314 -0
  13. macrostress-0.1.0/src/mst/corpus/convert.py +357 -0
  14. macrostress-0.1.0/src/mst/corpus/embed.py +59 -0
  15. macrostress-0.1.0/src/mst/corpus/episodes.py +287 -0
  16. macrostress-0.1.0/src/mst/corpus/fetch.py +191 -0
  17. macrostress-0.1.0/src/mst/corpus/index.py +474 -0
  18. macrostress-0.1.0/src/mst/corpus/manifest.py +194 -0
  19. macrostress-0.1.0/src/mst/corpus/series.py +234 -0
  20. macrostress-0.1.0/src/mst/corpus/tool.py +124 -0
  21. macrostress-0.1.0/src/mst/data/README.md +39 -0
  22. macrostress-0.1.0/src/mst/data/__init__.py +1 -0
  23. macrostress-0.1.0/src/mst/data/__main__.py +7 -0
  24. macrostress-0.1.0/src/mst/data/cbr.py +176 -0
  25. macrostress-0.1.0/src/mst/data/cbr_stats.py +193 -0
  26. macrostress-0.1.0/src/mst/data/cli.py +70 -0
  27. macrostress-0.1.0/src/mst/data/daily.py +51 -0
  28. macrostress-0.1.0/src/mst/data/fred.py +45 -0
  29. macrostress-0.1.0/src/mst/data/mintsifry-roots.pem +113 -0
  30. macrostress-0.1.0/src/mst/data/moex.py +186 -0
  31. macrostress-0.1.0/src/mst/data/release.py +12 -0
  32. macrostress-0.1.0/src/mst/data/rosstat.py +282 -0
  33. macrostress-0.1.0/src/mst/data/sources.py +115 -0
  34. macrostress-0.1.0/src/mst/data/sources.toml +228 -0
  35. macrostress-0.1.0/src/mst/data/store.py +118 -0
  36. macrostress-0.1.0/src/mst/data/update.py +40 -0
  37. macrostress-0.1.0/src/mst/drivers/README.md +38 -0
  38. macrostress-0.1.0/src/mst/drivers/__init__.py +1 -0
  39. macrostress-0.1.0/src/mst/drivers/__main__.py +11 -0
  40. macrostress-0.1.0/src/mst/drivers/catalogs/README.md +19 -0
  41. macrostress-0.1.0/src/mst/drivers/catalogs/__init__.py +17 -0
  42. macrostress-0.1.0/src/mst/drivers/catalogs/base.py +57 -0
  43. macrostress-0.1.0/src/mst/drivers/catalogs/cbr.py +62 -0
  44. macrostress-0.1.0/src/mst/drivers/catalogs/moex.py +240 -0
  45. macrostress-0.1.0/src/mst/drivers/catalogs/rosstat.py +62 -0
  46. macrostress-0.1.0/src/mst/drivers/check.py +98 -0
  47. macrostress-0.1.0/src/mst/drivers/cli.py +109 -0
  48. macrostress-0.1.0/src/mst/drivers/estimate.py +411 -0
  49. macrostress-0.1.0/src/mst/drivers/objects.py +39 -0
  50. macrostress-0.1.0/src/mst/drivers/registry.py +216 -0
  51. macrostress-0.1.0/src/mst/drivers/registry.toml +138 -0
  52. macrostress-0.1.0/src/mst/drivers/simulate.py +152 -0
  53. macrostress-0.1.0/src/mst/drivers/spec.py +33 -0
  54. macrostress-0.1.0/src/mst/drivers/spec.toml +24 -0
  55. macrostress-0.1.0/src/mst/drivers/update.py +82 -0
  56. macrostress-0.1.0/src/mst/drivers/version.py +215 -0
  57. macrostress-0.1.0/src/mst/engine/README.md +21 -0
  58. macrostress-0.1.0/src/mst/engine/__init__.py +11 -0
  59. macrostress-0.1.0/src/mst/engine/dispatch.py +269 -0
  60. macrostress-0.1.0/src/mst/engine/model_catalog.py +57 -0
  61. macrostress-0.1.0/src/mst/engine/run.py +102 -0
  62. macrostress-0.1.0/src/mst/engine/status.py +21 -0
  63. macrostress-0.1.0/src/mst/importer.py +25 -0
  64. macrostress-0.1.0/src/mst/jobs.py +246 -0
  65. macrostress-0.1.0/src/mst/macro/README.md +36 -0
  66. macrostress-0.1.0/src/mst/macro/__init__.py +1 -0
  67. macrostress-0.1.0/src/mst/macro/__main__.py +7 -0
  68. macrostress-0.1.0/src/mst/macro/backtest.py +269 -0
  69. macrostress-0.1.0/src/mst/macro/backtest.toml +157 -0
  70. macrostress-0.1.0/src/mst/macro/cli.py +192 -0
  71. macrostress-0.1.0/src/mst/macro/estimate.py +377 -0
  72. macrostress-0.1.0/src/mst/macro/forecast.py +307 -0
  73. macrostress-0.1.0/src/mst/macro/identify.py +120 -0
  74. macrostress-0.1.0/src/mst/macro/panel.py +94 -0
  75. macrostress-0.1.0/src/mst/macro/registry.py +120 -0
  76. macrostress-0.1.0/src/mst/macro/registry.toml +194 -0
  77. macrostress-0.1.0/src/mst/macro/spec.py +62 -0
  78. macrostress-0.1.0/src/mst/macro/spec.toml +55 -0
  79. macrostress-0.1.0/src/mst/macro/sv.py +75 -0
  80. macrostress-0.1.0/src/mst/macro/var.py +59 -0
  81. macrostress-0.1.0/src/mst/macro/version.py +87 -0
  82. macrostress-0.1.0/src/mst/mcp.py +398 -0
  83. macrostress-0.1.0/src/mst/plugin.py +288 -0
  84. macrostress-0.1.0/src/mst/plugin_sdk/README.md +25 -0
  85. macrostress-0.1.0/src/mst/plugin_sdk/__init__.py +62 -0
  86. macrostress-0.1.0/src/mst/plugin_sdk/fx.py +64 -0
  87. macrostress-0.1.0/src/mst/plugin_sdk/model.py +156 -0
  88. macrostress-0.1.0/src/mst/plugin_sdk/paths.py +114 -0
  89. macrostress-0.1.0/src/mst/plugin_sdk/plugin.py +45 -0
  90. macrostress-0.1.0/src/mst/plugin_sdk/results.py +139 -0
  91. macrostress-0.1.0/src/mst/plugin_sdk/testing.py +120 -0
  92. macrostress-0.1.0/src/mst/plugin_sdk/version.py +45 -0
  93. macrostress-0.1.0/src/mst/plugins/README.md +25 -0
  94. macrostress-0.1.0/src/mst/plugins/__init__.py +345 -0
  95. macrostress-0.1.0/src/mst/plugins/bond/__init__.py +6 -0
  96. macrostress-0.1.0/src/mst/plugins/bond/model.py +88 -0
  97. macrostress-0.1.0/src/mst/plugins/bond/plugin.py +37 -0
  98. macrostress-0.1.0/src/mst/plugins/credit/__init__.py +6 -0
  99. macrostress-0.1.0/src/mst/plugins/credit/model.py +88 -0
  100. macrostress-0.1.0/src/mst/plugins/credit/plugin.py +37 -0
  101. macrostress-0.1.0/src/mst/plugins/equity/__init__.py +6 -0
  102. macrostress-0.1.0/src/mst/plugins/equity/model.py +87 -0
  103. macrostress-0.1.0/src/mst/plugins/equity/plugin.py +36 -0
  104. macrostress-0.1.0/src/mst/plugins/housing/__init__.py +6 -0
  105. macrostress-0.1.0/src/mst/plugins/housing/model.py +93 -0
  106. macrostress-0.1.0/src/mst/plugins/housing/plugin.py +41 -0
  107. macrostress-0.1.0/src/mst/portfolio/README.md +29 -0
  108. macrostress-0.1.0/src/mst/portfolio/__init__.py +5 -0
  109. macrostress-0.1.0/src/mst/portfolio/report.py +121 -0
  110. macrostress-0.1.0/src/mst/portfolio/spec.py +29 -0
  111. macrostress-0.1.0/src/mst/portfolio/tool.py +220 -0
  112. macrostress-0.1.0/src/mst/runs.py +309 -0
  113. macrostress-0.1.0/src/mst/scenario/README.md +32 -0
  114. macrostress-0.1.0/src/mst/scenario/__init__.py +1 -0
  115. macrostress-0.1.0/src/mst/scenario/build.py +165 -0
  116. macrostress-0.1.0/src/mst/scenario/contracts.py +89 -0
  117. macrostress-0.1.0/src/mst/scenario/spec.py +17 -0
  118. macrostress-0.1.0/src/mst/scenario/tool.py +83 -0
  119. macrostress-0.1.0/src/mst/store.py +140 -0
  120. macrostress-0.1.0/src/mst/templates/plugin/README.md.tmpl +33 -0
  121. macrostress-0.1.0/src/mst/templates/plugin/__init__.py.tmpl +6 -0
  122. macrostress-0.1.0/src/mst/templates/plugin/model.py.tmpl +83 -0
  123. macrostress-0.1.0/src/mst/templates/plugin/plugin.py.tmpl +41 -0
  124. macrostress-0.1.0/src/mst/templates/plugin/tests/__init__.py.tmpl +1 -0
  125. macrostress-0.1.0/src/mst/templates/plugin/tests/test_plugin.py.tmpl +9 -0
  126. macrostress-0.1.0/src/mst/versions.py +83 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 LeoLourson
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.
@@ -0,0 +1,154 @@
1
+ Metadata-Version: 2.4
2
+ Name: macrostress
3
+ Version: 0.1.0
4
+ Summary: Portfolio stress testing over macroeconomic scenarios, as an MCP server
5
+ Keywords: stress-testing,scenario-analysis,macroeconomic,bvar,portfolio-risk,mcp
6
+ Author: LeoLourson
7
+ License-Expression: MIT
8
+ License-File: LICENSE
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: Intended Audience :: Financial and Insurance Industry
11
+ Classifier: Intended Audience :: Science/Research
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Topic :: Office/Business :: Financial :: Investment
17
+ Classifier: Topic :: Scientific/Engineering :: Mathematics
18
+ Requires-Dist: numpy==2.4.4
19
+ Requires-Dist: scipy==1.17.1
20
+ Requires-Dist: pydantic==2.12.5
21
+ Requires-Dist: httpx==0.28.1
22
+ Requires-Dist: fastmcp==2.14.7
23
+ Requires-Dist: openpyxl==3.1.5
24
+ Requires-Dist: pypdf==6.21.4
25
+ Requires-Dist: pyyaml==6.0.3
26
+ Requires-Dist: snowballstemmer==3.1.1
27
+ Requires-Dist: onnxruntime==1.31.1
28
+ Requires-Dist: tokenizers==0.23.2
29
+ Requires-Dist: huggingface-hub==1.33.0
30
+ Requires-Python: >=3.12
31
+ Project-URL: Homepage, https://github.com/LeoLourson/MacroStress-DSH
32
+ Project-URL: Documentation, https://github.com/LeoLourson/MacroStress-DSH/blob/main/docs/README.md
33
+ Project-URL: Repository, https://github.com/LeoLourson/MacroStress-DSH
34
+ Project-URL: Issues, https://github.com/LeoLourson/MacroStress-DSH/issues
35
+ Project-URL: Changelog, https://github.com/LeoLourson/MacroStress-DSH/blob/main/CHANGELOG.md
36
+ Description-Content-Type: text/markdown
37
+
38
+ # MacroStress
39
+
40
+ [English](README.md) | [Русский](README.ru.md) | [中文](README.zh.md)
41
+
42
+ [![CI](https://github.com/LeoLourson/MacroStress-DSH/actions/workflows/ci.yml/badge.svg)](https://github.com/LeoLourson/MacroStress-DSH/actions/workflows/ci.yml)
43
+ [![MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
44
+ [![Python 3.12+](https://img.shields.io/badge/python-3.12%2B-blue.svg)](pyproject.toml)
45
+
46
+ MacroStress answers one question: how much does a portfolio lose under a given macroeconomic
47
+ scenario? An analyst or an agent names paths for a few macro variables (say, the policy rate at
48
+ 21 % for the next year) and the shocks behind them. A BVAR estimated on history completes
49
+ consistent paths for the other variables, satellite models turn those paths into drivers (equity
50
+ prices, bond spreads, industry overdue shares, housing prices), and every position is repriced
51
+ along the same draws. The output is a loss distribution with $P_5$, $P_{50}$, $P_{95}$, ES95 and
52
+ the scenario severity $\delta$. The method is described in
53
+ [docs/scenario-formalization.md](docs/scenario-formalization.md) (Russian).
54
+
55
+ The server speaks MCP, so any client can drive it: Claude Desktop, Cursor, Claude Code. A
56
+ DeepSeek Harness plugin in `dsh/` adds three skills and a workspace snapshot for the agent
57
+ prompt. All state lives in one `workspace/` directory; there is no web app, no database, and no
58
+ scheduler.
59
+
60
+ ## What it does
61
+
62
+ | Part | What it does |
63
+ |---|---|
64
+ | Series | Monthly series from MOEX, the Bank of Russia, Rosstat, and FRED, with the release date of every value |
65
+ | Macro model | BVAR with stochastic volatility, sign restrictions, conditional forecasts, scenario severity, and checks on historical episodes |
66
+ | Drivers | Object catalogs, hierarchical satellite models, out-of-sample checks, driver paths on the macro draws |
67
+ | Scenario | A $(N, C, D, O)$ file with validation and paths: conditional forecast and drivers on the same draws |
68
+ | Positions | Equity, bond, credit or deposit, housing |
69
+ | Run and report | Positions along scenario paths, with $P_5$, $P_{50}$, $P_{95}$, ES95, and $\delta$ |
70
+ | Extensions | A new asset type is a plugin: `python -m mst.plugin new`, contract checks, a demo run |
71
+ | DSH plugin | Scenario from the corpus, report, drivers; the prompt section built from `workspace.json` |
72
+
73
+ MCP tools: `jobs`, `series`, `corpus`, `scenario`, `run`, `portfolio`, `model`. The reference is
74
+ in [docs/tools.md](docs/tools.md) (Russian).
75
+
76
+ ## Install
77
+
78
+ Python 3.12 or newer and [uv](https://docs.astral.sh/uv/) are enough.
79
+
80
+ ```bash
81
+ git clone https://github.com/LeoLourson/MacroStress-DSH
82
+ cd MacroStress-DSH
83
+ uv sync
84
+ uv run macrostress --workspace C:/data/macrostress
85
+ ```
86
+
87
+ The server speaks over stdio and prints only errors; it waits for a client. Configuration for
88
+ Claude Desktop, Cursor, and any other MCP client is in [docs/mcp.md](docs/mcp.md). The DeepSeek
89
+ Harness plugin is covered in [docs/install.md](docs/install.md).
90
+
91
+ ## First session
92
+
93
+ Once a client is connected, it sees seven tools. Long operations start jobs: a tool returns a
94
+ `job` at once, and `jobs` shows the progress.
95
+
96
+ 1. `jobs(action="state")` — what is in the workspace: model versions, corpus index, jobs.
97
+ 2. `series(action="update")` — download the macro series. FRED needs a key in `FRED_API_KEY`.
98
+ 3. `jobs(action="status", id=...)` and `jobs(action="log", id=...)` — wait for the job.
99
+ 4. `model(kind="macro", action="estimate", as_of="2026-10-29")` — estimate the macro model on the
100
+ data known at that date. The version is a hash.
101
+ 5. `series(action="update", quantity="share_price")` — find the objects of a quantity and
102
+ download their series; the same for `bond_spread`, `overdue_share`, `housing_price_primary`,
103
+ `housing_price_secondary`.
104
+ 6. `model(kind="satellites", action="estimate", version=<macro hash>)` — estimate the satellites
105
+ on that macro version.
106
+ 7. `model(kind="satellites", action="describe", version=<hash>)` — which drivers passed the
107
+ out-of-sample check and why the rest were rejected.
108
+ 8. `corpus(action="search", query=..., as_of=...)` — fragments known at the date;
109
+ `corpus(action="index")` rebuilds the index as a job.
110
+ 9. `scenario(action="save", id=..., spec={...})` — save a scenario;
111
+ `scenario(action="validate", id=...)` — list everything that blocks the calculation.
112
+ 10. `portfolio(action="save", id=..., spec={...})` — save a portfolio;
113
+ `portfolio(action="check", id=..., scenario=...)` — check it against the scenario.
114
+ 11. `run(action="start", scenario=..., portfolio=..., draws=..., seed=...)` — start a run;
115
+ `run(action="result", id=...)` — read $P_5$/$P_{50}$/$P_{95}$, $\delta$, and coverage.
116
+
117
+ ## Documentation
118
+
119
+ The documentation is in Russian, except [docs/mcp.md](docs/mcp.md), which is in English.
120
+
121
+ - [docs/mcp.md](docs/mcp.md) — any MCP client, the first session, troubleshooting;
122
+ - [docs/install.md](docs/install.md) — the Python package and the DeepSeek Harness plugin;
123
+ - [docs/architecture.md](docs/architecture.md) — packages, workspace, versions, jobs;
124
+ - [docs/tools.md](docs/tools.md) — MCP tools and `python -m mst.*` commands;
125
+ - [docs/plugins.md](docs/plugins.md) — a new asset type as a plugin;
126
+ - [docs/platform.md](docs/platform.md) — the contracts: position, paths, model, registry;
127
+ - [docs/scenario-formalization.md](docs/scenario-formalization.md) — the method and what the code
128
+ implements.
129
+
130
+ Every package folder has its own README that names its files.
131
+
132
+ ## Development
133
+
134
+ ```bash
135
+ uv sync
136
+ uv run pytest -q -m "not slow"
137
+ uv run ruff check .
138
+ ```
139
+
140
+ The `slow` tests estimate the BVAR and take minutes; the night workflow runs the full suite.
141
+ CI runs the fast suite on Linux and Windows with Python 3.12 and 3.13.
142
+
143
+ ## Status
144
+
145
+ 0.1.0, beta. Interfaces may change before 1.0. The data sources are the Russian market (MOEX,
146
+ the Bank of Russia, Rosstat) plus FRED for global series.
147
+
148
+ ## Contributing and license
149
+
150
+ Contributing rules and the release process are in [CONTRIBUTING.md](CONTRIBUTING.md);
151
+ vulnerabilities go through [SECURITY.md](SECURITY.md). The code is MIT-licensed, see
152
+ [LICENSE](LICENSE). Series from MOEX, the Bank of Russia, Rosstat, FRED, and BIS are not part of
153
+ this repository: you load them under the terms of each source, and the license does not cover
154
+ that data. Citations are described in [CITATION.cff](CITATION.cff).
@@ -0,0 +1,117 @@
1
+ # MacroStress
2
+
3
+ [English](README.md) | [Русский](README.ru.md) | [中文](README.zh.md)
4
+
5
+ [![CI](https://github.com/LeoLourson/MacroStress-DSH/actions/workflows/ci.yml/badge.svg)](https://github.com/LeoLourson/MacroStress-DSH/actions/workflows/ci.yml)
6
+ [![MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
7
+ [![Python 3.12+](https://img.shields.io/badge/python-3.12%2B-blue.svg)](pyproject.toml)
8
+
9
+ MacroStress answers one question: how much does a portfolio lose under a given macroeconomic
10
+ scenario? An analyst or an agent names paths for a few macro variables (say, the policy rate at
11
+ 21 % for the next year) and the shocks behind them. A BVAR estimated on history completes
12
+ consistent paths for the other variables, satellite models turn those paths into drivers (equity
13
+ prices, bond spreads, industry overdue shares, housing prices), and every position is repriced
14
+ along the same draws. The output is a loss distribution with $P_5$, $P_{50}$, $P_{95}$, ES95 and
15
+ the scenario severity $\delta$. The method is described in
16
+ [docs/scenario-formalization.md](docs/scenario-formalization.md) (Russian).
17
+
18
+ The server speaks MCP, so any client can drive it: Claude Desktop, Cursor, Claude Code. A
19
+ DeepSeek Harness plugin in `dsh/` adds three skills and a workspace snapshot for the agent
20
+ prompt. All state lives in one `workspace/` directory; there is no web app, no database, and no
21
+ scheduler.
22
+
23
+ ## What it does
24
+
25
+ | Part | What it does |
26
+ |---|---|
27
+ | Series | Monthly series from MOEX, the Bank of Russia, Rosstat, and FRED, with the release date of every value |
28
+ | Macro model | BVAR with stochastic volatility, sign restrictions, conditional forecasts, scenario severity, and checks on historical episodes |
29
+ | Drivers | Object catalogs, hierarchical satellite models, out-of-sample checks, driver paths on the macro draws |
30
+ | Scenario | A $(N, C, D, O)$ file with validation and paths: conditional forecast and drivers on the same draws |
31
+ | Positions | Equity, bond, credit or deposit, housing |
32
+ | Run and report | Positions along scenario paths, with $P_5$, $P_{50}$, $P_{95}$, ES95, and $\delta$ |
33
+ | Extensions | A new asset type is a plugin: `python -m mst.plugin new`, contract checks, a demo run |
34
+ | DSH plugin | Scenario from the corpus, report, drivers; the prompt section built from `workspace.json` |
35
+
36
+ MCP tools: `jobs`, `series`, `corpus`, `scenario`, `run`, `portfolio`, `model`. The reference is
37
+ in [docs/tools.md](docs/tools.md) (Russian).
38
+
39
+ ## Install
40
+
41
+ Python 3.12 or newer and [uv](https://docs.astral.sh/uv/) are enough.
42
+
43
+ ```bash
44
+ git clone https://github.com/LeoLourson/MacroStress-DSH
45
+ cd MacroStress-DSH
46
+ uv sync
47
+ uv run macrostress --workspace C:/data/macrostress
48
+ ```
49
+
50
+ The server speaks over stdio and prints only errors; it waits for a client. Configuration for
51
+ Claude Desktop, Cursor, and any other MCP client is in [docs/mcp.md](docs/mcp.md). The DeepSeek
52
+ Harness plugin is covered in [docs/install.md](docs/install.md).
53
+
54
+ ## First session
55
+
56
+ Once a client is connected, it sees seven tools. Long operations start jobs: a tool returns a
57
+ `job` at once, and `jobs` shows the progress.
58
+
59
+ 1. `jobs(action="state")` — what is in the workspace: model versions, corpus index, jobs.
60
+ 2. `series(action="update")` — download the macro series. FRED needs a key in `FRED_API_KEY`.
61
+ 3. `jobs(action="status", id=...)` and `jobs(action="log", id=...)` — wait for the job.
62
+ 4. `model(kind="macro", action="estimate", as_of="2026-10-29")` — estimate the macro model on the
63
+ data known at that date. The version is a hash.
64
+ 5. `series(action="update", quantity="share_price")` — find the objects of a quantity and
65
+ download their series; the same for `bond_spread`, `overdue_share`, `housing_price_primary`,
66
+ `housing_price_secondary`.
67
+ 6. `model(kind="satellites", action="estimate", version=<macro hash>)` — estimate the satellites
68
+ on that macro version.
69
+ 7. `model(kind="satellites", action="describe", version=<hash>)` — which drivers passed the
70
+ out-of-sample check and why the rest were rejected.
71
+ 8. `corpus(action="search", query=..., as_of=...)` — fragments known at the date;
72
+ `corpus(action="index")` rebuilds the index as a job.
73
+ 9. `scenario(action="save", id=..., spec={...})` — save a scenario;
74
+ `scenario(action="validate", id=...)` — list everything that blocks the calculation.
75
+ 10. `portfolio(action="save", id=..., spec={...})` — save a portfolio;
76
+ `portfolio(action="check", id=..., scenario=...)` — check it against the scenario.
77
+ 11. `run(action="start", scenario=..., portfolio=..., draws=..., seed=...)` — start a run;
78
+ `run(action="result", id=...)` — read $P_5$/$P_{50}$/$P_{95}$, $\delta$, and coverage.
79
+
80
+ ## Documentation
81
+
82
+ The documentation is in Russian, except [docs/mcp.md](docs/mcp.md), which is in English.
83
+
84
+ - [docs/mcp.md](docs/mcp.md) — any MCP client, the first session, troubleshooting;
85
+ - [docs/install.md](docs/install.md) — the Python package and the DeepSeek Harness plugin;
86
+ - [docs/architecture.md](docs/architecture.md) — packages, workspace, versions, jobs;
87
+ - [docs/tools.md](docs/tools.md) — MCP tools and `python -m mst.*` commands;
88
+ - [docs/plugins.md](docs/plugins.md) — a new asset type as a plugin;
89
+ - [docs/platform.md](docs/platform.md) — the contracts: position, paths, model, registry;
90
+ - [docs/scenario-formalization.md](docs/scenario-formalization.md) — the method and what the code
91
+ implements.
92
+
93
+ Every package folder has its own README that names its files.
94
+
95
+ ## Development
96
+
97
+ ```bash
98
+ uv sync
99
+ uv run pytest -q -m "not slow"
100
+ uv run ruff check .
101
+ ```
102
+
103
+ The `slow` tests estimate the BVAR and take minutes; the night workflow runs the full suite.
104
+ CI runs the fast suite on Linux and Windows with Python 3.12 and 3.13.
105
+
106
+ ## Status
107
+
108
+ 0.1.0, beta. Interfaces may change before 1.0. The data sources are the Russian market (MOEX,
109
+ the Bank of Russia, Rosstat) plus FRED for global series.
110
+
111
+ ## Contributing and license
112
+
113
+ Contributing rules and the release process are in [CONTRIBUTING.md](CONTRIBUTING.md);
114
+ vulnerabilities go through [SECURITY.md](SECURITY.md). The code is MIT-licensed, see
115
+ [LICENSE](LICENSE). Series from MOEX, the Bank of Russia, Rosstat, FRED, and BIS are not part of
116
+ this repository: you load them under the terms of each source, and the license does not cover
117
+ that data. Citations are described in [CITATION.cff](CITATION.cff).
@@ -0,0 +1,87 @@
1
+ [project]
2
+ name = "macrostress"
3
+ version = "0.1.0"
4
+ description = "Portfolio stress testing over macroeconomic scenarios, as an MCP server"
5
+ readme = "README.md"
6
+ license = "MIT"
7
+ license-files = ["LICENSE"]
8
+ requires-python = ">=3.12"
9
+ keywords = [
10
+ "stress-testing",
11
+ "scenario-analysis",
12
+ "macroeconomic",
13
+ "bvar",
14
+ "portfolio-risk",
15
+ "mcp",
16
+ ]
17
+ classifiers = [
18
+ "Development Status :: 4 - Beta",
19
+ "Intended Audience :: Financial and Insurance Industry",
20
+ "Intended Audience :: Science/Research",
21
+ "Operating System :: OS Independent",
22
+ "Programming Language :: Python :: 3",
23
+ "Programming Language :: Python :: 3.12",
24
+ "Programming Language :: Python :: 3.13",
25
+ "Topic :: Office/Business :: Financial :: Investment",
26
+ "Topic :: Scientific/Engineering :: Mathematics",
27
+ ]
28
+ dependencies = [
29
+ "numpy==2.4.4",
30
+ "scipy==1.17.1",
31
+ "pydantic==2.12.5",
32
+ "httpx==0.28.1",
33
+ "fastmcp==2.14.7",
34
+ "openpyxl==3.1.5",
35
+ "pypdf==6.21.4",
36
+ "pyyaml==6.0.3",
37
+ "snowballstemmer==3.1.1",
38
+ "onnxruntime==1.31.1",
39
+ "tokenizers==0.23.2",
40
+ "huggingface-hub==1.33.0",
41
+ ]
42
+
43
+ [[project.authors]]
44
+ name = "LeoLourson"
45
+
46
+ [project.urls]
47
+ Homepage = "https://github.com/LeoLourson/MacroStress-DSH"
48
+ Documentation = "https://github.com/LeoLourson/MacroStress-DSH/blob/main/docs/README.md"
49
+ Repository = "https://github.com/LeoLourson/MacroStress-DSH"
50
+ Issues = "https://github.com/LeoLourson/MacroStress-DSH/issues"
51
+ Changelog = "https://github.com/LeoLourson/MacroStress-DSH/blob/main/CHANGELOG.md"
52
+
53
+ [project.scripts]
54
+ macrostress = "mst.mcp:main"
55
+
56
+ [dependency-groups]
57
+ dev = [
58
+ "pytest==8.4.2",
59
+ "pytest-timeout==2.4.0",
60
+ "ruff==0.12.12",
61
+ ]
62
+
63
+ [build-system]
64
+ requires = ["uv_build>=0.12.13,<0.13.0"]
65
+ build-backend = "uv_build"
66
+
67
+ [tool.uv.build-backend]
68
+ module-name = "mst"
69
+
70
+ [tool.pytest.ini_options]
71
+ testpaths = ["tests"]
72
+ pythonpath = ["."]
73
+ addopts = "-ra"
74
+ markers = ["slow: estimates the BVAR and takes minutes"]
75
+
76
+ [tool.ruff]
77
+ target-version = "py312"
78
+ line-length = 100
79
+
80
+ [tool.ruff.lint]
81
+ select = [
82
+ "E",
83
+ "F",
84
+ "I",
85
+ "UP",
86
+ "B",
87
+ ]
@@ -0,0 +1,85 @@
1
+ [project]
2
+ name = "macrostress"
3
+ version = "0.1.0"
4
+ description = "Portfolio stress testing over macroeconomic scenarios, as an MCP server"
5
+ readme = "README.md"
6
+ license = "MIT"
7
+ license-files = ["LICENSE"]
8
+ requires-python = ">=3.12"
9
+ authors = [{ name = "LeoLourson" }]
10
+ keywords = [
11
+ "stress-testing",
12
+ "scenario-analysis",
13
+ "macroeconomic",
14
+ "bvar",
15
+ "portfolio-risk",
16
+ "mcp",
17
+ ]
18
+ classifiers = [
19
+ "Development Status :: 4 - Beta",
20
+ "Intended Audience :: Financial and Insurance Industry",
21
+ "Intended Audience :: Science/Research",
22
+ "Operating System :: OS Independent",
23
+ "Programming Language :: Python :: 3",
24
+ "Programming Language :: Python :: 3.12",
25
+ "Programming Language :: Python :: 3.13",
26
+ "Topic :: Office/Business :: Financial :: Investment",
27
+ "Topic :: Scientific/Engineering :: Mathematics",
28
+ ]
29
+ dependencies = [
30
+ "numpy==2.4.4",
31
+ "scipy==1.17.1",
32
+ "pydantic==2.12.5",
33
+ "httpx==0.28.1",
34
+ "fastmcp==2.14.7",
35
+ # Excel tables of the Bank of Russia and Rosstat archives.
36
+ "openpyxl==3.1.5",
37
+ # Corpus: PDF text extraction, the YAML header of a document, BM25 stems.
38
+ "pypdf==6.21.4",
39
+ "pyyaml==6.0.3",
40
+ "snowballstemmer==3.1.1",
41
+ # Corpus search vectors: multilingual-e5-small on the CPU through ONNX Runtime.
42
+ "onnxruntime==1.31.1",
43
+ "tokenizers==0.23.2",
44
+ "huggingface-hub==1.33.0",
45
+ ]
46
+
47
+ [project.urls]
48
+ Homepage = "https://github.com/LeoLourson/MacroStress-DSH"
49
+ Documentation = "https://github.com/LeoLourson/MacroStress-DSH/blob/main/docs/README.md"
50
+ Repository = "https://github.com/LeoLourson/MacroStress-DSH"
51
+ Issues = "https://github.com/LeoLourson/MacroStress-DSH/issues"
52
+ Changelog = "https://github.com/LeoLourson/MacroStress-DSH/blob/main/CHANGELOG.md"
53
+
54
+ [project.scripts]
55
+ macrostress = "mst.mcp:main"
56
+
57
+ [dependency-groups]
58
+ dev = [
59
+ "pytest==8.4.2",
60
+ "pytest-timeout==2.4.0",
61
+ "ruff==0.12.12",
62
+ ]
63
+
64
+ [build-system]
65
+ requires = ["uv_build>=0.12.13,<0.13.0"]
66
+ build-backend = "uv_build"
67
+
68
+ # The import package is mst; the distribution is macrostress.
69
+ [tool.uv.build-backend]
70
+ module-name = "mst"
71
+
72
+ [tool.pytest.ini_options]
73
+ testpaths = ["tests"]
74
+ pythonpath = ["."]
75
+ addopts = "-ra"
76
+ markers = [
77
+ "slow: estimates the BVAR and takes minutes",
78
+ ]
79
+
80
+ [tool.ruff]
81
+ target-version = "py312"
82
+ line-length = 100
83
+
84
+ [tool.ruff.lint]
85
+ select = ["E", "F", "I", "UP", "B"]
@@ -0,0 +1,32 @@
1
+ # mst
2
+
3
+ Ядро MacroStress: MCP-сервер, задачи, workspace и пакеты расчёта. Единственное состояние —
4
+ файлы workspace; каждый инструмент MCP разбирает аргументы и вызывает одну функцию
5
+ пакета или запускает задачу. Устройство целиком — в
6
+ [../../docs/architecture.md](../../docs/architecture.md).
7
+
8
+ | Модуль или пакет | Что делает |
9
+ |---|---|
10
+ | `mcp.py` | Сервер FastMCP с инструментами `jobs`, `series`, `corpus`, `scenario`, `run`, `portfolio`, `model` ([../../docs/tools.md](../../docs/tools.md)); `python -m mst.mcp --workspace DIR` |
11
+ | `plugin.py` | `python -m mst.plugin new|check|demo-run`: пакет типа актива, его контракт и демо-расчёт ([../../docs/plugins.md](../../docs/plugins.md)) |
12
+ | `jobs.py` | Задачи: дочерний процесс `python <argv>` под обёрткой `python -m mst.jobs`, которая пишет код выхода, с логом в `jobs/<id>/`, блокировка ресурса (`series`, `models`, `corpus`, `code`, `runs`), отмена дерева процессов |
13
+ | `runs.py` | Прогон на путях сценария: запрос, мета, пути, вклады позиций и отчёт в `runs/<id>/`; `python -m mst.runs <id> --workspace DIR` |
14
+ | `store.py` | Каталоги workspace (`Workspace`), атомарная запись JSON, канонический хеш `sha256_of`, снимок `workspace.json` |
15
+ | `versions.py` | Версии моделей на диске: хеш по метаданным и массивам, запись через черновик, чтение с проверкой хеша и имени, список без черновиков |
16
+ | `contracts.py` | Базовый класс контрактов pydantic (`extra="forbid"`, без NaN и бесконечностей) и общие типы: идентификатор, хеш, горизонт, сид, валюта, код объекта, статус результата |
17
+ | `importer.py` | Копирование рядов из каталога CSV (`<source>/data/series/*.csv`) в workspace |
18
+ | [data/](data/README.md) | Загрузка и хранение месячных рядов |
19
+ | [macro/](macro/README.md) | Макромодель: оценка, идентификация, условный прогноз, жёсткость, бэктест |
20
+ | [drivers/](drivers/README.md) | Драйверы: каталоги, сателлиты, их проверка и пути |
21
+ | [corpus/](corpus/README.md) | Корпус источников: манифест, загрузка, Markdown, эпизоды, индекс и поиск |
22
+ | [scenario/](scenario/README.md) | Сценарий: контракт, проверка и сборка путей |
23
+ | [plugin_sdk/](plugin_sdk/README.md) | Что модель позиции объявляет, читает и возвращает: `Paths`, требования, результат; `testing.py` — конформанс-проверка |
24
+ | [engine/](engine/README.md) | Каталог моделей, подготовка входов и расчёт позиций без ветвления по типу актива |
25
+ | [plugins/](plugins/README.md) | Пакеты типов активов: обнаружение, реестр объявлений и модели позиций |
26
+ | [portfolio/](portfolio/README.md) | Портфель, его проверка, стоимость позиций и отчёт прогона |
27
+
28
+ Зависимости между пакетами идут в одну сторону: `data` → `macro` → `drivers`; `scenario`
29
+ нужен `macro` и `drivers`, `corpus` — бэктесту `macro`; `engine` и `runs` — `plugin_sdk`,
30
+ `plugins`, `scenario` и обеим версиям моделей; `portfolio` — `engine` и `scenario`.
31
+ `plugin.py` читает реестр `plugins` и `plugin_sdk.testing`; сам `engine` конкретных плагинов
32
+ не импортирует.
@@ -0,0 +1,5 @@
1
+ """Series, macro model, drivers, corpus, scenarios, portfolios and runs of MacroStress.
2
+
3
+ The package is the core of the DSH plugin: every MCP tool in ``mst.mcp`` parses its
4
+ arguments and calls one function here, and the workspace on disk is the only state.
5
+ """
@@ -0,0 +1,48 @@
1
+ """Wire conventions: money in millions of its currency, rates as fractions, time in months/days."""
2
+
3
+ from typing import Annotated, Literal
4
+
5
+ from pydantic import BaseModel, ConfigDict, Field
6
+
7
+ Identifier = Annotated[str, Field(min_length=1, pattern=r"\S")]
8
+ Sha256 = Annotated[str, Field(pattern=r"^[0-9a-f]{64}$")]
9
+ Number = Annotated[float, Field(strict=True, allow_inf_nan=False)]
10
+ Probability = Annotated[Number, Field(ge=0, le=1)]
11
+ NonNegative = Annotated[Number, Field(ge=0)]
12
+ Positive = Annotated[Number, Field(gt=0)]
13
+ Horizon = Annotated[int, Field(strict=True, ge=1, le=360)]
14
+ Seed = Annotated[int, Field(strict=True, ge=0, le=2**32 - 1)]
15
+ #: An ISO 4217 code: the currency of a position, a portfolio's base currency.
16
+ Currency = Annotated[str, Field(pattern=r"^[A-Z]{3}$")]
17
+ #: A kind of code a position carries and an object may cover (``Registry.match``).
18
+ CodeKind = Literal["secid", "isin", "inn", "okved", "okato"]
19
+ Status = Literal["ok", "needs_review"]
20
+ #: The status of a calculated position: ``invalid`` never enters portfolio arithmetic.
21
+ ResultStatus = Literal["ok", "needs_review", "invalid"]
22
+
23
+ __all__ = [
24
+ "CodeKind",
25
+ "Contract",
26
+ "Currency",
27
+ "Horizon",
28
+ "Identifier",
29
+ "NonNegative",
30
+ "Number",
31
+ "Positive",
32
+ "Probability",
33
+ "ResultStatus",
34
+ "Seed",
35
+ "Sha256",
36
+ "Status",
37
+ ]
38
+
39
+
40
+ class Contract(BaseModel):
41
+ model_config = ConfigDict(
42
+ extra="forbid",
43
+ allow_inf_nan=False,
44
+ validate_default=True,
45
+ validate_assignment=True,
46
+ revalidate_instances="always",
47
+ )
48
+
@@ -0,0 +1,35 @@
1
+ # mst.corpus
2
+
3
+ Корпус источников сценария ([corpus/README.md](../../../corpus/README.md)): манифест с
4
+ адресами оригиналов, загрузка, перевод PDF и веб-страниц в Markdown, карточки эпизодов с
5
+ таблицами движений, индекс фрагментов и поиск по нему.
6
+
7
+ ```powershell
8
+ python -m mst.corpus check --corpus DIR
9
+ python -m mst.corpus fetch [ID ...] --corpus DIR [--force]
10
+ python -m mst.corpus add KEY FILE [--url URL] --corpus DIR
11
+ python -m mst.corpus convert [ID ...] --corpus DIR
12
+ python -m mst.corpus series [SERIES ...] --corpus DIR --store DIR
13
+ python -m mst.corpus episodes [ID ...] --corpus DIR --store DIR
14
+ python -m mst.corpus index --corpus DIR
15
+ python -m mst.corpus search QUERY --as-of DAY --corpus DIR
16
+ python -m mst.corpus eval --corpus DIR
17
+ ```
18
+
19
+ `--corpus` — корень корпуса в workspace (`workspace/corpus`). В git лежат только манифест,
20
+ `series.toml`, проверочные вопросы и шаблоны (`corpus/` в корне репозитория); скачанные
21
+ оригиналы, тексты и индекс — данные workspace.
22
+
23
+ | Файл | Что делает |
24
+ |---|---|
25
+ | `manifest.py` | Контракты манифеста: источники и их издания, адреса оригиналов (`url` или `page` + `match`), карточки эпизодов, ряды; `Document` — один файл источника |
26
+ | `fetch.py` | Загрузка оригиналов в `raw/`, запись в `downloads.json` с хешем и датой; файл с тем же хешем повторно не скачивается |
27
+ | `convert.py` | PDF и веб-страница в Markdown: текстовый слой pypdf, заголовки из оглавления, колонтитулы и номера страниц выброшены, шапка с датой публикации |
28
+ | `series.py` | Дневные ряды D1–D5 для таблиц движений: загрузчик из `mst.data` или BIS, кеш в `series/`, месячный ряд из хранилища как запасной вариант |
29
+ | `episodes.py` | Карточка эпизода: окно, база, экстремум, изменение, дни до экстремума и восстановления; написанные руками разделы сохраняются |
30
+ | `index.py` | Нарезка Markdown на фрагменты, `chunks.jsonl`, векторы e5, BM25 + косинус с фильтром `as_of`, recall@5 по `eval/questions.md` |
31
+ | `embed.py` | Векторы multilingual-e5-small на CPU через ONNX Runtime |
32
+ | `tool.py` | Операции инструмента `corpus`: `search`, `get`, `list`, `add` |
33
+ | `cli.py`, `__main__.py` | Команда `python -m mst.corpus` |
34
+
35
+ Тесты — [../../../tests/corpus](../../../tests/corpus/).
@@ -0,0 +1 @@
1
+ """The scenario corpus (corpus/README.md): manifest, downloads, texts, episode cards, search."""
@@ -0,0 +1,7 @@
1
+ """``python -m mst.corpus``: the corpus commands, run as a task."""
2
+
3
+ import sys
4
+
5
+ from .cli import main
6
+
7
+ sys.exit(main())