{
  "markdown": "# Finance MCP\n\n[![tests](https://github.com/Blahaj-gif/Finance-mcp/actions/workflows/tests.yml/badge.svg)](https://github.com/Blahaj-gif/Finance-mcp/actions/workflows/tests.yml)\n[![python](https://img.shields.io/badge/python-3.10%20%7C%203.12-blue)](https://github.com/Blahaj-gif/Finance-mcp/blob/master/pyproject.toml)\n[![licence](https://img.shields.io/badge/licence-MIT-green)](https://github.com/Blahaj-gif/Finance-mcp/blob/master/LICENSE)\n\nAn MCP server that gives an AI assistant read access to your real brokerage\naccount and the market around it — and gives you, not the assistant, the only\nbutton that sends an order.\n\n38 tools over Webull, Saxo, IBKR, Yahoo Finance, SEC EDGAR, BLS, the Federal\nReserve and the BEA, plus a Streamlit dashboard that is the sole path to\nexecution. The list is filtered to what your broker can actually serve, so a\ngiven account sees 36 or 37 of them — and **28 with no broker at all**, which\nis a working install, not a broken one.\n\n**The assistant can draft an order. It cannot place one.** Drafts go to a local\nqueue; sending requires a broker preview and your click in the dashboard. That\npath does not exist on the tool side, so no prompt can reach it.\n\nEvery tool declares this to the client rather than only to the reader: **32 of\n36 are annotated read-only, one is destructive** (`cancel_order`, which pulls a\nresting order). A client that respects MCP annotations can wave through a price\nlookup and stop on the one call that reaches the market.\n\n### What this can and cannot do with your account\n\nAsking an unknown repository for brokerage credentials is the hardest trust ask\nin software, so the answer is here rather than forty paragraphs down.\n\n- **There is no execution path on the tool side.** Not gated, not guarded —\n  absent. `draft_order` writes to a local queue. Sending needs a broker preview\n  and your click in the dashboard, which no prompt can reach.\n- **Twenty-eight of the 38 tools never touch a broker.** SEC filings, macro,\n  indicators, options maths, earnings. With no credentials at all you get those\n  twenty-eight, and that is a working install rather than a degraded one.\n- **Two of the three adapters have never been run.** Saxo and Interactive\n  Brokers are built from published API references and have not been executed\n  against a live or paper account. They say so in every tool output that uses\n  them. Only Webull has been run against a real account.\n- **The dashboard's submit button is Webull-only.** The MCP tools route through\n  the broker protocol; that one button does not, yet.\n- **Use a read-scoped key where your broker offers one.** Nothing outside the\n  dashboard needs write access.\n\n1,431 tests across 32 files, and CI runs them on every push.\n\nThose all stub the network, which is correct — a suite that goes red because SEC\nreturned a 503 overnight is a suite nobody reads. It is also why two defects\nsurvived every one of them: a euro-area series that had stopped publishing eight\nmonths earlier and was reported as current, and a filing watcher that answered\n403 to every request and called itself healthy. Both needed the real internet to\nsee. `tests/test_live_sources.py` asks every external source for real and checks\nthat this tool's account of the answer is true — in particular that a series\nmarked fresh actually is. It is opt-in and skipped by default:\n\n```\nFINANCE_LIVE_SOURCES=1 pytest tests/test_live_sources.py -v -s\n```\n\nIt also runs weekly in CI, because a check that depends on somebody remembering\nis a check that eventually stops happening — and discontinued series and broken\nscrapers are exactly the failures that arrive quietly over weeks. That workflow\nis inert until `FRED_API_KEY` and `SEC_USER_AGENT` exist as repository secrets,\nand when they do not it says so in the job summary rather than reporting a green\ntick that verified nothing.\n\n---\n\n![Charts tab](https://raw.githubusercontent.com/Blahaj-gif/Finance-mcp/master/docs/img/charts.png)\n\n*Candles with overlays, a volume pane and the forecast cone. Weekends and market\nholidays are collapsed, so there are no blank stretches.*\n\n---\n\n## What it does\n\n| | |\n|---|---|\n| **Prices** | Live OHLCV from Webull with a Yahoo fallback, behind an integrity gate that checks bar ordering, staleness in trading sessions, and OHLC sanity. Every price says which bar it came from and how old that bar is. |\n| **Analysis** | 96 pinned technical indicators, volume profile with POC and value area, Black-Scholes greeks and implied volatility, backtesting, position sizing from an ATR-aware stop, portfolio concentration and correlation. |\n| **Filings** | SEC EDGAR parsed rather than forwarded: Form 4 with transaction codes and 10b5-1 status, 8-K by item code, 13F, 13D/G, 144, NPORT, inline XBRL. One Form 4 is ~6,600 tokens of XML; the tool returns the answer instead. |\n| **Macro** | Economic calendar from BLS, the Fed (FOMC, dot-plot meetings flagged) and BEA (PCE, GDP), each row carrying the print that happened or the prior one, never a forecast. |\n| **Execution** | Draft, broker preview, human approval. Pre-trade checks block naked shorts, verify per-currency buying power, and refuse orders the broker's own rules would reject. |\n\n---\n\n## What you need\n\n| | |\n|---|---|\n| **OS** | Windows, macOS, Linux. Desktop alerts use each platform's own notifier (PowerShell, `osascript`, `notify-send`) and report plainly when a machine has none, which a headless server will not. |\n| **Install** | `uv tool install --with streamlit --with plotly hitl-finance-mcp`, then point your client at the `finance-mcp` command. Scripted installers (`install.sh`, `install.bat`) add a config template, client registration and a shortcut. |\n| **Python** | 3.10 or 3.12, both covered by CI. `uv` is installed for you. |\n| **Broker** | Optional. Webull, Saxo or IBKR — set `FINANCE_BROKER`. Only Webull has been run against a live account. With no broker credentials, the account and order tools are not registered, prices come from Yahoo, and the other 28 tools work normally. |\n| **Keys** | `SEC_USER_AGENT` (a contact address, required by the SEC for filings) and optionally a free BLS key. |\n\n**[Installation guide →](https://github.com/Blahaj-gif/Finance-mcp/blob/master/INSTALL.md)** — fifteen minutes, most of it waiting for\nfree API keys.\n\n> **This places real orders against a real account.** The consensus score is a\n> fixed-weight heuristic that underperformed buy-and-hold in backtest and is\n> labelled as such throughout. Not financial advice — see\n> [NOTICE.md](https://github.com/Blahaj-gif/Finance-mcp/blob/master/NOTICE.md)\n> for the full disclaimers and the third-party data terms.\n\n---\n\n## Brokers\n\n| Broker | Status |\n|---|---|\n| **Webull** | Verified end to end — a real order drafted, previewed, placed, watched resting and cancelled. |\n| **Saxo Bank** | **Unverified.** Built from Saxo's published OpenAPI reference and never run against their API. |\n| **Interactive Brokers** | **Unverified.** Built from the Client Portal Web API reference and never run against it. Needs the local Client Portal Gateway. |\n\nBoth unverified adapters say so in every tool output that uses them, and refuse\nrather than guess on the paths the docs did not pin down.\n\n`dashboard/broker_protocol.py` is the interface; `tests/test_broker_conformance.py`\nruns the same suite against every adapter. `FINANCE_BROKER=ibkr` selects which\nadapter the protocol and the MCP tools report through.\n\n### What works with which broker\n\n**28 of the 38 tools are broker-agnostic** — indicators, options, SEC filings,\ninsider and institutional data, earnings, macro. They work the same whoever you\nclear through, though the *prices* underneath them now come from your broker\nwhere it serves bars (see below).\n\nEight are **account and order tools**, and they go through\n`broker_protocol.py`, so they work with any adapter that declares the capability\nthey need:\n\n| Tool | Needs |\n|---|---|\n| `get_account_info` `get_open_positions` `get_open_orders` | `accounts` `positions` `open_orders` |\n| `draft_order` `preview_order` `cancel_order` | `buying_power` `preview_order` `cancel_order` |\n| `calculate_position_size` `get_portfolio_risk` | `buying_power` `positions` |\n\n**A tool is only registered when the configured broker can serve it.** Start with\n`FINANCE_BROKER=saxo` and `cancel_order` is not in `tools/list` at all — Saxo\ncancels by its own order id and documents no mapping from ours, so a cancel tool\nwould be a tool that can only refuse. An unusable tool costs a model context on every\nrequest and is one more wrong choice available to it.\n\nThe same rule covers having no broker. Every adapter constructs lazily, so that\nlisting tools never opens a socket — which meant an empty `.env` built a broker\nobject quite happily and all eight account tools were offered to someone who\ncould not call a single one of them. They now check for their credentials\noffline and register only if they have them, so a broker-free install lists 28\ntools that all work rather than 36 of which eight cannot. Where the answer is\ngenuinely unknowable offline — IBKR's Client Portal Gateway holds the session\nafter a browser login and wants no token at all — the tools stay registered,\nbecause hiding a tool that would have worked leaves nobody a way to find out why.\n\nTwo more exist only for the broker that has them: **`saxo_corporate_actions`**\n(dividends, splits, tenders and their election deadlines — Saxo is the only one\nof the three with them) and **`ibkr_market_scanner`** (the exchange-side scan,\nrather than inferring rotation from eleven ETF price pulls).\n\nCapability is resolved per **account**, not per broker name, because three\ndifferent things have to line up:\n\n```\ncapability = what the SDK implements\n           ∩ what the regional entity serves\n           ∩ what this account is entitled to\n```\n\nWebull alone runs twelve independent regional entities on separate hosts, and\nmarket-data entitlements are bought per account on top of that — an order book\nthat returns one level means an L1 subscription, not a missing endpoint.\n`dashboard/capabilities.py` keeps a cache keyed on broker × entity × account, a\nprobe can only ever *withdraw* a capability and never invent one, and it records\n**what** a call did rather than why. Inferring a cause from an error message is\nhow this file previously came to blame a region for a parameter mistake.\n\n### Macro releases\n\nCPI prints at 08:30:00 ET. The calendar carries the print that happened or the\nprior one, **never a forecast** — there is no consensus feed here, street\nestimates are a licensed product, so a \"surprise\" against a prior reading is not\na surprise.\n\nNear a scheduled release the macro cache collapses from six hours to three\nseconds, so the answer is the freshest available whenever you ask. Set\n`FINANCE_MACRO_WATCH=1` and a background thread also fetches the print as it\npublishes, so it is already in hand.\n\nNeither bursts. BLS documents 50 requests per 10 seconds; the fast cadence uses\nten. Polling faster does not make BLS publish sooner — a burst of identical\nrequests fired at the instant all return the same stale payload, because the\nwait is on an external event rather than on throughput.\n\n### Prices come from your broker\n\n`fetch_data` tries the configured broker first and Yahoo second, so a Saxo user\ngets Saxo's `/chart/v1/charts` and an IBKR user gets\n`/iserver/marketdata/history` — for **every** price in the server, not just the\naccount tools. Before this, supplying Saxo or IBKR credentials still left the\nwhole price feed on the public fallback, which was invisible because the tools\nstill worked. The fallback always announces itself when it is used.\n\nBoth broker feeds sort ascending before returning. Webull returned newest-first\nand nothing sorted it, so every indicator ran on a reversed series and the\nsector heatmap ranked the worst performers as leaders; that is not a mistake\nworth making twice.\n\n`contract_rules()` fetches real tick and lot sizes, and `rule_violations()` uses\nthem when supplied — the check that catches an order preview prices cleanly and\nplacement then refuses.\n\n**The dashboard's submit button is still Webull-only.** The MCP tools route\nthrough the protocol; the Streamlit approve-and-submit path does not yet, and\nmoving it means putting two adapters nobody has run on the one code path that\nhas been exercised for real. Verification comes first — which is what\n[HELP-WANTED.md](https://github.com/Blahaj-gif/Finance-mcp/blob/master/HELP-WANTED.md) is asking for.\n\nIBKR is the Client Portal **Web** API — plain request/response JSON — not the TWS\nsocket API. That distinction is why it is an adapter and not a rewrite. It also\nbrought the one broker behaviour the protocol did not already have: IBKR can\nanswer a placement with warnings instead of an order, each needing confirmation\nbefore anything is transmitted. Client libraries normally answer those from a\ntable of canned replies. This one raises them to the person who approved the\norder, which is the whole point of the tool.\n\n**[Help wanted →](https://github.com/Blahaj-gif/Finance-mcp/blob/master/HELP-WANTED.md)** — an hour with a Saxo simulation token or an\nIBKR paper account would close the verification gap. The scripts that do it read\nonly; they never place an order.\n\n---\n\n## Works with any MCP client\n\nNothing in the server is specific to one assistant. It speaks MCP over stdio, so\nanything that speaks MCP can run it. The installer registers it with every\nclient it finds on the machine; to wire one up by hand, add this to that\nclient's MCP config:\n\n```json\n{\n  \"mcpServers\": {\n    \"finance\": {\n      \"command\": \"finance-mcp\",\n      \"args\": []\n    }\n  }\n}\n```\n\n| Client | Where that goes |\n|---|---|\n| Claude Desktop | `%APPDATA%\\Claude\\claude_desktop_config.json` |\n| Claude Code | `claude mcp add finance -- finance-mcp` |\n| Cursor | `~/.cursor/mcp.json` |\n| Windsurf | `~/.codeium/windsurf/mcp_config.json` |\n| VS Code | `.vscode/mcp.json`, or the user-level MCP settings |\n| Anything else | Whatever that client calls its MCP config; the JSON above is the standard shape |\n\nThe dashboard is a separate Streamlit app and does not care which client you\nuse. Order approval happens there regardless.\n\n---\n\n## Macro Calendar & Real-Time Filings\n\nFour public sources sit alongside the broker feed, so Claude can see the events that move price as well as the price itself.\n\n| Tool | What it gives you |\n|---|---|\n| `get_economic_calendar` | Scheduled US events from three sources — BLS (CPI, PPI, NFP, JOLTS), the Federal Reserve (FOMC decisions, flagged when they carry a dot plot), and BEA (PCE, GDP, trade) — each row carrying the actual print where it has happened and the **prior** print where it has not. |\n| `get_updates` | What has changed since a timestamp: new SEC filings, macro releases that printed, and outsized price moves. Answers \"anything new?\" without refetching everything. |\n| `get_macro_data` | Historical CPI, core CPI, unemployment, payrolls, PPI and wages with MoM/YoY changes. `source=\"markets\"` gives policy rates, the yield curve and financial conditions from FRED, the ECB and the Bank of England. |\n| `get_edgar_filings` | SEC filings in three modes: one company's filings, the all-registrant live feed, or full-text search across filing bodies. |\n| `get_insider_activity` | Parsed Form 3/4/5 — who traded, at what price, **whether the sale was under a Rule 10b5-1 plan**, and opening positions. `forms=\"144\"` gives *proposed* sales, filed ahead of the trade, with the plan-adoption date. |\n| `read_filing` | Form-aware: executive pay from a DEF 14A, the cover page of a 13D/13G, the press release from an 8-K, a named Item (Risk Factors, MD&A) from a 10-K, or a text search. |\n| `get_institutional_holdings` | Latest 13F-HR portfolio, positions merged across manager rows. `source=\"NPORT\"` gives a registered fund's monthly portfolio, including the bonds and derivatives 13F omits. |\n\n**Filings are parsed, not forwarded.** Most of what an analyst wants from a filing is already a machine-readable field. \"Was that sale pre-scheduled?\" is `<aff10b5One>` in the Form 4 XML — a boolean. So the server extracts and answers rather than handing over a document: one Form 4 is ~6,600 tokens of raw XML, and a single 10-K is ~610,000 tokens, which is three times a 200k context window. `get_insider_activity` also separates real decisions (codes P/S) from compensation mechanics — grants, option exercises, and shares withheld for tax — which are routinely misreported as \"insiders sold $X\".\n\n**On latency.** EDGAR acceptance timestamps are exact to the second, so an earnings 8-K (item `2.02`) is visible as soon as it is accepted. The delay you experience is your own polling interval, not the feed. `get_earnings` uses that same item code to confirm which quarters were actually released, and flags an upcoming date that Yahoo is only *estimating* — Yahoo publishes an unset date as a window and a set one as a single day, and the two are indistinguishable once formatted.\n\n**No consensus, and it says so.** Street forecasts are a licensed product with no free source, so every comparison in the calendar is against the **previous print** and is labelled that way. A \"surprise\" measured against a prior reading is not a surprise: the market trades the gap to expectations, and expectations are the one thing not available here.\n\n**Rate and quota handling.** BLS allows 25 API queries a day unregistered; the release-schedule pages are ordinary web fetches and deliberately do *not* draw on that budget. Results are cached (6h for macro series, 24h for schedules, 2min for filings), so a repeated calendar call costs nothing. The SEC's 10 req/s ceiling is enforced at the client.\n\n**Normalization.** Filer names and release text arrive in mixed scripts and number conventions. A dedicated layer folds Unicode to a canonical form, expands atomic Latin letters that have no decomposition (`Ærø` → `AEro`, not `r`), converts non-ASCII digits, and parses numbers written US, European, Swiss or Indian style — including accounting negatives like `(1,234.56)`.\n\n### Getting a BLS key (optional, free, ~2 minutes)\n\nUnregistered access is capped at 25 API queries a day. To lift it to 500:\n\n1. Register at [data.bls.gov/registrationEngine](https://data.bls.gov/registrationEngine/) — the key arrives by return email.\n2. Add `BLS_API_KEY=<your key>` to `.env` and restart the server.\n3. Run the `validate_bls_key` tool to confirm it was accepted.\n\nThat last step matters: a mistyped key does not raise an error, it silently drops you back to the 25/day tier, which only shows up days later as an exhausted quota. Registered access also extends history from 10 to 20 years and returns BLS's own computed percentage changes, which the server checks its own arithmetic against.\n\nUse `get_data_sources` at any time to see every source's configuration and remaining quota.\n\n---\n\n## Help wanted\n\nThree things where an hour from someone else is worth more than a day from me:\n\n- **Verify the Saxo adapter.** Written from Saxo's published reference, never run\n  against their API. A 24-hour simulation token is free and needs no approval,\n  and `tests/verify_saxo.py` answers the six open questions without placing an\n  order.\n- **Verify the IBKR adapter.** Same situation, and a **paper account** is enough.\n  Run the Client Portal Gateway, log in through a browser, then\n  `python -m tests.verify_ibkr`. It submits nothing — it reads, and it calls\n  IBKR's own non-binding `whatif`. → **[HELP-WANTED.md](https://github.com/Blahaj-gif/Finance-mcp/blob/master/HELP-WANTED.md)**\n- **Install it on a clean machine.** `install.bat` and `install.sh` are covered\n  by unit tests and inspection, never by a fresh OS — the first thing every new\n  user touches is the least proven path here.\n\nAlso open: **IG Markets** (REST, similar shape to Saxo).\n`dashboard/broker_protocol.py` is the interface;\n`tests/test_broker_conformance.py` runs against anything that implements it.\n\n---\n\n## Where the numbers come from\n\nNot all data carries the same weight, and the tools say which is which.\n\n| Class | Source | Validation |\n|---|---|---|\n| **Prices & bars** | Webull OpenAPI, Yahoo fallback | Full integrity gate: ordering, session-based staleness, OHLC sanity |\n| **Filed financials** | SEC EDGAR XBRL | Authoritative — the filing itself, stamped with form and filing date |\n| **Macro** | BLS | Official series; our MoM/YoY cross-checked against BLS's own figures on the registered tier |\n| **Third-party fundamentals** | Yahoo | Cross-checked against the XBRL filing where a comparable figure exists; disagreements are reported |\n| **Consensus score** | Computed here | A fixed-weight heuristic, labelled as such — it underperformed buy & hold in backtest |\n\n`get_company_financials` returns the filed figures directly. Where Yahoo and the filing disagree, the filing wins and the tool says so.\n\n**IV rank** is the one measure that cannot be sourced authoritatively for free: no public feed publishes implied-volatility history. Rather than pretend otherwise, the server records one ATM IV observation per symbol per day as options are queried, and reports a true IV rank once a symbol has 30 days of its own history. Until then it shows a realised-volatility proxy, explicitly labelled, with a count of how many more observations are needed.\n\n---\n\n## Data Integrity\n\nMarket data is the foundation everything else rests on, so it is checked rather than trusted. Every price frame — from either source — passes a single gate before any tool sees it:\n\n* **Bar ordering is enforced.** The Webull API returns bars newest-first. Frames are sorted ascending and the invariant is asserted, so `.iloc[-1]` is always the most recent bar. *(Without this, tools reported the oldest bar of the window as the current price and computed every indicator on a time-reversed series.)*\n* **Staleness is measured in trading sessions**, using a built-in NYSE calendar — not calendar days, which cannot tell a holiday weekend from an outage.\n* **Sanity checks** reject NaN prices and impossible OHLC bars.\n* **Failures are errors, not text.** Tools raise real MCP errors rather than returning `\"Error: ...\"` as content, so a failure can never be mistaken for a finding.\n* **Source substitution is announced.** When the Webull feed fails and Yahoo serves the request, every affected tool says so.\n\nRun the suite with `pytest`. It is entirely offline — no credentials, no network, no orders.\n\n---\n\n## Installation\n\n### Path A — the installer (Windows, no Python required)\n\n1. Unzip this package anywhere (e.g. your Desktop).\n2. Double-click **`install.bat`**.\n3. It installs the `uv` Python engine if missing, registers the server in\n   `%APPDATA%\\Claude\\claude_desktop_config.json` under the name `finance`,\n   writes a `.env` template, and drops a **Finance MCP Dashboard** shortcut on\n   your Desktop.\n4. Fill in `.env` (see [Authentication](#authentication)).\n5. **Restart Claude Desktop.** The server is only read at startup.\n\nThe installer prints an `ACTION NEEDED` block if `WEBULL_APP_KEY` or\n`SEC_USER_AGENT` are still at their placeholder values, so a half-configured\ninstall does not look like a finished one.\n\nNothing is installed system-wide beyond `uv`; dependencies are resolved into a\ncache the first time the server or dashboard runs, so the first launch is\nslower than the rest.\n\n### Path B — clone the repo\n\nNo installer, no shortcut. You wire up the two entry points yourself.\n\n```bash\ngit clone <repo> && cd finance-mcp\nuv venv && uv pip install -e \".[dev,dashboard]\"\ncp .env.example .env      # then edit it — see Authentication below\n```\n\n**The MCP server** (what Claude talks to). Add this to\n`%APPDATA%\\Claude\\claude_desktop_config.json` on Windows, or\n`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS, and\nrestart Claude Desktop:\n\n```json\n{\n  \"mcpServers\": {\n    \"finance\": {\n      \"command\": \"uv\",\n      \"args\": [\"run\", \"--with\", \"pandas\", \"--with\", \"numpy\", \"--with\", \"fastmcp\",\n               \"--with\", \"yfinance\", \"--with\", \"tabulate\", \"--with\", \"lxml\",\n               \"--with\", \"html5lib\", \"--with\", \"webull-openapi-python-sdk\",\n               \"/absolute/path/to/finance_mcp.py\"]\n    }\n  }\n}\n```\n\nUse an **absolute** path, and forward slashes even on Windows. Claude Code users\ncan instead run `claude mcp add finance -- uv run /absolute/path/to/finance_mcp.py`.\n\n**The dashboard** (what you look at). Run it from the **repo root**, not from\n**Module boundaries.** `dashboard/webull_client.py` is the market-data client and\nthe shared signed-request plumbing; `dashboard/broker.py` is the trading surface\n(accounts, buying power, positions, the order lifecycle). They are separate\nbecause they fail differently: a price feed degrades to a fallback and says so,\nwhile an order path must refuse rather than substitute.\n`dashboard/barcache.py` is a small on-disk cache of *validated* bar frames shared\nbetween the MCP server and the dashboard — a hit skips the download, never the\nintegrity gate. Disable it with `FINMCP_BAR_CACHE=0`.\n\n`dashboard/` — `app.py` resolves its sibling modules and `.streamlit/config.toml`\nrelative to the working directory, and launching from elsewhere loses the theme:\n\n```bash\nstreamlit run dashboard/app.py            # inside the venv\n# or, without activating anything:\nuv run --with streamlit --with plotly --with pandas --with numpy \\\n       --with yfinance --with lxml --with html5lib --with tabulate \\\n       --with webull-openapi-python-sdk streamlit run dashboard/app.py\n```\n\nIt opens on <http://localhost:8501>. Add `--server.port 8899` to move it.\n\n**The alert manager** (optional, Windows toast notifications). The dashboard\nstarts it automatically in a background thread; to run it standalone:\n\n```bash\npython -m dashboard.alert_manager\n```\n\n**Verify the install** — `pytest` runs the whole suite offline, with no\ncredentials and no network:\n\n```bash\npytest -q\n```\n\nThen ask Claude to run **`get_data_sources`** — it reports which credentials are\nconfigured, which feeds are reachable and what quota is left, without touching\nyour account. **`check_connection`** confirms the Webull session specifically.\n\n---\n\n## Authentication\n1. Open the newly generated `.env` file located in this folder.\n2. Paste your Webull `WEBULL_APP_KEY` and `WEBULL_APP_SECRET`.\n3. Save the file.\n\n`.env` and `conf/token.txt` are gitignored, and SDK logs are credential-redacted at write time — the Webull SDK dumps the full signed request (key, HMAC signature, access token) at ERROR level, which routine rate-limit responses would otherwise write straight to disk.\n\n### Optional settings\n\n| Variable | Default | Purpose |\n|---|---|---|\n| `WEBULL_ENVIRONMENT` | `prod` | `prod` is the default and trades the real account — which is the point: reads are what the tool is for, and no order leaves without your approval in the dashboard. **`paper` is not \"live data, simulated orders\"** — it repoints the *entire* client at Webull's sandbox, quotes included, and the sandbox has its own app registry, so production keys return `401` there and nothing works. Use it only with `WEBULL_PAPER_APP_KEY`/`SECRET` to rehearse the approval flow. **`paper`** (aliases `uat`, `sandbox`, `simulated`) routes every call to Webull's simulated environment for your region, so the whole approval path — draft, preview, approve, submit — can be rehearsed without risking anything. The dashboard shows `LIVE` or `PAPER` beside the wordmark and on the Execution tab. If no sandbox host is published for your region the client refuses to start rather than falling through to production. |\n| `WEBULL_PAPER_APP_KEY` / `WEBULL_PAPER_APP_SECRET` | — | Optional, paper mode only. Webull's sandbox is a separate deployment with its own app registry, so a **production** key authenticates there as `401 UNAUTHORIZED` — verified live. Register a sandbox app and set these; paper falls back to the production pair when they are unset, which will 401. |\n| `WEBULL_ACCOUNT_ID` | *(unset)* | Pin a specific account. **Required if your login has more than one** — the server refuses to guess rather than silently trading the wrong account. |\n| `WEBULL_MIN_REQUEST_INTERVAL` | `0.25` | Seconds between Webull API calls. Pacing keeps list-sweeping tools (sector heatmap, watchlist scans) off the rate limiter. |\n| `WEBULL_MAX_RETRIES` | `3` | Attempts before a rate-limited call gives up and falls back. |\n| `WEBULL_RETRY_BACKOFF` | `0.75` | Base seconds for exponential backoff on HTTP 429. |\n| `WEBULL_REGION_ID` | `th` | Webull region. Also gates the Yahoo `.BK` ticker fallback. |\n| `SEC_USER_AGENT` | *(unset)* | **Required for the EDGAR tools.** The SEC's fair-access policy demands a descriptive User-Agent with a real contact address, e.g. `Your Name (you@example.com)`. Requests are refused locally without one rather than sent anonymously, which risks an IP ban. |\n| `BLS_API_KEY` | *(unset)* | Optional. BLS works with no key at 25 queries/day; a [free key](https://data.bls.gov/registrationEngine/) raises it to 500/day and unlocks longer history. |\n\n---\n\n## Using it\n\n### From the assistant\nRestart your MCP client so it picks up the server, then ask for what you want:\n*\"How does NVDA look on the daily?\"*, *\"What's due on the economic calendar this\nweek?\"*, *\"Draft a limit buy for 10 AAPL at 300.\"* The last one writes a draft to\nthe queue and stops there.\n\n### In the dashboard\nDouble-click the **Finance MCP Dashboard** shortcut, or run\n`streamlit run dashboard/app.py` from the repo root. Nine tabs:\n\n| Tab | What it is for |\n|---|---|\n| **Charts** | Candles with overlays, a volume pane and the forecast cone. Drag to pan, scroll to zoom, double-click to reset; drag a single axis to scale it alone. Weekends and market holidays are collapsed, so there are no blank stretches. |\n| **Backtest** | Runs the adaptive consensus rules over the loaded window and reports CAGR, Sharpe, max drawdown, profit factor and exposure. |\n| **Journal** | Theses Claude logged via `log_journal_entry`, with a drift warning when the logged price has moved away from the market. |\n| **Signals** | The four indicator verdicts behind the consensus score, and the regime weighting matrix that produced them. |\n| **Execution** | The approval desk. See below. |\n| **Portfolio** | Live balance, buying power and open positions with P&L, straight from the broker, plus a value-over-time chart. Position marks are labelled in their own currency — a USD holding inside a THB account is never summed with the account base. |\n| **Events** | The economic calendar (BLS, FOMC, BEA) with each row's actual or prior print, SEC filings for your watchlist, and a \"what changed since\" diff over filings and price moves. |\n| **Alerts** | Price, RSI and MACD-cross alerts; the manager fires Windows notifications and stamps which bar triggered. |\n| **Data** | Every computed indicator column for the loaded window, newest first. |\n\n![Events tab](https://raw.githubusercontent.com/Blahaj-gif/Finance-mcp/master/docs/img/events.png)\n\n*The Events tab: the economic calendar with each row's actual or prior print,\nearnings dates flagged as confirmed, disputed or estimated, and watchlist\nfilings with a hover preview.*\n\n![Execution tab](https://raw.githubusercontent.com/Blahaj-gif/Finance-mcp/master/docs/img/execution.png)\n\n*The Execution tab — the only path to the market. Preview with the broker, then\napprove.*\n\n**DISPLAY** in the top right switches the visual theme (**Terminal**, the\ndefault; **Research**; **Slate**), the chart overlay palette and row density.\n\n**The Execution tab is the only place an order can be submitted.** Submission is\ntwo-step by design: **1 — Preview with Webull** asks the broker to price the\norder (non-binding), and only then does **2 — Approve and submit**\nunlock. An order the broker will not preview is never sent, and a failed\nsubmission leaves the draft pending rather than marking it executed.\n\n### 3. What Claude can and cannot do\n\n| | |\n|---|---|\n| Read prices, filings, macro series, your balance and your positions | **yes** |\n| Draft an order to a local JSON file, and preview it with the broker | **yes** |\n| Submit an order | **no** — the submit button exists only in the dashboard, and only after a broker preview |\n\nThere is no configuration flag that grants Claude submission rights. Removing\nthe human from that step would require editing the source.\n\n---\n*Disclaimer: This is an open-source project for educational and experimental quantitative research. Algorithmic trading carries significant financial risk.*\n\n<!-- Ownership proof for the MCP registry: it checks that whoever lists this\n     server also controls the PyPI package, by looking for this name in the\n     published README. -->\nmcp-name: io.github.Blahaj-gif/hitl-finance-mcp\n\n",
  "bytes": 32814,
  "sha": "70d0c3f5f48c55f6ae4d31b2d121d565cac4659789f1d326a459d1ef49039214",
  "repo_slug": "blahaj-gif/finance-mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_blahaj_gif_hitl_finance_mcp_8f2e200b/readme"
}