mcp-monte-carlo
# mcp-monte-carlo
**Give any AI agent the power to run a serious Monte Carlo forecast for a stock or ETF — in one tool call.**
This is an [MCP](https://modelcontextprotocol.io/) (Model Context Protocol) server. Connect it once to Hermes, Claude Desktop, Cursor, or any MCP-capable agent, and the agent can download market history, fit a volatility model, simulate thousands of future price paths, and return percentiles, drawdowns, and risk probabilities — without you writing a single line of simulation code.
```text
You: "What does a bad year look like for SPY over the next 12 months?"
Agent → forecast_asset_monte_carlo("SPY")
→ EGARCH + skewed-t Monte Carlo (5,000 paths by default)
You ← JSON: price/return percentiles, vol, max drawdowns, loss probabilities
```
---
## Why this matters
Large language models are excellent at reasoning and explanation. They are **not** engines for sampling fat-tailed returns under time-varying volatility. Left alone, an agent might invent plausible-looking percentiles or hand-wave “historical vol $\times\sqrt{T}$”.
This server closes that gap:
| Without this MCP | With this MCP |
|---|---|
| Agent guesses ranges or quotes stale numbers | Agent calls a reproducible statistical pipeline |
| No consistent treatment of crashes / fat tails | Skewed-t innovations model skewness and fat tails |
| Constant-vol assumptions ignore clustering | EGARCH captures shock-driven, asymmetric volatility |
| Hard to compare 7-day vs 10-year risk | Same model, same paths, many horizons in one JSON |
The agent stays in charge of *interpretation* and *conversation*. The MCP owns *estimation* and *simulation*.
---
## What it does (pipeline)
```text
Yahoo Finance (max history)
│ adjusted daily Close
▼
Log returns
│
▼
Fit EGARCH(1,1) + leverage + skewed-t shocks
│ constant mean drift (historical mean)
▼
Simulate N paths (default 5,000) out to 10 years
│
▼
Summarize each horizon → percentiles, vol, MDD, probabilities
```
### 1. Data
Uses [`yfinance`](https://github.com/ranaroussi/yfinance) to pull the **maximum** available daily history. The `Close` field is already adjusted for splits and dividends, so returns are suitable for long-horizon compounding.
### 2. Returns and drift
Prices are converted to **log returns**:
```math
r_t=\ln\left(\frac{P_t}{P_{t-1}}\right)
```
The mean model is **constant**: each simulated day has drift equal to the fitted historical average $\mu$. That is a simple, transparent assumption — not a crystal ball for future expected return.
### 3. Volatility: EGARCH with leverage
Equity volatility is neither constant nor symmetric:
- **Volatility clustering** — turbulent days tend to follow turbulent days.
- **Leverage effect** — large *down* moves tend to raise future vol more than equally large *up* moves.
This server fits **EGARCH(1,1) with leverage** ($p=1$, $o=1$, $q=1$) via the [`arch`](https://arch.readthedocs.io/) package. Conditionally, log-variance evolves roughly as:
```math
\ln(\sigma_t^2)=\omega+\alpha\bigl(\lvert z_{t-1}\rvert-\mathbb{E}[\lvert z\rvert]\bigr)+\gamma z_{t-1}+\beta\ln(\sigma_{t-1}^2)
```
For equities, the leverage coefficient $\gamma$ is typically **negative**: a negative shock $z$ increases tomorrow’s volatility.
### 4. Shocks: skewed Student-t
Gaussian shocks understate crash risk. Standardized innovations are drawn from a **skewed t** distribution, so simulated paths can show:
- fat tails (extreme moves more often than a normal),
- skewness (asymmetric left/right risk).
### 5. Monte Carlo paths
Given the fitted parameters, the server simulates $N$ forward trajectories (`n_paths`; vectorized NumPy loop for stability out to multi-year horizons). Each path is a full price series; horizons are slices of those same paths so short- and long-term stats are coherent.
### 6. Horizons (trading days)
| Label | Trading days | Rough calendar |
|------:|-------------:|----------------|
| `7d` | 5 | ~1 week |
| `30d` | 21 | ~1 month |
| `3m` | 63 | ~3 months |
| `6m` | 126 | ~6 months |
| `1y` | 252 | ~1 year |
| `3y` | 756 | ~3 years |
| `5y` | 1260 | ~5 years |
| `10y` | 2520 | ~10 years |
---
## Tools
### `forecast_asset_monte_carlo(ticker, n_paths=5000)`
**When to use:** The user wants forward scenarios, risk ranges, or path statistics for a ticker (e.g. `SPY`, `AAPL`).
For **each** horizon, the JSON includes:
- **Price percentiles** — `1, 5, 10, 25, 50, 75, 90, 95, 99`
- **Return percentiles (%)** — same grid, vs today’s price
- **Annualized volatility (%)** — cross-sectional vol of path outcomes at that horizon
- **Max-drawdown percentiles (%)** — peak-to-trough loss along each path up to that horizon
- **Probabilities** — end below start, ±20% moves, max drawdown over 20%
`n_paths` defaults to `5000` (minimum `100`). More paths → smoother percentile estimates, slower run.
### `inspect_asset_model(ticker)`
**When to use:** Validate data quality or model sanity *before* (or instead of) a full forecast — enough history? sensible parameters? how fat are residual tails?
Returns history span, last price, fitted EGARCH + skew-t parameters, AIC/BIC, last conditional volatility (daily and annualized), and residual skewness / excess kurtosis.
Does **not** simulate paths. Prefer `forecast_asset_monte_carlo` for percentiles and drawdowns.
---
## Requirements
- macOS, Linux, or Windows
- [uv](https://docs.astral.sh/uv/) (recommended)
- Python **≥ 3.12** (declared in `pyproject.toml`)
- Network access (Yahoo Finance download)
---
## Quick start (local)
```bash
cd /path/to/mcp-monte-carlo
uv sync
```
Smoke-test without MCP:
```bash
uv run python -c "
from server import run_inspect, run
import json
print(json.dumps(run_inspect('SPY'), indent=2))
print(json.dumps(run('SPY', 200)['horizons']['1y'], indent=2))
"
```
Run the MCP server on stdio:
```bash
uv run mcp-monte-carlo
# or, from a published clone / path:
uvx --from /path/to/mcp-monte-carlo mcp-monte-carlo
```
---
## Connect an AI agent
### Hermes Agent (`~/.hermes/config.yaml`)
Prefer `uv run` against a synced project (faster and more reliable than a cold `uvx`):
```yaml
mcp_servers:
mcp-monte-carlo:
command: /opt/homebrew/bin/uv # which uv → paste absolute path
args:
- run
- --directory
- /ABSOLUTE/PATH/TO/mcp-monte-carlo
- mcp-monte-carlo
connect_timeout: 120
timeout: 300
```
Then: `hermes mcp test mcp-monte-carlo` or `/reload-mcp` in a chat.
### Cursor / Claude Desktop
```json
{
"mcpServers": {
"mcp-monte-carlo": {
"command": "uvx",
"args": [
"--from",
"/ABSOLUTE/PATH/TO/mcp-monte-carlo",
"mcp-monte-carlo"
]
}
}
}
```
Once published on GitHub, others can point `--from` at the repo URL or clone locally and use the same pattern.
---
## Example agent prompts
- “Inspect the EGARCH model for `QQQ`, then forecast with 2,000 paths.”
- “For `AAPL`, what is the 5th percentile price in 1 year, and the probability of a >20% max drawdown?”
- “Compare 1-year median and 95th percentile max drawdown for `SPY` vs `TLT`.”
---
## Project layout
```text
mcp-monte-carlo/
├── server.py # MCP tools + EGARCH/skew-t Monte Carlo (single module)
├── pyproject.toml # package metadata, deps, console entry point
├── uv.lock # locked dependency versions
├── README.md
└── .gitignore
```
One Python file keeps the project easy to read, audit, and ship.
---
## Model caveats (read this)
This is a **research / educational risk tool**, not investment advice and not a guarantee of future prices.
- Past drift $\mu$ is **not** a forecast of expected return; long-horizon medians inherit that assumption.
- EGARCH(1,1)+leverage and skewed-t are strong defaults for many liquid equities/ETFs — not universally “optimal” for every ticker.
- Yahoo data quality and corporate actions can affect results; always check `inspect_asset_model` on unfamiliar symbols.
- Extremely long horizons (5y–10y) compound model risk; treat tails as illustrative, not certainties.
---
## License / authorship
Created by Alexandre Martins. Use and adapt freely for personal agents and learning; if you redistribute, keep attribution and these caveats visible.
TDQS
Scored across 2 tools
The two tools have fully distinct purposes: one generates forward Monte Carlo forecasts, while the other inspects model fit without simulating. Each description explicitly states when to use it and when not to, so there is no realistic ambiguity.
Both tool names follow a consistent verb-first snake_case pattern: forecast_asset... and inspect_asset.... The shared '_asset_' segment reinforces that they operate on the same domain, and there is no mix of naming conventions.
Two tools is on the low edge of what feels like a reasonable server surface. Each tool serves a necessary role in the workflow, but the server is minimal and could feel thin to agents expecting additional financial utilities.
For the stated purpose, the core workflow is covered: fit/inspect the model and run forecasts. There is no obvious dead end for the main Monte Carlo use cases, though a direct historical data or backtesting tool would make the surface more complete.