Crocodile

Crocodile

Enables querying and replaying deterministic market data for crypto and US equities from a local Parquet lake via natural language.

Category
Visit Server

README

<p align="center"><img src="assets/logo.svg" width="84" alt="Crocodile"></p>

<h1 align="center">Crocodile</h1>

<p align="center"> <b>A deterministic market-data engine for crypto <i>and</i> US equities.</b><br> Pull order books, trades, funding, option chains, filings and on-chain DEX events from 109 crypto venues and nine equity sources into one Parquet lake you own —<br>then replay any window and get the same bytes back. </p>

<p align="center"> <a href="LICENSE"><img alt="Apache-2.0" src="https://img.shields.io/badge/license-Apache--2.0-2ea043"></a> <img alt="Python 3.12+" src="https://img.shields.io/badge/python-3.12%2B-3572A5"> <img alt="3,941 tests" src="https://img.shields.io/badge/tests-3%2C941-2ea043"> <img alt="62 CLI commands" src="https://img.shields.io/badge/CLI-62%20commands-a3620a"> <img alt="API keys mostly optional" src="https://img.shields.io/badge/API%20keys-mostly%20optional-f0a92e"> </p>

A crocodile session: the lake catalog, a DuckDB query over it, then the same window replayed twice with identical SHA-256 digests

Every market-data tool can fetch a price. Three things make this one worth the disk space.

One schema for everything. A hand-written Deribit connector, a ccxt Kraken venue, a Uniswap V3 pool on Base and an SEC EDGAR Form 4 all emit the same record types — 30 of them, defined once. "Show me every trade across all my sources" is one SQL statement, not four codebases.

One capability, both markets. Crocodile is the merge of two engines — Crypcodile for crypto, Stockodile for equities — and both histories are in this repository's git log. What came out is a single registry of 49 capabilities, 42 of which answer for both asset classes under one name and one parameter schema. The seven that do not each carry a written argument for why no equity analogue can exist. The CLI, the REST API and the MCP server are projections of that registry, so none of them can drift from it.

The lake is yours, and it says where every number came from. What lands on disk is normalized, validated and replayable. Every record also carries how it came to exist: a provenance level, the method it rests on, and a confidence computed by a registered formula rather than chosen by hand. A modelled answer says so before you read a row.

How it compares

The market-data business is mostly rented. You pay monthly, you query an API, and when you stop paying you are contractually required to delete what you pulled. Massive's Market Data Terms §8 put it plainly:

you agree to cease all use of the Market Data and delete all Market Data in your possession.

Crocodile writes Parquet into a directory you chose. There is nothing to stop paying for.

Capability matrix comparing eleven market-data tools on crypto, US equities, a local store you own, keeping the data after you stop paying, SQL over the store, and deterministic replay

Three honest notes, because a comparison nobody can check is worth nothing:

  • Crocodile is not a licensed archive. Kaiko and Databento sell SLA-backed, audit-grade history under venue agreements. Crocodile ingests what public endpoints return. If you have a compliance requirement, these are not interchangeable.
  • Deterministic replay is not unique. Tardis.dev ships four documented replay endpoints, and NautilusTrader has nanosecond L3-to-bar event replay. Both are good. What is unusual here is the combination — both asset classes, an arbitrary window, over a store you own, for nothing.
  • Neither is MCP. CoinAPI, Massive and CryptoQuant all ship MCP servers. What is unusual is that Crocodile's MCP tools, REST routes and CLI commands are generated from one declaration, so an agent, a curl and a shell cannot disagree about what a capability does.

<sub>Verified 2026-07-29 against each vendor's own pricing, docs and terms pages. Kaiko has acquired Amberdata; counted once. Polygon.io was renamed Massive.com on 2025-10-30. NautilusTrader reaches US equities through a paid Databento subscription (from $199/mo) or an Interactive Brokers account.</sub>

Install

Not on PyPI. That name belongs to an unrelated project, so install from the repository:

uv pip install "crocodile[full] @ git+https://github.com/nazmiefearmutcu/Crocodile.git"

Drop [full] and you still get the whole streaming core: every native connector, the Parquet lake, replay, and the 62-command CLI. Heavier surfaces are opt-in, so you never pay for a dependency tree you don't use:

Extra Adds
[market] 100+ exchanges through the universal ccxt connector
[ml] funding prediction, Black-Scholes, 3D vol surfaces (xgboost, scipy, matplotlib)
[web] FastAPI server + Streamlit examples
[onchain] Base L2 / GMX / Superchain connectors (web3)
[full] everything above

There is no desktop extra. Nothing in this repository needs a display server.

Five minutes

# stream Deribit BTC-perp trades + book deltas into a local lake
crocodile collect --asset-class crypto --sources deribit --symbols BTC-PERPETUAL \
    --channels trade --channels book_delta --data-dir data

# the lake is just partitioned Parquet — one DuckDB view per channel on disk
crocodile query --asset-class crypto "SELECT count(*) AS n FROM trade"

# replay that window later — same bytes, every run
crocodile replay --asset-class crypto --channels trade --symbols BTC-PERPETUAL \
    --start-ns 0 --end-ns 9223372036854775807

Three things in there are load-bearing and easy to get wrong:

  • --sources, not --exchange/--provider. The merge collapsed the two forks' partitions into one source=, and the flag went with it. Lakes written under the old exchange= prefix want crocodile migrate-lake first.
  • --asset-class when the symbol does not name its market. A bare BTC-PERPETUAL could belong to either fork. Guessing is how a request lands in the wrong market's implementation and comes back with plausible numbers, so it refuses instead.
  • There is no records table. The lake registers one view per channel= directory it finds — trade, book_snapshot, ohlcv — so the channel is the view name rather than a column. crocodile catalog-channels lists what a given lake actually has.

replay and query read the lake you point them at and nothing else. There is no bundled sample and no offline fallback, so both answer empty on a fresh clone until you have collected something. There is also an interactive shell — crocodile shell — with history and tab-completion, and every command runs inside it.

Reaching the whole market

Architecture: 109 crypto venues and nine equity sources normalize into 30 record types, land in a partitioned Parquet lake, and surface as CLI, REST and MCP

On the crypto side Crocodile speaks to 109 venues: ten native connectors, hand-written for fidelity, plus the entire ccxt family behind one universal connector. Six names exist in both — binance, bybit, coinbase, deribit, derive, okx — and the native connector wins, so the total is a union rather than a sum: 10 ∪ 105 = 109.

That 105 is not a fixed number. The dependency is ccxt>=4.5, and 105 is what the version resolved here ships. crocodile markets --asset-class crypto prints the count your install actually has.

Venues
Native Binance · Bybit · Coinbase · Deribit · OKX · Base on-chain (Uniswap V3, Aerodrome) · GMX/Synthetix · Derive · Superchain · CoinGecko
Universal every ccxt exchange id — Kraken, KuCoin, MEXC, Gate, HTX, Bitget, …

On the equity side there are nine sources, and what each may be asked for is derived from the source itself rather than from a shared menu — so a channel is offered for the providers that serve it and nowhere else:

Source Writes Key
alpaca trade quote ohlcv yes
yahoo options_chain ohlcv corp_action insider no
sec_edgar insider holding_13f filing fundamental no (contact string)
treasury macro_series no
tiingo ohlcv corp_action yes (free tier)
stooq ohlcv index_value no
msn_money ohlcv corp_action no
google_finance trade index_value fundamental no
finnhub trade yes

openfigi is deliberately not a source: it enriches an instrument universe and writes no channel of its own.

You don't have to name symbols. Name a slice of the market and Crocodile resolves the concrete list from the live universe:

# the 200 most-liquid spot pairs on Binance, streamed over a single WebSocket
crocodile collect-market --asset-class crypto --sources binance --top 200 \
    --kinds spot --use-ws --channels trade --channels book_ticker

# every USDT perpetual across three venues at once, order books included
crocodile collect-market --asset-class crypto --sources bybit,okx,mexc \
    --all-symbols --quote USDT --kinds perpetual --channels book_snapshot \
    --max-symbols 400

# the whole coin universe — 17k+ coins, including the long tail no CEX lists
crocodile collect --asset-class crypto --sources coingecko --symbols _ \
    --channels ohlcv

Two design choices make that scale honestly. The ccxt path is REST-poll-first, which works on every venue, but upgrades to a single multi-symbol WebSocket per channel where the exchange supports it (watchTradesForSymbols / watchTickers) — the difference between one socket for three symbols and one socket for a whole exchange's book. And universe ranks any venue's markets by live 24h volume, so --top N covers the liquid core instead of ten thousand dead pairs.

The whole market, on one screen

crocodile census measures the market live and prints one JSON document — connector reach, venue market counts, the coin universe, market cap, dominance, and total value locked. Every figure comes from a keyless public feed. A run over the twelve major venues, 2026-07-28:

109 reachable venues · 34,208 markets across the majors · 17,863 active coins · $2.28T market cap · $75.4B DeFi TVL

crocodile census --asset-class crypto \
    --venues binance,bybit,okx,coinbase,kraken,kucoin,mexc,gate,htx,bitget,bingx,cryptocom

Name the venues explicitly. A bare crocodile census reports zero of them today — an empty tuple default reaches the parameter as a literal string — and zero venues is indistinguishable from a census that ran and found nothing. The same bug makes --kinds mandatory on collect-market.

One declaration, three projections

One capability declared once, projected into a CLI command, a REST route and an MCP tool

Three numbers describe the same CLI, and which one you want depends on the question. 62 is what --help lists. 54 folds away eight retired spellings kept resolving for callers already wired to them. 49 is the capability registry — every command below except the five launchers is a projection of one declaration, which is also what the REST route and the MCP tool are projections of.

Cluster Commands
Lake collect · collect-market · backfill · replay · query · export · resample
Discovery census · markets · universe · search · resolve-symbols · data-coverage · list-exchanges · catalog · catalog-channels · catalog-dates · catalog-exchanges · catalog-inventory · catalog-scan · catalog-stats · catalog-summary · catalog-symbols
Options & funding iv-surface · term-structure · vol-skew · risk-reversal · funding-apr · funding-predict · basis · perp-basis · spot-future-basis · open-interest
Microstructure ofi · slippage · whale-alerts · liquidity-depth · depth · indicators
On-chain / L2 risk sequencer-latency · peg-deviation · chaos-score · lending-stress · gas-vol · smart-money · label-transfers · mev-sandwich · onchain-price · base-market-data
Servers mcp · api
Operator shell · update · migrate-lake
Retired spellings inventory_snapshot · list_data_channels · list_dates · list_exchanges_on_disk · list_symbols · query_market_data · search_symbols · simulate-price-impact

The last row is a ledger that only shrinks. Each entry was on the wire under one of the two forks and resolves to a single declaration, so there is never a second implementation behind a second name.

Ingest survives disconnects with sequence-gap bridging and a dead-letter queue (src/crocodile/core/ingest/); whatever reaches disk is normalized against the 30-record schema and replayable.

Where every number came from

Provenance: a record's level, the method behind it, and a confidence computed by a registered formula

Four fields ride along on every record. prov says what class of thing the value is — native when the venue reported it, derived when it was computed from native records, synthetic when it was modelled from another data class, unavailable when it is a typed hole rather than a zero. prov_basis names the registered method. prov_inputs lists what that method consumed. prov_confidence is filled by the basis's own pure, documented formula — there is no supported way to type a confidence in by hand.

On a join, worst_provenance() wins, so a modelled input cannot launder itself into a native-looking answer.

Analytics

The lake feeds an options + microstructure library, reachable three ways — CLI, MCP tool, or plain Python over a Catalog. Nine runnable scripts in examples/:

from crocodile.crypto.analytics.funding import funding_apr
from crocodile.crypto.analytics.volsurface import iv_surface
from crocodile.core.store.catalog import Catalog

catalog = Catalog(data_dir="data")
apr     = funding_apr(catalog, "BTCUSDT", from_ns, to_ns)  # Polars DataFrame
surface = iv_surface(catalog, "BTC", at_ns, rate=0.0)      # strike × expiry × IV

Symbols here are the venue's own spelling, as stored. The canonical source:RAW form the CLI reads to work out which market you mean is not what the lake's symbol column holds, so passing binance:BTCUSDT to a reader matches nothing and returns an empty frame rather than raising.

The full set spans OFI, slippage, whale alerts, term structure, vol skew, risk reversal, spot–perp / spot–future basis, open interest, and an L2/DeFi-risk family (sequencer latency, peg deviation, lending stress, MEV-sandwich detection). Each reads the same normalized records — native venue or ccxt, it can't tell the difference.

For agents (MCP)

crocodile mcp --data-dir data     # Model Context Protocol server over stdio

Every tool is read-only and deterministic — answers come from the lake and the chain, never the model's imagination. Tools cover market-data reads (bounded DuckDB SQL, on-chain prices), catalog discovery, and the analytics library. Because the tool list is generated from the same registry as the CLI, an agent cannot call a capability the shell doesn't have, and each input schema is derived from the capability's parameter type rather than written out a second time. Works with Claude, Cursor, or anything that speaks MCP.

REST API

crocodile api serves the same lake over FastAPI at /api/v1/* — ops and catalog discovery, a bounded read-only POST /query, the derivatives and microstructure analytics, and a payment-gated demo route over the x402 protocol.

Base L2

BaseOnchainConnector reads Uniswap V3 and Aerodrome swap/reserve events from Base RPC logs and emits the same record types as the CEX connectors, so a cross-venue query is one SQL statement instead of two codebases. Start with docs/base_quickstart.md; there is a Streamlit dashboard and a Farcaster frame server under examples/. Public data needs no keys; on-chain reads use a default Base RPC you can override.

Performance

Benchmark: 3.54M records/s normalize, 17M rows/s resample, 458k records/s replay, 20.2x compression, 0.36 ms count over 500k rows

A network-free, synthetic-data benchmark of the offline path (normalize → store → query → resample → replay), fixed seed, every number written by the run itself: benchmarks/RESULTS.md. Reproduce with uv run python benchmarks/bench.py.

There is no GUI in this repository, and therefore no frame rate to quote.

Tests

uv sync --all-extras
pytest tests/

2,682 test functions across 240 files, 3,941 cases once parametrisation expands them. Under that headline: a local mock-RPC server for degraded-network E2E runs (tests/e2e/), adversarial payload suites, a regression file seeded by real exchange API anomalies, and tests/conformance/, which asserts the arguments this codebase runs on rather than its behaviour. CI-friendly — Matplotlib runs headless and BLAS thread caps keep imports fast on Apple Silicon.

One of them is worth naming. tests/replay/test_byte_identity.py replays the same window in two subprocesses under two different PYTHONHASHSEED values and compares raw stdout, because the claim at the top of this page is about bytes and a same-process repeat would not notice a silent dependency on set iteration order. It is the gate under the hero image.

Neither mypy --strict nor Ruff gates the whole of src/, and saying otherwise was the more expensive kind of wrong — it described a gate nobody would re-derive. What actually holds:

  • Zero Ruff findings in core, contrib, capabilities, surfaces and the two deprecation shims — every line the merge wrote.
  • A ratchet, currently 202, over crypto and equity, the two inherited trees. It only ever falls; tests/conformance/test_phase1_exit.py fails if it rises, and a third test fails if any file lands under neither gate. Deleting the desktop layer took it from 222 to 202 on its own — a gate that tightens when you remove code needs nobody to remember to tighten it.
  • mypy --strict clean across 229 files, with crocodile.crypto.* and crocodile.equity.* exempted in pyproject.toml until they are migrated.

There is also a conformance gate that fails when a sentence in the source states a number the code no longer holds. It is the reason the figures on this page can be trusted: they are not maintained by hand.

What it isn't

  • Not a trading bot. There is no order-execution path, by design.
  • Not a hosted service. Everything runs on your machine, against your lake.
  • Not a licensed data archive. It ingests what public endpoints return.
  • Not a chart. Crocodile has no GUI. FlowMap is a separate dual-market order-flow visualizer that reads a lake like this one.
  • Not magic. Options analytics need options data — point iv-surface at a lake with Deribit snapshots in it, not an empty directory.

Contributing

PRs welcome. Skim CHANGELOG.md for direction, keep the mypy --strict and Ruff gates described above green — new code goes in the zero-findings half, and the ratchet over the inherited half never rises — and make sure the E2E and adversarial suites pass before opening one.

Apache-2.0 — see LICENSE.

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
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
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
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