portfolio-analytics-mcp

portfolio-analytics-mcp

MCP server providing portfolio analytics tools: beta to a benchmark, sector correlation, and FIFO trade matching with realized/unrealized P&L using Yahoo Finance prices.

Category
Visit Server

README

portfolio-analytics-mcp

An MCP server that gives an AI agent three portfolio analytics tools: beta to a benchmark, correlation between sectors, and FIFO trade matching with realised and unrealised P&L.

Point an agent at it and ask "what's the beta of this book to the S&P", "are my sectors actually diversified", or "what did I make on these fills" — in natural language, against a portfolio you supply.

No API key. Prices come from Yahoo Finance, so a bare checkout works.


Tools

Tool Answers You supply
portfolio_beta How sensitive is this portfolio to the market? Holdings (+ optional weights), benchmark
sector_correlation Is this book actually diversified, or is everything one bet? Holdings with sector labels
revalue_positions What did I make, and what's still open? A list of fills, optionally current marks

What these tools do not do

They have no brokerage connection and no account access. Nothing here can look up what you own — you pass the portfolio in. That is a deliberate boundary, not a missing feature: the analytics are useful without ever touching a broker, and the server has no business holding credentials.


Install

git clone https://github.com/quanttrucker/portfolio-analytics-mcp
cd portfolio-analytics-mcp
python3 -m venv .venv && .venv/bin/pip install -e .

Register it with an MCP client — for Claude Desktop, in claude_desktop_config.json:

{
  "mcpServers": {
    "portfolio-analytics": {
      "command": "/absolute/path/to/portfolio-analytics-mcp/.venv/bin/portfolio-analytics-mcp"
    }
  }
}

Restart the client and the three tools appear.


Demo

A recorded session of a real agent (claude-opus-5) driving this server over stdio — MCP handshake, live Yahoo prices, plain-English questions, verbatim tool calls and answers. It comes in two forms:

  • demo/transcript.html — the session rendered as a self-contained page: tool calls as cards, results as charts (beta bars, sector correlation heatmap, P&L tiles), raw JSON collapsed underneath. Open it in a browser; no build step, no external assets.
  • demo/TRANSCRIPT.md — the same session as plain markdown, the source of record.

What the session shows, one exchange per tool:

  1. "I hold 60% AAPL and 40% MSFT — what's my beta to SPY?" → one portfolio_beta call with the weights and benchmark filled in correctly; answer 0.83 with a per-holding decomposition.
  2. "Is my portfolio actually diversified?" → sector_correlation and portfolio_beta called in parallel; the agent states its equal-weight assumption, reads the near-zero cross-sector correlations, and flags that XOM's −0.48 beta is regime-specific rather than presenting it as a stable hedge.
  3. "I bought 100 AAPL at 180, sold 40 at 195, it's at 210 now — what did I make?" → revalue_positions matches the fills FIFO: $600 realised, $1,800 unrealised on the 60-share remainder.

Re-record against the current market with:

python demo/transcript.py    # needs the same .env credentials as the evals

Example

Matching two fills and marking what's left open:

// revalue_positions
{
  "executions": [
    {"symbol": "AAA", "side": "BUY",  "quantity": 100, "price": 10.0, "timestamp": "2025-01-02T10:00:00"},
    {"symbol": "AAA", "side": "SELL", "quantity": 40,  "price": 12.5, "timestamp": "2025-01-09T15:30:00"}
  ],
  "marks": {"AAA": 13.0}
}
{
  "realised_pnl_base": 100.0,          // 40 units closed at +2.50
  "open_lots": [
    {"symbol": "AAA", "direction": "Long", "quantity": 60,
     "entry_price": 10.0, "unrealised_pnl_currency": 180.0}
  ]
}

Details worth knowing

FIFO matching is symmetric. A sell consumes the oldest open lots first; any excess opens a position the other way, so a sell of 150 against a long of 100 closes the 100 and leaves a short of 50. Shorts work identically in reverse. Realised P&L converts at the closing fill's FX rate, which is where the gain is crystallised.

London prices are handled. Yahoo quotes LSE listings in pence and reports their currency as GBp, not GBP. Left alone that inflates a UK holding 100× against everything else in the portfolio; here it is normalised to major units at ingestion. Non-US listings take an exchange code (LSE, IBIS, SEHK) to resolve the venue.

Cross-venue portfolios don't share a trading calendar. A UK line and a US line disagree on holidays, so on some dates one is missing. Summing across such a row drops the absent member's weight rather than its return, understating the portfolio on exactly the days two markets diverge. Returns are complete-case by default.

Undefined statistics come back as null, never as a number. A beta estimated on too few overlapping observations is null with a note saying so, rather than a figure that looks authoritative. A sector whose members offset each other exactly has no variance, so its correlation is genuinely undefined — also null, not zero.

Prices are cached to disk. Yahoo is unofficial and occasionally flaky. Fetches are cached (12h TTL) under ~/.cache/portfolio-analytics-mcp, overridable with PORTFOLIO_ANALYTICS_CACHE. A corrupt cache entry refetches rather than failing.


Development

.venv/bin/pip install -e ".[dev]"
.venv/bin/python -m pytest

The test suite never touches the network — the price downloader sits behind a protocol and is faked. Symbology and the pence conversion are additionally checked against the live feed by hand, since those are the two claims a fake cannot validate.


Licence

MIT

Recommended Servers

playwright-mcp

playwright-mcp

A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.

Official
Featured
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.

Official
Featured
Local
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

graphlit-mcp-server

The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.

Official
Featured
TypeScript
Kagi MCP Server

Kagi MCP Server

An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.

Official
Featured
Python
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
Exa Search

Exa Search

A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured