Skip to main content
Glama

market-mcp

An MCP server that gives an AI assistant real market data, technical analysis, prediction-market odds and an honest strategy backtester — plus a scheduled scanner that feeds a live dashboard and Telegram alerts.

Covers four things in one server:

Domain

Source

No key needed

Crypto

Binance public spot API

✅

Global equities / ETFs / indices / FX

Yahoo Finance

✅

Indonesian equities (IDX)

Yahoo Finance .JK + bundled universe

✅

Prediction markets

Polymarket Gamma + CLOB

✅

No API keys, no accounts, no paid tiers. Runtime dependencies are mcp and httpx — indicators and the backtester are pure Python, so there is no pandas/numpy wheel to fight with.


Install

git clone https://github.com/kumaraprayoga68-sketch/market-mcp
cd market-mcp
python -m venv .venv
.venv/Scripts/activate        # Linux/macOS: source .venv/bin/activate
pip install -e .

Then point your MCP client at it. For Claude Desktop, add this to claude_desktop_config.json (use the absolute path to the venv's Python):

{
  "mcpServers": {
    "market": {
      "command": "C:\\path\\to\\market-mcp\\.venv\\Scripts\\python.exe",
      "args": ["-m", "market_mcp.server"]
    }
  }
}

For an HTTP transport instead of stdio:

market-mcp --transport streamable-http --port 8000

Related MCP server: pmxt-mcp

The 20 tools

Prices

  • get_price — price, daily change, day range, 52-week position

  • get_prices — many symbols at once; unresolvable ones are reported, not fatal

  • search_symbol — company name → ticker

  • market_snapshot — US/Asia indices, VIX, crypto, gold, oil, USD/IDR

Analysis

  • technical_analysis — RSI, MACD, Bollinger, EMA 20/50/200, ATR, ADX, Supertrend, Stochastic + a composite rating

  • multi_timeframe_analysis — weekly → hourly, and whether they agree

  • candlestick_patterns — 13 patterns, each tagged with the trend it appeared in

Screeners

  • crypto_screener — Binance spot by volume and 24h change

  • crypto_top_movers — gainers/losers with a liquidity floor

  • stock_screener — scan the bundled IDX (277) or US (117) universe

  • technical_scan — scan for a setup (oversold, uptrend, squeeze, volume spike…), not just a price move

  • list_universes

Prediction markets

  • prediction_markets — most active markets

  • prediction_search — keyword search, question matches outrank description matches

  • prediction_market_detail — outcomes, prices, token ids

  • prediction_price_history — how the market's probability has moved

Backtesting

  • list_strategies — 9 strategies and their parameters

  • backtest_strategy — one strategy, one instrument, with costs

  • compare_strategies — rank all 9 against buy-and-hold

  • walk_forward_backtest — out-of-sample validation with an overfitting verdict


Scheduled scans, alerts and dashboard

Beyond the MCP server, the repo runs itself on a schedule:

GitHub Actions (every 4h)
  └─ scripts/run_scan.py
       ├─ snapshots/latest.json  ──►  Vercel dashboard (reads it straight from GitHub)
       └─ Telegram alert          ──►  only for setups that are new since the last run

snapshots/latest.json is committed back to the repo, so the dashboard picks up fresh data without a redeploy: the cron owns the data, Vercel only renders it.

Telegram alerts

Alerts fire only on findings absent from the previous snapshot. A scan that runs every four hours will keep matching the same oversold ticker for days, and an alert that fires every run is an alert you stop reading.

Set these in the repo under Settings → Secrets and variables → Actions:

Secret

Where to get it

TELEGRAM_BOT_TOKEN

@BotFather → your bot → API token

TELEGRAM_CHAT_ID

message your bot, then open https://api.telegram.org/bot<TOKEN>/getUpdates and read result[].message.chat.id

Without them the job still runs and simply skips the alert. Never commit the token — it is a bearer credential for the whole bot.

Running the scan by hand

python scripts/run_scan.py --quick --no-alert

--quick shrinks the universes and skips backtests; --no-alert never sends Telegram. A full run takes a few minutes and produces roughly 80 KB of JSON.

Every section is independently guarded, so an outage at one venue degrades the snapshot rather than losing the run. Failures land in snapshot.errors and the dashboard shows them rather than pretending the data is complete.

Dashboard

Live: market-mcp-dashboard.vercel.app

dashboard/ is a Next.js app with no UI dependencies beyond React.

cd dashboard && npm install && npm run dev

It fetches snapshots/latest.json over HTTP with a 5-minute revalidate, so new scan data appears on its own. Only code changes need a redeploy:

cd dashboard && vercel deploy --prod

To get automatic deploys on every code push, connect the repository under Project Settings → Git and set Root Directory to dashboard. That last part matters and cannot be set from the CLI: without it Vercel builds from the repository root, finds no package.json, and the build fails.

Point the dashboard at a different data source with the SNAPSHOT_URL environment variable.


Two things this server is careful about

The backtester cannot see the future

A strategy sees bar i only after it closes, so the position it asks for is held during bar i+1. This is enforced by the engine, not by convention, and it is asserted in the test suite: an "oracle" strategy that reads the next bar returns ~5600% on the same data where the identical rule, lagged one bar, returns ~19%. If look-ahead ever leaked in, both numbers would be huge.

Costs are charged on position changes, so a long→short flip pays twice. Every result reports buy_and_hold_return_pct beside it, because beating a flat market is not an edge.

walk_forward_backtest is the tool that matters. It re-optimises parameters on data up to each fold and scores the fold that follows, then compares in-sample against out-of-sample and returns a verdict — robust, acceptable, fragile, likely_overfitted or overfitted. Most strategies that look great in a single backtest come back overfitted, which is the point.

The rating explains itself, and knows when oscillators lie

technical_analysis returns a composite score, but also every individual vote that produced it, so the reasoning can be inspected rather than trusted.

RSI pins near 100 for the whole of a real rally. A naive vote counter reads that as bearish, lets it cancel the trend indicators, and rates an unmistakable uptrend "neutral". When ADX shows a strong trend, this server drops oscillator votes that oppose the trend and says so in the vote's reason. ADX itself never votes on direction — it only scales confidence.


Output shape

Every tool returns the same envelope:

{ "ok": true, "data": { ... }, "error": null }
{ "ok": false, "data": null,
  "error": { "code": "rate_limited", "message": "...", "retryable": true } }

retryable distinguishes "the upstream is having a moment" from "you asked for something that does not exist", so a client knows whether trying again is worthwhile. Codes: bad_input, not_found, upstream_error, rate_limited, timeout, internal.


Layout

src/market_mcp/
├── server.py         MCP entry point
├── core/             error envelope, TTL cache, shared HTTP with retry
├── providers/        yahoo, binance, idx, polymarket — plain async functions
├── indicators.py     pure-Python TA
├── patterns.py       candlestick detection
├── analysis.py       indicator snapshot + composite rating
├── scanning.py       signal matching, shared by the MCP tool and the cron job
├── alerting.py       snapshot diffing and Telegram formatting
├── market_data.py    one candle loader across all venues
├── backtest/         engine, strategies, walk-forward
└── tools/            MCP tool definitions
scripts/run_scan.py   the scheduled job
dashboard/            Next.js dashboard for Vercel
snapshots/            latest.json + trimmed history, written by the cron
data/                 idx.txt (277), us.txt (117)
tests/                130 tests, no network required

Providers know nothing about MCP; tools know nothing about HTTP. Adding a venue means writing one provider module that returns the shared candle shape.


Tests

pytest

130 tests, all offline — they cover indicator known-answer cases, engine invariants (look-ahead, cost accounting, drawdown), symbol normalisation, the error envelope, cache de-duplication, alert de-duplication, and server wiring. CI runs them on Python 3.10, 3.12 and 3.14, plus a smoke job that starts the server and checks it serves all 20 tools.


Adding a strategy

Write a function that maps candles to positions (+1 long, 0 flat, -1 short), then register it:

def my_strategy(candles, period: int = 14) -> list[int]:
    r = ta.rsi(series(candles, "close"), period)
    return [0 if v is None else (1 if v < 25 else 0) for v in r]

Strategy(
    name="my_strategy",
    description="...",
    fn=my_strategy,
    defaults={"period": 14},
    grid={"period": [7, 14, 21]},   # searched by walk-forward
)

Warm-up bars must be 0 — a strategy is never allowed to act on an undefined indicator. test_every_strategy_produces_valid_signals enforces the contract for anything in the registry.


Limits

  • Binance geo-blocks US cloud regions. api.binance.com answers HTTP 451 from GitHub Actions runners and Vercel functions alike (verified from iad1). The provider fails over to data-api.binance.vision, which serves the same market-data routes from those regions and is remembered for the rest of the process. If you host this somewhere new and crypto sections come back empty, check for 451 before suspecting anything else.

  • Yahoo caps intraday history (7 days of 1-minute bars, 60 days of sub-hourly).

  • Polymarket has no server-side text search, so prediction_search scans the most active markets rather than every market ever created.

  • IDX has no free bulk screener, so stock_screener quotes a curated universe file rather than the whole exchange.

  • Backtests model costs as flat fee + slippage. Real fills, funding, borrow and liquidity are not modelled.

Not affiliated with TradingView, Binance, Yahoo or Polymarket. Information only — not financial advice.

Available Tools

20 tools
backtest_strategyA

Backtest one strategy on one instrument.

Args: symbol: Ticker (BTC, AAPL, BBCA). strategy: Strategy name from list_strategies. market: "crypto", "stock" or "idx". interval: Candle size (15m, 30m, 1h, 4h crypto-only, 1d, 1w). bars: Candles to test over (30-1500). params: Strategy parameter overrides, e.g. {"period": 21}. fee_bps: Round-trip fee in basis points per position change. slippage_bps: Assumed slippage in basis points per position change. include_trades: Return the full trade log as well as the metrics.

Signals are acted on at the next bar, so results contain no look-ahead. Always compare total_return_pct against buy_and_hold_return_pct — beating a flat market is not an edge.

Inputs: symbol (required), strategy (required), bars, market, +5 more. Returns: object

candlestick_patternsA

Detect candlestick patterns in the most recent bars.

Args: symbol: Ticker. market: "crypto", "stock" or "idx". interval: Candle size. lookback: How many recent bars to scan (1-20).

Each hit reports the trend it appeared in and whether that makes it a genuine reversal — a hammer inside an uptrend is not a bottom signal.

Inputs: symbol (required), market, interval, lookback. Returns: object

compare_strategiesA

Run every strategy on one instrument and rank them.

Args: symbol: Ticker. market: "crypto", "stock" or "idx". interval: Candle size. bars: Candles to test over. fee_bps: Fee in basis points per position change. slippage_bps: Slippage in basis points per position change. rank_by: "sharpe", "total_return_pct", "calmar" or "profit_factor".

A leaderboard on one symbol over one window is a weak signal — the winner is partly luck. Confirm anything promising with walk_forward_backtest before trusting it.

Inputs: symbol (required), bars, market, fee_bps, +3 more. Returns: object

crypto_screenerA

Screen Binance spot pairs on 24h price and volume.

Args: quote_asset: Quote leg to screen against (USDT, USDC, BTC...). min_volume_usd: Minimum 24h turnover. Raising this is the main lever for filtering out pairs too illiquid to trade. min_change_pct: Keep only pairs up at least this much over 24h. max_change_pct: Keep only pairs below this 24h change. sort_by: "volume", "change" or "change_asc". limit: Rows to return.

Inputs: limit, sort_by, quote_asset, max_change_pct, +2 more. Returns: object

crypto_top_moversA

Get the biggest 24h movers on Binance spot.

Args: quote_asset: Quote leg (USDT, USDC...). direction: "gainers", "losers" or "both". min_volume_usd: Liquidity floor — without one the list fills with thin pairs whose percentage moves are noise. limit: Rows per direction.

Inputs: limit, direction, quote_asset, min_volume_usd. Returns: object

get_priceA

Get the current price and daily change for one instrument.

Args: symbol: Ticker. Crypto accepts BTC, BTCUSDT or BTC/USDT; equities use the Yahoo symbol (AAPL, MSFT); IDX accepts BBCA or BBCA.JK. market: One of "crypto", "stock" (global equities/ETFs/indices/FX) or "idx" (Indonesia Stock Exchange).

Returns price, previous close, change %, day range and — for equities — the 52-week range and where price sits inside it.

Inputs: symbol (required), market. Returns: object

get_pricesA

Get current prices for several instruments at once.

Args: symbols: Up to 50 tickers. market: "crypto", "stock" or "idx" — applies to every symbol.

Symbols that cannot be resolved are reported in failed rather than failing the whole call.

Inputs: symbols (required), market. Returns: object

list_strategiesA

List the available backtest strategies with their parameters.

Call this before backtest_strategy if you are unsure of a strategy name or which parameters it accepts.

Inputs: no named parameters. Returns: object

list_universesA

List the bundled stock ticker universes and their sizes.

Inputs: no named parameters. Returns: object

market_snapshotA

Get a cross-market overview: US and Asian indices, VIX, crypto, FX and commodities.

Includes the IDX Composite (^JKSE) and USD/IDR. Use this to answer "how are markets doing" without picking symbols by hand.

Inputs: no named parameters. Returns: object

multi_timeframe_analysisA

Check whether timeframes agree on direction.

Args: symbol: Ticker. market: "crypto", "stock" or "idx". bars: Candles per timeframe.

Runs the composite rating on weekly through hourly candles. Aligned timeframes are the setups worth acting on; conflict between them is the usual reason a signal on one chart fails.

Inputs: symbol (required), bars, market. Returns: object

prediction_market_detailA

Get full detail for one prediction market, including outcome token ids.

Args: slug_or_id: Market slug (from a polymarket.com URL) or numeric id.

The token_id on each outcome is what prediction_price_history needs.

Inputs: slug_or_id (required). Returns: object

prediction_marketsA

List the most active Polymarket prediction markets.

Args: limit: Markets to return (max 100). order: "volume24hr", "volume", "liquidity" or "endDate". tag: Optional category slug, e.g. "politics", "crypto", "sports".

Prices are probabilities: 0.63 means the market prices that outcome at 63%.

Inputs: tag, limit, order. Returns: object

prediction_price_historyA

Get the probability history for one market outcome.

Args: token_id: Outcome token id from prediction_market_detail. interval: Window to cover — "1h", "6h", "1d", "1w", "1m" or "max". fidelity: Bucket size in minutes. Larger values return fewer points.

Use this to see whether the market's view has been drifting or is reacting to something recent.

Inputs: token_id (required), fidelity, interval. Returns: object

search_symbolA

Find the ticker for a company or asset by name.

Args: query: Company or asset name, e.g. "bank central asia" or "nvidia". limit: Maximum matches to return.

Use this first whenever the user names a company rather than a ticker.

Inputs: query (required), limit. Returns: object

stock_screenerA

Screen a stock universe on today's move.

Args: universe: "idx" (~277 Indonesian tickers) or "us" (~117 US tickers). min_change_pct: Keep only names up at least this much today. max_change_pct: Keep only names below this change today. min_price: Minimum share price, in the listing currency (IDR for IDX). max_symbols: How many tickers to quote. The universe files are ordered by liquidity, so the first N are the tradeable ones. limit: Rows to return.

Inputs: limit, universe, min_price, max_symbols, +2 more. Returns: object

technical_analysisA

Run a full technical analysis on one instrument.

Args: symbol: Ticker (BTC, AAPL, BBCA). market: "crypto", "stock" or "idx". interval: 15m, 30m, 1h, 4h (crypto only), 1d or 1w. bars: How many candles to analyse. More bars means slower indicators like the 200-EMA actually warm up.

Returns RSI, MACD, Bollinger, EMA20/50/200, ATR, ADX, Supertrend and Stochastic, plus support/resistance and a composite rating. The rating lists every individual vote, so you can explain why rather than just quoting a label.

Inputs: symbol (required), bars, market, interval. Returns: object

technical_scanA

Scan a universe for a technical setup, not just a price move.

Args: signal: One of oversold, overbought, bullish, bearish, uptrend, downtrend, volume_spike, squeeze. market: "crypto" scans the most liquid Binance pairs; "stock"/"idx" scan the bundled ticker universes. universe: For stock/idx markets: "idx" or "us". interval: Candle size for the indicators. max_symbols: Symbols to examine (capped at 60 — each one costs a full candle download). limit: Rows to return.

Slower than the price screeners because it downloads candles per symbol; prefer a tighter max_symbols when you only need ideas, not coverage.

Inputs: signal (required), limit, market, interval, +2 more. Returns: object

walk_forward_backtestA

Validate a strategy out-of-sample and check whether it is curve-fitted.

Args: symbol: Ticker. strategy: Strategy name from list_strategies. market: "crypto", "stock" or "idx". interval: Candle size. bars: Candles to use. Needs roughly 40 x (folds + 1) minimum. folds: Number of out-of-sample test windows (2-10). objective: Metric the parameter search optimises for. fee_bps: Fee in basis points per position change. slippage_bps: Slippage in basis points per position change.

Optimises parameters on data up to each fold, then scores the fold that follows on bars the optimiser never saw. The verdict compares in- and out-of-sample results: this is the tool that catches a strategy that only looks good in hindsight.

Inputs: symbol (required), strategy (required), bars, folds, +5 more. Returns: object

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A4/5.0

    Scored across 20 tools

    Disambiguation5/5

    Each tool targets a distinct purpose: price fetching, technical analysis, screening, prediction markets, and backtesting are clearly separated. Even within similar areas like screeners, descriptions clarify the difference (e.g., price-based vs. technical-setup scans). No two tools are interchangeable.

    Naming Consistency4/5

    All names use snake_case and are descriptive, with a consistent pattern within subdomains (e.g., prediction_* prefix, get_* for price retrieval, *_screener for screeners). The mix of verb-first (list_strategies) and noun-first (technical_analysis) is a minor deviation but not confusing.

    Tool Count3/5

    At 20 tools, this is on the heavy side, but the breadth of the domain (market data, technical analysis, screeners, prediction markets, backtesting) justifies many of them. Still, the count approaches the 'too many' threshold, and some redundancy like get_price vs. get_prices adds to the load.

    Completeness4/5

    The toolset covers a wide lifecycle: fetching prices, searching symbols, running analysis, screening, prediction market exploration, and backtesting with validation. The main gap is the lack of a direct historical OHLCV endpoint for raw price data, which would be useful for charting or custom analysis.

    Maintenance

    ActivityActive
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers

    • -
      license
      Not graded
      quality
      Not graded
      maintenance
      Real-time financial market data MCP server. Stocks, crypto, technicals, sentiment, FDA calendar. No API keys required.
      -
    • A
      license
      C
      quality
      D
      maintenance
      MCP server that provides a unified prediction market API for multiple venues like Polymarket and Kalshi, allowing AI agents to discover markets, fetch order books, and execute trades through a single interface.
      32
      71 npm
      8
      MIT
    • A
      license
      C
      quality
      D
      maintenance
      An MCP server providing stock market data and trading capabilities through multiple broker APIs including Robinhood and Schwab.
      100
      30 PyPI
      11
      Apache 2.0
    • A
      license
      A
      quality
      D
      maintenance
      An advanced MCP server for real-time worldwide stock market data, technical indicators, financials, news, and Python sandboxing with AI models, supporting US and Indian markets.
      29
      1
      MIT