Portfolio analytics via the okama library: backtests, Monte Carlo, efficient frontier, PNG charts
okama-mcp (Model Context Protocol) Server
The io.github.mbk-dev/okama-mcp MCP server provides portfolio analytics built on the okama library. The server description specifies capabilities including backtests, Monte Carlo simulation, efficient frontier analysis, and generation of PNG charts. It is positioned for AI-assistant workflows that need investment analytics outputs.
π οΈ Key Features
Portfolio analytics via the okama library
Backtests
Monte Carlo
Efficient frontier
PNG charts
π Use Cases
Running investment portfolio analytics with an MCP server
Performing backtesting and Monte Carlo evaluations
Computing efficient frontiers and producing chart artifacts (PNG)
β‘ Developer Benefits
Clear analytic scope: backtests, Monte Carlo, efficient frontier
Chart output format called out as PNG
β οΈ Limitations
Provided documentation excerpt does not include supported MCP tools, configuration details, or tool count.
MCP (Model Context Protocol) server that exposes the okama
investment portfolio toolkit to AI assistants β Claude Desktop, Claude Code, Cursor, Codex,
and any other MCP-compatible client.
With okama-mcp installed, you can ask an AI things like:
"Backtest a portfolio of 30% gold and 70% real estate over the last 15 years."
"Run a Monte Carlo retirement forecast on that portfolio, withdrawing $1,000/month
indexed to inflation, over 25 years."
"What's the tangency portfolio of SPY, BND, and GLD with a 3% risk-free rate?"
β¦and the AI uses the MCP tools to call okama directly β no Python code needed.
Built on FastMCP. Single codebase, two transports:
stdio (for local clients) and streamable-http (for self-hosting).
okama-mcp is free and open source β no hosted service, no registration; you run it
yourself, locally or on your own server.
Install
Requires Python β₯ 3.11 (same floor as okama itself); okama β₯ 2.2.0 is installed automatically.
The easiest way β no clone, no venv β is uv or pipx:
bash
uvx okama-mcp stdio # run straight from PyPI# or
pipx install okama-mcp
Plain pip works too:
bash
pip install okama-mcp
WARNING
With pip, prefer a dedicated virtual environment: on most modern Linux distros the
system Python is marked externally managed (PEP 668), so pip install outside a venv
fails, and a shared environment risks dependency conflicts. In your MCP client config,
point command at the absolute path of the okama-mcp script inside the venv β GUI
clients don't see your shell PATH. uvx and pipx avoid all of this by isolating
the install automatically.
To work on the code, install from source instead:
bash
git clone https://github.com/mbk-dev/okama-mcp
cd okama-mcp
poetry install
Run
bash
# stdio β for Claude Desktop, Claude Code, Cursor (local IPC)
okama-mcp stdio
# streamable HTTP β for self-hosting on your own server
okama-mcp http --host 127.0.0.1 --port 8765
When running from a source checkout, prefix each command with poetry run.
Connect a client
Claude Desktop
Edit ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or
%APPDATA%\Claude\claude_desktop_config.json (Windows):
Then point your MCP client at http://<your-server>:8765/mcp. For a production
setup put nginx + TLS in front; ready-made examples live in deploy/:
deploy/systemd/okama-mcp.service β systemd unit (hardened, runs as a dedicated user)
deploy/nginx/self-hosted.conf β nginx vhost: TLS, SSE-friendly proxying of /mcp
The server is open by design β free to run, no registration. If your instance must
not be public, restrict access at the nginx level (allow-list, VPN, or HTTP basic auth).
Tool catalog
A multi-stage financial plan (contribute $1,000/month for 20 years into a 70/30
SPY/AGG portfolio, then withdraw $6,000/month indexed to inflation for 25 years),
a Monte Carlo retirement forecast (30% gold / 70% real estate, withdrawing $1,000/month
indexed to inflation over 25 years) and the efficient frontier of SPY/BND/GLD:
Financial-plan forecast fan β percentile bands with dashed stage boundaries
All tools are stateless β pass the full portfolio specification with every call.
The server caches expensive okama objects (Portfolio, EfficientFrontier) by content
hash, so repeated calls on the same spec are fast.
Nested portfolios. Wherever a list of assets is accepted β the assets field of
PortfolioSpec/FrontierSpec, or the portfolios argument on the comparison tools β
an entry may be a ticker string or a nested portfolio object (the same spec shape).
This lets you treat a whole portfolio as a single component: e.g. compare a 60/40
portfolio against gold, or put a sub-portfolio on the efficient frontier.
Free-text search by name / local name / ticker / ISIN. Filter by okama type and sort by first_date; for example, namespace="MOEX", asset_type="ETF", oldest_first=true, limit=5 finds the five oldest MOEX-listed BPIFs.
list_namespaces(kind="all"|"assets"|"macro")
Show the available okama namespaces.
get_asset_info(symbol)
Metadata for one symbol β name, country, currency, type, date range.
Rolling CAGR time series (optionally inflation-adjusted).
get_cagr_probability(portfolio, years, cagr_target)
Historical probability of CAGR below a target (e.g. of a loss) over N-year periods.
Monte Carlo DCF
Tool
Purpose
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, survival metrics. Includes the money-weighted IRR distribution (percentiles + mean).
get_portfolio_irr(portfolio, cashflow)
Historical money-weighted return (IRR) for a contribution/withdrawal plan.
Monte Carlo distribution of future cash flows over time (percentile bands).
The mc argument accepts distribution_parameters to override the fitted distribution (e.g. a fixed Student-t df); see the MCSpec shape below.
Financial plan (multi-stage)
A plan is an ordered sequence of stages, each with its own portfolio, horizon and
cash-flow regime. Scenarios are chained: the balance a Monte Carlo scenario ends a
stage with is the balance it starts the next one with, so the retirement stage is
funded by whatever the accumulation stage produced in that same scenario β not by
a percentile of it. Use these instead of monte_carlo_forecast whenever the portfolio
or the contribution/withdrawal regime changes partway through the horizon.
Tool
Purpose
finplan_forecast(plan, success_threshold=0)
Monte Carlo forecast of the whole plan: percentile wealth bands, terminal-wealth stats, survival metrics, the share of scenarios finishing above success_threshold, the balance distribution at every stage boundary, and the IRR distribution.
finplan_backtest(plan, discounting?, first_date?)
Replay the same plan over real history β a glide-path backtest. Requires the window covered by every stage portfolio to be at least as long as the plan.
Distribution diagnostics
Tool
Purpose
get_distribution_fit(portfolio, mc)
Goodness-of-fit for the return distribution: fitted parameters, Jarque-Bera, Kolmogorov-Smirnov (chosen + all distributions), and backtesting error (theoretical vs empirical mean/VaR/CVaR).
Macro indicator from the RATIO namespace (e.g. USA_CAPE10.RATIO); bare country code defaults to that country's CAPE10.
Charts
Each tool renders a PNG (default 1500Γ900) and returns it as MCP image content β
clients like Claude Desktop display it inline. Every chart tool also accepts
optional width / height (pixels, 300β4000) for custom sizes and aspect ratios,
and an optional save_path β the chart is then also written to that file and the
path reported back. Use save_path in clients that don't render MCP images in
their UI (e.g. Claude Code's terminal): ask for a chart "saved to /tmp/chart.png"
and open the file reference. Note: in self-hosted (streamable-http) deployments
save_path is written on the server's filesystem, not the client's machine.
Tool
Chart
plot_wealth_index(portfolio)
Portfolio wealth index (+ inflation line).
plot_drawdowns(portfolio)
Drawdown depth over time.
plot_monte_carlo(portfolio, mc, cashflow)
Monte Carlo forecast fan (percentile bands).
plot_finplan_forecast(plan)
Financial-plan forecast fan: percentile bands with dashed stage boundaries and stage labels.
plot_irr_distribution(portfolio, mc, cashflow)
Histogram of IRR across Monte Carlo scenarios (percentile markers).
plot_qq(portfolio, mc)
Q-Q plot of historical returns against the fitted distribution (norm/lognorm/t).
plot_hist_fit(portfolio, mc, bins?)
Histogram of historical returns with the fitted distribution PDF overlaid.
plot_efficient_frontier(frontier)
EF curve with individual asset points.
plot_transition_map(frontier, x_axe="risk")
Transition map: asset weights along the efficient frontier (x-axis = risk or CAGR).
Line chart of inflation / central-bank rate / CAPE10 series. Overlay multiple symbols (e.g. ["USA_CAPE10.RATIO", "EUR_CAPE10.RATIO"]). frequency='daily' valid only for .RATE symbols.
Spec shapes
The complex tools take typed dicts validated by pydantic. The full schemas live in
src/okama_mcp/schemas.py; here are the headline shapes:
jsonc
// PortfolioSpec{"assets":["GLD.US","VNQ.US"],// each entry: a ticker OR a nested PortfolioSpec"weights":[0.3,0.7],// optional, must sum to 1.0"ccy":"USD","first_date":"2010-01","last_date":"2024-12","rebalancing_strategy":{// mirrors okama.Rebalance"period":"year",// month | quarter | half-year | year | none"abs_deviation":0.05,// optional, |actual - target| threshold, 0 < x <= 1"rel_deviation":0.1// optional, |actual / target - 1| threshold, > 0},"inflation":true}// MCSpec{"distribution":"norm",// norm | lognorm | t"period_years":25,"scenarios":500,// β₯ 1, no upper limit"percentiles":[5,50,95],"random_seed":42,// optional, for reproducibility"distribution_parameters":null// optional; null = fit from history (MLE). Lengths: norm [mu, sigma]; lognorm/t [shape|df, loc, scale]. Any element null = fit that one (e.g. [4, null, null])}// CashflowSpec β discriminated by `type`{"type":"indexation","initial_investment":1000000,"frequency":"month","amount":-1000,"indexation":"inflation"}{"type":"percentage","initial_investment":1000000,"frequency":"year","percentage":-0.04}{"type":"time_series","initial_investment":100000,"events":{"2030-06":-50000},"time_series_discounted_values":false}{"type":"vanguard","initial_investment":1000000,"percentage":-0.04,"floor_ceiling":[-0.025,0.05],"indexation":"inflation"}{"type":"cut_if_drawdown","initial_investment":1000000,"frequency":"year","amount":-60000,"indexation":"inflation","crash_threshold_reduction":[[0.2,0.4],[0.5,1.0]]}// FinPlanSpec β a plan is a sequence of stages, chained per scenario{"stages":[{"portfolio":{"assets":["SPY.US","AGG.US"],"weights":[0.7,0.3]},"period_years":20,"name":"accumulation","cashflow":{"type":"indexation","initial_investment":100000,"frequency":"year","amount":12000,"indexation":0.03}},{"portfolio":{"assets":["SPY.US","AGG.US"],"weights":[0.3,0.7]},"period_years":25,"name":"retirement","distribution":"t","distribution_parameters":[5,null,null],"cashflow":{"type":"percentage","initial_investment":100000,"frequency":"year","percentage":-0.04}}],"initial_investment":100000,// balance the first stage starts with"discount_rate":null,// optional; null lets okama use inflation"scenarios":500,"random_seed":42,// optional"percentiles":[10,50,90],"name":"retirement plan"}// FrontierSpec{"assets":["SPY.US","BND.US","GLD.US"],"ccy":"USD","bounds":[[0.0,0.7],[0.1,1.0],[0.0,0.3]],// optional"n_points":20,"rebalancing_strategy":{"period":"year"},"inflation":false}// Nesting β a portfolio used as a single component (works in PortfolioSpec /// FrontierSpec `assets`, and the `portfolios` argument of the comparison tools):{"assets":["GLD.US",{"assets":["SPY.US","AGG.US"],"weights":[0.6,0.4],"symbol":"bench6040.PF"}],"weights":[0.3,0.7]// one weight per top-level entry}
Development
The project follows TDD (see AGENTS.md). After every code change run:
bash
poetry run pytest -q
poetry run ruff check .
To run the live-API integration test (hits api.okama.io):