okama-mcp 0.2.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.
- okama_mcp-0.2.0/LICENSE +21 -0
- okama_mcp-0.2.0/PKG-INFO +276 -0
- okama_mcp-0.2.0/README.md +249 -0
- okama_mcp-0.2.0/pyproject.toml +62 -0
- okama_mcp-0.2.0/src/okama_mcp/__init__.py +10 -0
- okama_mcp-0.2.0/src/okama_mcp/__main__.py +10 -0
- okama_mcp-0.2.0/src/okama_mcp/cache.py +87 -0
- okama_mcp-0.2.0/src/okama_mcp/errors.py +98 -0
- okama_mcp-0.2.0/src/okama_mcp/schemas.py +220 -0
- okama_mcp-0.2.0/src/okama_mcp/serialization.py +148 -0
- okama_mcp-0.2.0/src/okama_mcp/server.py +33 -0
- okama_mcp-0.2.0/src/okama_mcp/tools/__init__.py +30 -0
- okama_mcp-0.2.0/src/okama_mcp/tools/asset.py +66 -0
- okama_mcp-0.2.0/src/okama_mcp/tools/asset_list.py +106 -0
- okama_mcp-0.2.0/src/okama_mcp/tools/frontier.py +162 -0
- okama_mcp-0.2.0/src/okama_mcp/tools/macro.py +116 -0
- okama_mcp-0.2.0/src/okama_mcp/tools/monte_carlo.py +230 -0
- okama_mcp-0.2.0/src/okama_mcp/tools/portfolio.py +227 -0
- okama_mcp-0.2.0/src/okama_mcp/tools/search.py +152 -0
- okama_mcp-0.2.0/src/okama_mcp/transport.py +77 -0
okama_mcp-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Sergey Kikevich
|
|
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.
|
okama_mcp-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: okama-mcp
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: MCP (Model Context Protocol) server exposing the okama investment portfolio toolkit to AI assistants
|
|
5
|
+
License: MIT
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Keywords: mcp,okama,investments,portfolio,finance,llm
|
|
8
|
+
Author: Sergey Kikevich
|
|
9
|
+
Author-email: sergey@rostsber.ru
|
|
10
|
+
Requires-Python: >=3.11,<4.0.0
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Financial and Insurance Industry
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
19
|
+
Classifier: Topic :: Office/Business :: Financial :: Investment
|
|
20
|
+
Requires-Dist: fastmcp (>=2.7)
|
|
21
|
+
Requires-Dist: okama (>=2.1.0)
|
|
22
|
+
Requires-Dist: pydantic (>=2.0)
|
|
23
|
+
Project-URL: Homepage, https://mcp.okama.io
|
|
24
|
+
Project-URL: Repository, https://github.com/mbk-dev/okama-mcp
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
|
|
27
|
+
# okama-mcp
|
|
28
|
+
|
|
29
|
+
MCP (Model Context Protocol) server that exposes the [okama](https://github.com/mbk-dev/okama)
|
|
30
|
+
investment portfolio toolkit to AI assistants — Claude Desktop, Claude Code, Cursor, and any
|
|
31
|
+
other MCP-compatible client.
|
|
32
|
+
|
|
33
|
+
With okama-mcp installed, you can ask an AI things like:
|
|
34
|
+
|
|
35
|
+
> *"Backtest a portfolio of 30% gold and 70% real estate over the last 15 years."*
|
|
36
|
+
>
|
|
37
|
+
> *"Run a Monte Carlo retirement forecast on that portfolio, withdrawing $1,000/month
|
|
38
|
+
> indexed to inflation, over 25 years."*
|
|
39
|
+
>
|
|
40
|
+
> *"What's the tangency portfolio of SPY, BND, and GLD with a 3% risk-free rate?"*
|
|
41
|
+
|
|
42
|
+
…and the AI uses the MCP tools to call okama directly — no Python code needed.
|
|
43
|
+
|
|
44
|
+
Built on [FastMCP](https://github.com/jlowin/fastmcp). Single codebase, two transports:
|
|
45
|
+
`stdio` (for local clients) and `streamable-http` (for self-hosting).
|
|
46
|
+
okama-mcp is free and open source — no hosted service, no registration; you run it
|
|
47
|
+
yourself, locally or on your own server.
|
|
48
|
+
|
|
49
|
+
## Install
|
|
50
|
+
|
|
51
|
+
Requires Python ≥ 3.11 (same floor as okama itself).
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
git clone https://github.com/mbk-dev/okama-mcp
|
|
55
|
+
cd okama-mcp
|
|
56
|
+
poetry install
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Run
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
# stdio — for Claude Desktop, Claude Code, Cursor (local IPC)
|
|
63
|
+
poetry run okama-mcp stdio
|
|
64
|
+
|
|
65
|
+
# streamable HTTP — for self-hosting on your own server
|
|
66
|
+
poetry run okama-mcp http --host 127.0.0.1 --port 8765
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Connect a client
|
|
70
|
+
|
|
71
|
+
### Claude Desktop
|
|
72
|
+
|
|
73
|
+
Edit `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or
|
|
74
|
+
`%APPDATA%\Claude\claude_desktop_config.json` (Windows):
|
|
75
|
+
|
|
76
|
+
```json
|
|
77
|
+
{
|
|
78
|
+
"mcpServers": {
|
|
79
|
+
"okama": {
|
|
80
|
+
"command": "poetry",
|
|
81
|
+
"args": ["run", "okama-mcp", "stdio"],
|
|
82
|
+
"cwd": "/absolute/path/to/okama-mcp"
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Restart Claude Desktop; the server appears in the tools menu.
|
|
89
|
+
|
|
90
|
+
### Claude Code
|
|
91
|
+
|
|
92
|
+
From the project root — registers the server for this project only (`claude` must be
|
|
93
|
+
launched from this directory to see it):
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
claude mcp add okama poetry run okama-mcp stdio
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
To make the server available in **every** project (user scope), register the absolute
|
|
100
|
+
path of the venv binary — `poetry run` would not work outside the project directory:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
claude mcp add --scope user okama -- "$(poetry env info -p)/bin/okama-mcp" stdio
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Or commit a `.claude/mcp.json` so the whole team picks it up:
|
|
107
|
+
|
|
108
|
+
```json
|
|
109
|
+
{
|
|
110
|
+
"mcpServers": {
|
|
111
|
+
"okama": {
|
|
112
|
+
"command": "poetry",
|
|
113
|
+
"args": ["run", "okama-mcp", "stdio"]
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Cursor
|
|
120
|
+
|
|
121
|
+
Open *Settings → MCP*, click *Add new MCP Server*, and use:
|
|
122
|
+
|
|
123
|
+
- Name: `okama`
|
|
124
|
+
- Type: `stdio`
|
|
125
|
+
- Command: `poetry run okama-mcp stdio`
|
|
126
|
+
- Working dir: this project's root
|
|
127
|
+
|
|
128
|
+
### Self-hosting (streamable HTTP)
|
|
129
|
+
|
|
130
|
+
Run okama-mcp on your own server and share it across your MCP clients:
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
poetry run okama-mcp http --host 127.0.0.1 --port 8765 --path /mcp
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Then point your MCP client at `http://<your-server>:8765/mcp`. For a production
|
|
137
|
+
setup put nginx + TLS in front; ready-made examples live in `deploy/`:
|
|
138
|
+
|
|
139
|
+
- `deploy/systemd/okama-mcp.service` — systemd unit (hardened, runs as a dedicated user)
|
|
140
|
+
- `deploy/nginx/self-hosted.conf` — nginx vhost: TLS, SSE-friendly proxying of `/mcp`
|
|
141
|
+
|
|
142
|
+
The server is open by design — free to run, no registration. If your instance must
|
|
143
|
+
not be public, restrict access at the nginx level (allow-list, VPN, or HTTP basic auth).
|
|
144
|
+
|
|
145
|
+
## Tool catalog
|
|
146
|
+
|
|
147
|
+
All tools are **stateless** — pass the full portfolio specification with every call.
|
|
148
|
+
The server caches expensive okama objects (`Portfolio`, `EfficientFrontier`) by content
|
|
149
|
+
hash, so repeated calls on the same spec are fast.
|
|
150
|
+
|
|
151
|
+
### Search & metadata
|
|
152
|
+
|
|
153
|
+
| Tool | Purpose |
|
|
154
|
+
|---|---|
|
|
155
|
+
| `search_assets(query, namespace?)` | Free-text search across all okama symbols by name / ticker / ISIN. |
|
|
156
|
+
| `list_namespaces(kind="all"\|"assets"\|"macro")` | Show the available okama namespaces. |
|
|
157
|
+
| `get_asset_info(symbol)` | Metadata for one symbol — name, country, currency, type, date range. |
|
|
158
|
+
|
|
159
|
+
### Single asset & comparisons
|
|
160
|
+
|
|
161
|
+
| Tool | Purpose |
|
|
162
|
+
|---|---|
|
|
163
|
+
| `get_asset_history(symbol, kind, first_date?, last_date?)` | Time series for one asset. `kind` ∈ {`close_monthly`, `close_daily`, `adj_close`, `ror`, `dividends`}. |
|
|
164
|
+
| `compare_assets(symbols, ccy, first_date?, last_date?, inflation)` | Side-by-side statistics (`describe()` table: CAGR, risk, drawdowns by period). |
|
|
165
|
+
| `get_correlations(symbols, ccy, ...)` | Correlation matrix of monthly returns. |
|
|
166
|
+
|
|
167
|
+
### Portfolio backtest
|
|
168
|
+
|
|
169
|
+
| Tool | Purpose |
|
|
170
|
+
|---|---|
|
|
171
|
+
| `analyze_portfolio(portfolio)` | Headline metrics + full `describe()` for a `PortfolioSpec`. |
|
|
172
|
+
| `get_portfolio_drawdowns(portfolio)` | Drawdown time series + max drawdown / recovery period. |
|
|
173
|
+
| `get_portfolio_var_cvar(portfolio, time_frame=12, level=1)` | Historical Value at Risk and CVaR. |
|
|
174
|
+
| `get_portfolio_wealth_index(portfolio, full=False)` | Wealth-index series (cumulative growth of 1000). |
|
|
175
|
+
|
|
176
|
+
### Monte Carlo DCF
|
|
177
|
+
|
|
178
|
+
| Tool | Purpose |
|
|
179
|
+
|---|---|
|
|
180
|
+
| `monte_carlo_forecast(portfolio, mc, cashflow)` | Forward simulation with one of five cash-flow strategies (`indexation`, `percentage`, `time_series`, `vanguard`, `cut_if_drawdown`). Returns percentile wealth bands, terminal-wealth stats, and survival metrics. |
|
|
181
|
+
|
|
182
|
+
### Efficient Frontier
|
|
183
|
+
|
|
184
|
+
| Tool | Purpose |
|
|
185
|
+
|---|---|
|
|
186
|
+
| `build_efficient_frontier(frontier)` | Full EF point table (Risk / Mean return / CAGR + per-asset weights). |
|
|
187
|
+
| `get_tangency_portfolio(frontier, rf_return, rate_of_return)` | Max-Sharpe portfolio on the EF. |
|
|
188
|
+
| `get_min_variance_portfolio(frontier)` | Global Minimum Variance portfolio. |
|
|
189
|
+
|
|
190
|
+
### Macro
|
|
191
|
+
|
|
192
|
+
| Tool | Purpose |
|
|
193
|
+
|---|---|
|
|
194
|
+
| `get_inflation(currency, first_date?, last_date?, include_cumulative?)` | Inflation series for a currency (`USD`, `EUR`, `RUB`, …). |
|
|
195
|
+
| `get_central_bank_rate(country, first_date?, last_date?)` | Central-bank policy rate (`US`, `ECB`, `RUS`, …). |
|
|
196
|
+
|
|
197
|
+
## Spec shapes
|
|
198
|
+
|
|
199
|
+
The complex tools take typed dicts validated by pydantic. The full schemas live in
|
|
200
|
+
`src/okama_mcp/schemas.py`; here are the headline shapes:
|
|
201
|
+
|
|
202
|
+
```jsonc
|
|
203
|
+
// PortfolioSpec
|
|
204
|
+
{
|
|
205
|
+
"assets": ["GLD.US", "VNQ.US"],
|
|
206
|
+
"weights": [0.3, 0.7], // optional, must sum to 1.0
|
|
207
|
+
"ccy": "USD",
|
|
208
|
+
"first_date": "2010-01",
|
|
209
|
+
"last_date": "2024-12",
|
|
210
|
+
"rebalancing_period": "year", // month | quarter | half-year | year | none
|
|
211
|
+
"inflation": true
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// MCSpec
|
|
215
|
+
{
|
|
216
|
+
"distribution": "norm", // norm | lognorm | t
|
|
217
|
+
"period_years": 25,
|
|
218
|
+
"scenarios": 500, // ≤ 5000
|
|
219
|
+
"percentiles": [5, 50, 95],
|
|
220
|
+
"random_seed": 42 // optional, for reproducibility
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// CashflowSpec — discriminated by `type`
|
|
224
|
+
{ "type": "indexation", "initial_investment": 1000000, "frequency": "month", "amount": -1000, "indexation": "inflation" }
|
|
225
|
+
{ "type": "percentage", "initial_investment": 1000000, "frequency": "year", "percentage": -0.04 }
|
|
226
|
+
{ "type": "time_series", "initial_investment": 100000, "events": { "2030-06": -50000 } }
|
|
227
|
+
{ "type": "vanguard", "initial_investment": 1000000, "percentage": -0.04, "floor_ceiling": [-0.025, 0.05], "indexation": "inflation" }
|
|
228
|
+
{ "type": "cut_if_drawdown", "initial_investment": 1000000, "frequency": "year", "amount": -60000, "indexation": "inflation",
|
|
229
|
+
"crash_threshold_reduction": [[0.2, 0.4], [0.5, 1.0]] }
|
|
230
|
+
|
|
231
|
+
// FrontierSpec
|
|
232
|
+
{
|
|
233
|
+
"assets": ["SPY.US", "BND.US", "GLD.US"],
|
|
234
|
+
"ccy": "USD",
|
|
235
|
+
"bounds": [[0.0, 0.7], [0.1, 1.0], [0.0, 0.3]], // optional
|
|
236
|
+
"n_points": 20,
|
|
237
|
+
"rebalancing_period": "year",
|
|
238
|
+
"inflation": false
|
|
239
|
+
}
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## Development
|
|
243
|
+
|
|
244
|
+
The project follows TDD (see `AGENTS.md`). After every code change run:
|
|
245
|
+
|
|
246
|
+
```bash
|
|
247
|
+
poetry run pytest -q
|
|
248
|
+
poetry run ruff check .
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
To run the live-API integration test (hits `api.okama.io`):
|
|
252
|
+
|
|
253
|
+
```bash
|
|
254
|
+
poetry run pytest -m integration
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
## Project layout
|
|
258
|
+
|
|
259
|
+
```
|
|
260
|
+
src/okama_mcp/
|
|
261
|
+
├── server.py # FastMCP instance + registration entry point
|
|
262
|
+
├── transport.py # CLI: `okama-mcp stdio | http`
|
|
263
|
+
├── schemas.py # PortfolioSpec, MCSpec, CashflowSpec, FrontierSpec
|
|
264
|
+
├── cache.py # TTL+LRU cache keyed by sha256 of canonical spec
|
|
265
|
+
├── serialization.py # pandas → JSON-safe with smart truncation
|
|
266
|
+
├── errors.py # Translate okama exceptions to actionable MCP errors
|
|
267
|
+
└── tools/
|
|
268
|
+
├── search.py, asset.py, asset_list.py
|
|
269
|
+
├── portfolio.py, monte_carlo.py
|
|
270
|
+
├── frontier.py, macro.py
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
## License
|
|
274
|
+
|
|
275
|
+
Same as okama itself: MIT.
|
|
276
|
+
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
# okama-mcp
|
|
2
|
+
|
|
3
|
+
MCP (Model Context Protocol) server that exposes the [okama](https://github.com/mbk-dev/okama)
|
|
4
|
+
investment portfolio toolkit to AI assistants — Claude Desktop, Claude Code, Cursor, and any
|
|
5
|
+
other MCP-compatible client.
|
|
6
|
+
|
|
7
|
+
With okama-mcp installed, you can ask an AI things like:
|
|
8
|
+
|
|
9
|
+
> *"Backtest a portfolio of 30% gold and 70% real estate over the last 15 years."*
|
|
10
|
+
>
|
|
11
|
+
> *"Run a Monte Carlo retirement forecast on that portfolio, withdrawing $1,000/month
|
|
12
|
+
> indexed to inflation, over 25 years."*
|
|
13
|
+
>
|
|
14
|
+
> *"What's the tangency portfolio of SPY, BND, and GLD with a 3% risk-free rate?"*
|
|
15
|
+
|
|
16
|
+
…and the AI uses the MCP tools to call okama directly — no Python code needed.
|
|
17
|
+
|
|
18
|
+
Built on [FastMCP](https://github.com/jlowin/fastmcp). Single codebase, two transports:
|
|
19
|
+
`stdio` (for local clients) and `streamable-http` (for self-hosting).
|
|
20
|
+
okama-mcp is free and open source — no hosted service, no registration; you run it
|
|
21
|
+
yourself, locally or on your own server.
|
|
22
|
+
|
|
23
|
+
## Install
|
|
24
|
+
|
|
25
|
+
Requires Python ≥ 3.11 (same floor as okama itself).
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
git clone https://github.com/mbk-dev/okama-mcp
|
|
29
|
+
cd okama-mcp
|
|
30
|
+
poetry install
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Run
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
# stdio — for Claude Desktop, Claude Code, Cursor (local IPC)
|
|
37
|
+
poetry run okama-mcp stdio
|
|
38
|
+
|
|
39
|
+
# streamable HTTP — for self-hosting on your own server
|
|
40
|
+
poetry run okama-mcp http --host 127.0.0.1 --port 8765
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Connect a client
|
|
44
|
+
|
|
45
|
+
### Claude Desktop
|
|
46
|
+
|
|
47
|
+
Edit `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or
|
|
48
|
+
`%APPDATA%\Claude\claude_desktop_config.json` (Windows):
|
|
49
|
+
|
|
50
|
+
```json
|
|
51
|
+
{
|
|
52
|
+
"mcpServers": {
|
|
53
|
+
"okama": {
|
|
54
|
+
"command": "poetry",
|
|
55
|
+
"args": ["run", "okama-mcp", "stdio"],
|
|
56
|
+
"cwd": "/absolute/path/to/okama-mcp"
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Restart Claude Desktop; the server appears in the tools menu.
|
|
63
|
+
|
|
64
|
+
### Claude Code
|
|
65
|
+
|
|
66
|
+
From the project root — registers the server for this project only (`claude` must be
|
|
67
|
+
launched from this directory to see it):
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
claude mcp add okama poetry run okama-mcp stdio
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
To make the server available in **every** project (user scope), register the absolute
|
|
74
|
+
path of the venv binary — `poetry run` would not work outside the project directory:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
claude mcp add --scope user okama -- "$(poetry env info -p)/bin/okama-mcp" stdio
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Or commit a `.claude/mcp.json` so the whole team picks it up:
|
|
81
|
+
|
|
82
|
+
```json
|
|
83
|
+
{
|
|
84
|
+
"mcpServers": {
|
|
85
|
+
"okama": {
|
|
86
|
+
"command": "poetry",
|
|
87
|
+
"args": ["run", "okama-mcp", "stdio"]
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Cursor
|
|
94
|
+
|
|
95
|
+
Open *Settings → MCP*, click *Add new MCP Server*, and use:
|
|
96
|
+
|
|
97
|
+
- Name: `okama`
|
|
98
|
+
- Type: `stdio`
|
|
99
|
+
- Command: `poetry run okama-mcp stdio`
|
|
100
|
+
- Working dir: this project's root
|
|
101
|
+
|
|
102
|
+
### Self-hosting (streamable HTTP)
|
|
103
|
+
|
|
104
|
+
Run okama-mcp on your own server and share it across your MCP clients:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
poetry run okama-mcp http --host 127.0.0.1 --port 8765 --path /mcp
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Then point your MCP client at `http://<your-server>:8765/mcp`. For a production
|
|
111
|
+
setup put nginx + TLS in front; ready-made examples live in `deploy/`:
|
|
112
|
+
|
|
113
|
+
- `deploy/systemd/okama-mcp.service` — systemd unit (hardened, runs as a dedicated user)
|
|
114
|
+
- `deploy/nginx/self-hosted.conf` — nginx vhost: TLS, SSE-friendly proxying of `/mcp`
|
|
115
|
+
|
|
116
|
+
The server is open by design — free to run, no registration. If your instance must
|
|
117
|
+
not be public, restrict access at the nginx level (allow-list, VPN, or HTTP basic auth).
|
|
118
|
+
|
|
119
|
+
## Tool catalog
|
|
120
|
+
|
|
121
|
+
All tools are **stateless** — pass the full portfolio specification with every call.
|
|
122
|
+
The server caches expensive okama objects (`Portfolio`, `EfficientFrontier`) by content
|
|
123
|
+
hash, so repeated calls on the same spec are fast.
|
|
124
|
+
|
|
125
|
+
### Search & metadata
|
|
126
|
+
|
|
127
|
+
| Tool | Purpose |
|
|
128
|
+
|---|---|
|
|
129
|
+
| `search_assets(query, namespace?)` | Free-text search across all okama symbols by name / ticker / ISIN. |
|
|
130
|
+
| `list_namespaces(kind="all"\|"assets"\|"macro")` | Show the available okama namespaces. |
|
|
131
|
+
| `get_asset_info(symbol)` | Metadata for one symbol — name, country, currency, type, date range. |
|
|
132
|
+
|
|
133
|
+
### Single asset & comparisons
|
|
134
|
+
|
|
135
|
+
| Tool | Purpose |
|
|
136
|
+
|---|---|
|
|
137
|
+
| `get_asset_history(symbol, kind, first_date?, last_date?)` | Time series for one asset. `kind` ∈ {`close_monthly`, `close_daily`, `adj_close`, `ror`, `dividends`}. |
|
|
138
|
+
| `compare_assets(symbols, ccy, first_date?, last_date?, inflation)` | Side-by-side statistics (`describe()` table: CAGR, risk, drawdowns by period). |
|
|
139
|
+
| `get_correlations(symbols, ccy, ...)` | Correlation matrix of monthly returns. |
|
|
140
|
+
|
|
141
|
+
### Portfolio backtest
|
|
142
|
+
|
|
143
|
+
| Tool | Purpose |
|
|
144
|
+
|---|---|
|
|
145
|
+
| `analyze_portfolio(portfolio)` | Headline metrics + full `describe()` for a `PortfolioSpec`. |
|
|
146
|
+
| `get_portfolio_drawdowns(portfolio)` | Drawdown time series + max drawdown / recovery period. |
|
|
147
|
+
| `get_portfolio_var_cvar(portfolio, time_frame=12, level=1)` | Historical Value at Risk and CVaR. |
|
|
148
|
+
| `get_portfolio_wealth_index(portfolio, full=False)` | Wealth-index series (cumulative growth of 1000). |
|
|
149
|
+
|
|
150
|
+
### Monte Carlo DCF
|
|
151
|
+
|
|
152
|
+
| Tool | Purpose |
|
|
153
|
+
|---|---|
|
|
154
|
+
| `monte_carlo_forecast(portfolio, mc, cashflow)` | Forward simulation with one of five cash-flow strategies (`indexation`, `percentage`, `time_series`, `vanguard`, `cut_if_drawdown`). Returns percentile wealth bands, terminal-wealth stats, and survival metrics. |
|
|
155
|
+
|
|
156
|
+
### Efficient Frontier
|
|
157
|
+
|
|
158
|
+
| Tool | Purpose |
|
|
159
|
+
|---|---|
|
|
160
|
+
| `build_efficient_frontier(frontier)` | Full EF point table (Risk / Mean return / CAGR + per-asset weights). |
|
|
161
|
+
| `get_tangency_portfolio(frontier, rf_return, rate_of_return)` | Max-Sharpe portfolio on the EF. |
|
|
162
|
+
| `get_min_variance_portfolio(frontier)` | Global Minimum Variance portfolio. |
|
|
163
|
+
|
|
164
|
+
### Macro
|
|
165
|
+
|
|
166
|
+
| Tool | Purpose |
|
|
167
|
+
|---|---|
|
|
168
|
+
| `get_inflation(currency, first_date?, last_date?, include_cumulative?)` | Inflation series for a currency (`USD`, `EUR`, `RUB`, …). |
|
|
169
|
+
| `get_central_bank_rate(country, first_date?, last_date?)` | Central-bank policy rate (`US`, `ECB`, `RUS`, …). |
|
|
170
|
+
|
|
171
|
+
## Spec shapes
|
|
172
|
+
|
|
173
|
+
The complex tools take typed dicts validated by pydantic. The full schemas live in
|
|
174
|
+
`src/okama_mcp/schemas.py`; here are the headline shapes:
|
|
175
|
+
|
|
176
|
+
```jsonc
|
|
177
|
+
// PortfolioSpec
|
|
178
|
+
{
|
|
179
|
+
"assets": ["GLD.US", "VNQ.US"],
|
|
180
|
+
"weights": [0.3, 0.7], // optional, must sum to 1.0
|
|
181
|
+
"ccy": "USD",
|
|
182
|
+
"first_date": "2010-01",
|
|
183
|
+
"last_date": "2024-12",
|
|
184
|
+
"rebalancing_period": "year", // month | quarter | half-year | year | none
|
|
185
|
+
"inflation": true
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// MCSpec
|
|
189
|
+
{
|
|
190
|
+
"distribution": "norm", // norm | lognorm | t
|
|
191
|
+
"period_years": 25,
|
|
192
|
+
"scenarios": 500, // ≤ 5000
|
|
193
|
+
"percentiles": [5, 50, 95],
|
|
194
|
+
"random_seed": 42 // optional, for reproducibility
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// CashflowSpec — discriminated by `type`
|
|
198
|
+
{ "type": "indexation", "initial_investment": 1000000, "frequency": "month", "amount": -1000, "indexation": "inflation" }
|
|
199
|
+
{ "type": "percentage", "initial_investment": 1000000, "frequency": "year", "percentage": -0.04 }
|
|
200
|
+
{ "type": "time_series", "initial_investment": 100000, "events": { "2030-06": -50000 } }
|
|
201
|
+
{ "type": "vanguard", "initial_investment": 1000000, "percentage": -0.04, "floor_ceiling": [-0.025, 0.05], "indexation": "inflation" }
|
|
202
|
+
{ "type": "cut_if_drawdown", "initial_investment": 1000000, "frequency": "year", "amount": -60000, "indexation": "inflation",
|
|
203
|
+
"crash_threshold_reduction": [[0.2, 0.4], [0.5, 1.0]] }
|
|
204
|
+
|
|
205
|
+
// FrontierSpec
|
|
206
|
+
{
|
|
207
|
+
"assets": ["SPY.US", "BND.US", "GLD.US"],
|
|
208
|
+
"ccy": "USD",
|
|
209
|
+
"bounds": [[0.0, 0.7], [0.1, 1.0], [0.0, 0.3]], // optional
|
|
210
|
+
"n_points": 20,
|
|
211
|
+
"rebalancing_period": "year",
|
|
212
|
+
"inflation": false
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## Development
|
|
217
|
+
|
|
218
|
+
The project follows TDD (see `AGENTS.md`). After every code change run:
|
|
219
|
+
|
|
220
|
+
```bash
|
|
221
|
+
poetry run pytest -q
|
|
222
|
+
poetry run ruff check .
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
To run the live-API integration test (hits `api.okama.io`):
|
|
226
|
+
|
|
227
|
+
```bash
|
|
228
|
+
poetry run pytest -m integration
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
## Project layout
|
|
232
|
+
|
|
233
|
+
```
|
|
234
|
+
src/okama_mcp/
|
|
235
|
+
├── server.py # FastMCP instance + registration entry point
|
|
236
|
+
├── transport.py # CLI: `okama-mcp stdio | http`
|
|
237
|
+
├── schemas.py # PortfolioSpec, MCSpec, CashflowSpec, FrontierSpec
|
|
238
|
+
├── cache.py # TTL+LRU cache keyed by sha256 of canonical spec
|
|
239
|
+
├── serialization.py # pandas → JSON-safe with smart truncation
|
|
240
|
+
├── errors.py # Translate okama exceptions to actionable MCP errors
|
|
241
|
+
└── tools/
|
|
242
|
+
├── search.py, asset.py, asset_list.py
|
|
243
|
+
├── portfolio.py, monte_carlo.py
|
|
244
|
+
├── frontier.py, macro.py
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
## License
|
|
248
|
+
|
|
249
|
+
Same as okama itself: MIT.
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
[tool.poetry]
|
|
2
|
+
name = "okama-mcp"
|
|
3
|
+
version = "0.2.0"
|
|
4
|
+
description = "MCP (Model Context Protocol) server exposing the okama investment portfolio toolkit to AI assistants"
|
|
5
|
+
authors = ["Sergey Kikevich <sergey@rostsber.ru>"]
|
|
6
|
+
readme = "README.md"
|
|
7
|
+
license = "MIT"
|
|
8
|
+
homepage = "https://mcp.okama.io"
|
|
9
|
+
repository = "https://github.com/mbk-dev/okama-mcp"
|
|
10
|
+
keywords = ["mcp", "okama", "investments", "portfolio", "finance", "llm"]
|
|
11
|
+
classifiers = [
|
|
12
|
+
"Development Status :: 4 - Beta",
|
|
13
|
+
"Intended Audience :: Financial and Insurance Industry",
|
|
14
|
+
"Topic :: Office/Business :: Financial :: Investment",
|
|
15
|
+
]
|
|
16
|
+
packages = [{ include = "okama_mcp", from = "src" }]
|
|
17
|
+
|
|
18
|
+
[tool.poetry.dependencies]
|
|
19
|
+
python = ">=3.11,<4.0.0"
|
|
20
|
+
okama = ">=2.1.0"
|
|
21
|
+
fastmcp = ">=2.7"
|
|
22
|
+
pydantic = ">=2.0"
|
|
23
|
+
|
|
24
|
+
[tool.poetry.group.test]
|
|
25
|
+
optional = false
|
|
26
|
+
|
|
27
|
+
[tool.poetry.group.test.dependencies]
|
|
28
|
+
pytest = "*"
|
|
29
|
+
pytest-asyncio = "*"
|
|
30
|
+
|
|
31
|
+
[tool.poetry.group.dev]
|
|
32
|
+
optional = false
|
|
33
|
+
|
|
34
|
+
[tool.poetry.group.dev.dependencies]
|
|
35
|
+
ruff = "*"
|
|
36
|
+
|
|
37
|
+
[tool.poetry.scripts]
|
|
38
|
+
okama-mcp = "okama_mcp.transport:main"
|
|
39
|
+
|
|
40
|
+
[build-system]
|
|
41
|
+
requires = ["poetry-core>=1.0.0"]
|
|
42
|
+
build-backend = "poetry.core.masonry.api"
|
|
43
|
+
|
|
44
|
+
[tool.ruff]
|
|
45
|
+
line-length = 120
|
|
46
|
+
extend-exclude = [".ipynb_checkpoints", "env", ".env"]
|
|
47
|
+
target-version = "py311"
|
|
48
|
+
|
|
49
|
+
[tool.ruff.lint]
|
|
50
|
+
select = ["C", "E", "F", "W", "B", "UP"]
|
|
51
|
+
ignore = ["E203", "E501"]
|
|
52
|
+
|
|
53
|
+
[tool.ruff.lint.per-file-ignores]
|
|
54
|
+
"__init__.py" = ["F401"]
|
|
55
|
+
|
|
56
|
+
[tool.pytest.ini_options]
|
|
57
|
+
testpaths = ["tests"]
|
|
58
|
+
asyncio_mode = "auto"
|
|
59
|
+
markers = [
|
|
60
|
+
"integration: live test that hits api.okama.io (slow, network-dependent)",
|
|
61
|
+
]
|
|
62
|
+
addopts = "-m 'not integration'"
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"""okama-mcp: an MCP server wrapping the okama investment toolkit.
|
|
2
|
+
|
|
3
|
+
Exposes a FastMCP server through both stdio and streamable-http transports so AI
|
|
4
|
+
assistants can run portfolio backtests, Monte Carlo forecasts, efficient-frontier
|
|
5
|
+
optimisation and macro queries via tool calls.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
__version__ = "0.1.0"
|