Your agent asks the archive. The answer comes back computed.
Connect an agent on your laptop and it can work across the whole archive with nothing to provision and nothing to download. The aggregation happens next to the data, so what reaches your Python is a bar series or a statistic, not a tape to clean first. This page shows the code that takes, measured against the parquet route, including where we lost.
The laptop did not download, clean or deduplicate anything.
Every row below is a real run of the same agent on the same question, once through this MCP and once over the parquet on its own disk. Read it for what the laptop did not have to do: hold the data, write the wrangling code, wait for it. Then read the last columns, because the research rows are where we lost.
{
"dataset": "edgex/trades",
"instrument": "ETHUSDC",
"start": "2026-09-27T00:00:00Z",
"end": "2026-09-28T00:00:00Z",
"sql": "WITH r AS (SELECT ln(close / lag(close) OVER (ORDER BY ts)) AS lr FROM t) SELECT stddev_samp(lr) * sqrt(525600) AS rv FROM r"
}
Returned rv = 0.2661 from 13
shards read server-side. 14,589 tokens, 10 s,
nothing downloaded. Transcript
cd /tmp/peerfeed-a21/archive/edgex; P=/tmp/peerfeed-a21/venv/bin/python; $P -c "
import duckdb
print(duckdb.sql(\"select distinct contractName, contractId from 'trades/parquet/2026/09/27/*.parquet' where contractName ilike '%ETH%'\"))
print(duckdb.sql(\"select filename, min(time), max(time), count(*), count(distinct ticketId) from read_parquet('trades/parquet/2026/09/2[678]/*.parquet', filename=true) where contractName='ETHUSD' group by 1 order by 1\").df().to_string())
"
48 lines of shell and Python across 8 commands: the ones before this found the files, read the schema, picked the symbol and checked for duplicate trades. 73,532 tokens, 37 s, with 234.2 MB of parquet already downloaded. Same answer. Transcript
Every question, both routes
| Question | Data the parquet route downloaded first | Code the agent wrote | Wall time | Tokens | Answer | ||||
|---|---|---|---|---|---|---|---|---|---|
| MCP | Parquet | MCP | Parquet | MCP | Parquet | MCP | Parquet | ||
| BTC VWAP on Hyperliquid, the call already known One call, known |
560.6 MB | 0 lines 1 call | 27 lines 5 calls |
10 s | 32 s | 14k | 39k | correct | correct |
| BTC notional on Nado, the call already known One call, known |
8.5 MB | 0 lines 1 call | 37 lines 6 calls |
12 s | 37 s | 14k | 65k | correct | correct |
| ETH realised vol on edgeX, the call already known One call, known |
234.2 MB | 1 line 1 call | 48 lines 8 calls |
10 s | 37 s | 15k | 74k | correct | correct |
| BTC VWAP on Hyperliquid for one day One number, found |
560.6 MB | 1 line 4 calls | 27 lines 5 calls |
17 s | 32 s | 49k | 39k | correct | correct |
| BTC traded notional on Nado for one day One number, found |
8.5 MB | 5 lines 8 calls | 37 lines 6 calls |
34 s | 37 s | 193k | 65k | correct | correct |
| ETH 1-minute realised vol on edgeX for one day One number, found |
234.2 MB | 20 lines 6 calls | 48 lines 8 calls |
33 s | 37 s | 82k | 74k | correct | correct |
| Funding carry across five perp venues Research |
2.0 GB | 1 line 13 calls | 169 lines 15 calls |
53 s | 134 s | 165k | 297k | correct | correct |
| Cross-venue lead-lag and price dislocation, BTC, one day Research |
859.7 MB | 54 lines 22 calls | 113 lines 9 calls |
129 s | 88 s | 614k | 134k | partly wrong | correct |
| Seven-venue BTC volume share and realised volatility, one week Research |
2.9 GB | 78 lines 31 calls | 140 lines 14 calls |
185 s | 156 s | 963k | 328k | partly wrong | correct |
Code is counted from each agent's own stream: non-blank lines of shell and Python it sent to Bash, plus lines of SQL it sent to the MCP; the other arguments of a call are not counted. Wall time on the parquet side starts with the files already on disk; the download is not in it.
Two servers: one answers, one delivers files.
The first is for questions whose answer is smaller than their data, and is free up to a quota. The second is for when you need every row on your own machine, and is the paid product.
market-data-mcp
Answers questions. DuckDB reads the parquet shards next to the archive, computes the aggregate and returns it, so your agent has no rows to hold, store or clean.
- Returns
- Bars, SQL results over 1-minute bars across venues, funding normalised per venue, metadata, coverage
- You receive
- Only the answer
- Metered by
- Rows scanned, per user, per day
- Serves today
- Trades from the sellable DEX venues, as bars (get_ohlcv) or as free-form SQL over 1-minute bars (run_query, several venues joined in one call), and funding normalised across venues' intervals (get_funding, in the build measured here; live on the public server from its next deploy). Order books and liquidations are next; until they are, they are sold as files.
PeerFeed delivery MCP
Hands over files. Browse the catalogue, then get_slice returns a presigned
download link for one venue, data type and day. This one does put the data
on your machine: that is what it is for.
- Returns
- Catalogue, quality, presigned R2 links valid ~24 h
- You receive
- The raw parquet, by design
- Metered by
- GB against your plan
- Tools
list_venuesdescribe_venuequalityget_slice
A real research task, run end to end both ways.
The same agent, the same model and the same code sandbox ran each question twice: once through this MCP in the public mode a third party gets, once over the parquet on its own disk. Every turn is counted, the exploration and the mistakes included, and each answer is graded against a reference computed independently. The parquet agent won more of this than we did. Here is all of it, with every transcript.
The parquet side started with the data already on disk. Each of its runs presupposes the files were already downloaded — 8.5 MB to 2.9 GB per task, 4.9 GB for all of them. Obtaining that is our paid product. It is money and time rather than tokens, so it is not in any token count below, and no ratio on this page folds it in.
Three research tasks, each run twice
| Research task | Through the MCP | Agent over the parquet | ||||
|---|---|---|---|---|---|---|
| Tokens | Wall | Answer | Tokens | Wall | Answer | |
| Funding carry across five perp venues | 165,475 6 turns, 13 tool calls, $0.45 | 53 s | correct | 296,714 16 turns, 15 tool calls, $0.57 | 134 s | correct |
| Cross-venue lead-lag and price dislocation, BTC, one day | 614,489 15 turns, 22 tool calls, $0.80 | 129 s | partly wrong | 134,223 10 turns, 9 tool calls, $0.39 | 88 s | correct |
| Seven-venue BTC volume share and realised volatility, one week | 963,396 16 turns, 31 tool calls, $1.50 | 185 s | partly wrong | 328,034 15 turns, 14 tool calls, $0.65 | 156 s | correct |
Tokens are the API's own usage counts, summed over every turn: each turn re-reads its context, so the whole conversation is paid for again at each step (cache reads are billed at a tenth, which the dollar figure reflects). Model claude-opus-5-5 on both sides, same system prompt apart from where the data is, same Bash and Python.
Since the last run, both directions
| Task | Route | Tokens, previous run | Tokens, now | Now / before | Answer before | Answer now | |
|---|---|---|---|---|---|---|---|
| T1 · Funding carry across five perp venues | MCP | 126,548 | 165,475 | 1.31× | correct | correct | answer unchanged |
| T1 · Funding carry across five perp venues | Parquet | 318,603 | 296,714 | 0.93× | correct | correct | answer unchanged |
| T2 · Cross-venue lead-lag and price dislocation, BTC, one day | MCP | 308,841 | 614,489 | 1.99× | partly wrong | partly wrong | answer unchanged |
| T2 · Cross-venue lead-lag and price dislocation, BTC, one day | Parquet | 193,471 | 134,223 | 0.69× | correct | correct | answer unchanged |
| T3 · Seven-venue BTC volume share and realised volatility, one week | MCP | 707,174 | 963,396 | 1.36× | partly wrong | partly wrong | answer unchanged |
| T3 · Seven-venue BTC volume share and realised volatility, one week | Parquet | 337,590 | 328,034 | 0.97× | correct | correct | answer unchanged |
| S1 · BTC VWAP on Hyperliquid for one day | MCP | 167,019 | 49,031 | 0.29× | correct | correct | answer unchanged |
| S1 · BTC VWAP on Hyperliquid for one day | Parquet | 53,504 | 39,076 | 0.73× | correct | correct | answer unchanged |
| S2 · BTC traded notional on Nado for one day | MCP | 279,514 | 193,395 | 0.69× | correct | correct | answer unchanged |
| S2 · BTC traded notional on Nado for one day | Parquet | 62,072 | 65,441 | 1.05× | correct | correct | answer unchanged |
| S3 · ETH 1-minute realised vol on edgeX for one day | MCP | 55,384 | 81,782 | 1.48× | correct | correct | answer unchanged |
| S3 · ETH 1-minute realised vol on edgeX for one day | Parquet | 70,768 | 73,532 | 1.04× | correct | correct | answer unchanged |
| K1 · BTC VWAP on Hyperliquid, the call already known | MCP | 14,107 | 14,226 | 1.01× | correct | correct | answer unchanged |
| K2 · BTC notional on Nado, the call already known | MCP | 21,618 | 14,447 | 0.67× | correct | correct | answer unchanged |
| K3 · ETH realised vol on edgeX, the call already known | MCP | 14,359 | 14,589 | 1.02× | correct | correct | answer unchanged |
Every task both measurements ran, on both routes. A win and a regression come out of the same loop, so neither can be left off. Read the parquet rows first: its data and harness did not change, so how far they moved is the run-to-run noise of one agent, and a smaller move on the MCP side is not evidence of anything.
Predicted before the run, reported after it
The server's authors registered what this run would show before it ran, so the result could not be read charitably afterwards. Each is reported met or missed.
| Task | Registered before the run | Outcome | Evidence |
|---|---|---|---|
| T1 | T1 stays correct and cheap. | met | Correct on both routes. 165k tokens through the MCP against 297k over the parquet: still the cheaper route. Against A20's 127k it is 1.31x, inside the 1.5x noise band, so the win holds and did not measurably change. |
| T3 | T3 improves, because it is a sum and totals are addable. | missed | The sum improved exactly as predicted: all seven notionals are now right, because the agent read withheld_by_floor and added the withheld volume back (dYdX $58.6M against $58.5M; A20's agent was 4% short). The grade did not improve: it is still partly wrong, because the floor also biases realised vol, which is not a sum. Pacifica came back 43.8% against 36.4% and dYdX 88.5% against 68.0% on the same definition. The prediction's mechanism worked; the task outcome it predicted did not happen. |
| T2 | T2 stays partly wrong, with the best available outcome being an honest refusal. | met, for a reason other than the one registered | Still partly wrong, and the one cell the floor genuinely blocks (dYdX lead-lag, 34 usable pairs) was correctly declined. But the extremum the prediction was about is not a floor limit: under fill order the floor removes none of the four maxima. What is left wrong (Pacifica's value, ApeX's minute) comes from how the server picks a bar's close among fills sharing a millisecond, which is fixable, not policy. |
Where we win: funding carry, and sums
The carry task cost 165k tokens through
4
get_funding calls against 297k for the parquet agent, which had
to find five venues' funding feeds, work out each interval and carry a change-only feed
forward itself. Both were graded correct. The margin is well outside one run's noise, and
the change since the last run is inside it: the win holds.
The floor disclosure did its job on sums. The venue-share agent read
withheld_by_floor, added the withheld volume back, and got all seven venues'
notional right, where the last run's agent was 4% short on dYdX and Paradex. And finding
one number got cheaper: Hyperliquid's VWAP cost 49k tokens through the MCP,
down from 167k, because describing one instrument now answers in about
9,000 to 12,000 characters instead of 47,000.
Where we lose: tokens on research
On the lead-lag task the parquet agent spent 134k tokens and the MCP agent 614k; on venue share 328k against 963k, 2.9× the parquet agent. Lead-lag through the MCP cost 2.0× what it did last run, more than the noise, and not because responses grew: the agent made 15 model calls instead of 7, and every call re-reads the whole conversation. The cause is structural: every MCP result lands in the agent's context, while the parquet agent's DuckDB reads gigabytes and prints a summary. When the agent had to find the call itself, the MCP was the cheaper route on none of three single numbers.
Where we lose: answers
The parquet agent got all three research answers right; through the MCP, one of three. Public mode's three-trade floor still costs answers, but not only where we said it did, and in one place not at all.
Volatility is biased by the floor, and nothing says so. On venue share the agent measured 1-minute realised vol over back-to-back minutes, from bars the floor had already thinned. A minute with three or more trades is a busier, more volatile minute, so what survives is biased upward: Pacifica 43.8% against 36.4% on the same definition over every minute, dYdX 88.5% against 68.0%. Removing the floor's minutes from the parquet reproduces both of the agent's figures to the pair. A withheld total repairs a sum; it cannot repair a variance.
Our own extremum case was wrong. Last run we said Pacifica's largest dislocation sat in a 2-trade minute the floor withheld. That is true only if fills sharing a millisecond are ordered by trade id, and Hyperliquid's id is not a sequence. In fill order — the archive's row order, in which one order at 13:31 visibly walks up the book from 85,028 to 85,040 inside one millisecond — the largest dislocation is −11.05 bp at 13:31, a minute the floor keeps. The floor removes none of the four venues' maxima that day. The parquet agent chose fill order on its own and got every one. What the MCP still gets wrong here (Pacifica's value, ApeX's minute by 0.08 bp) comes from which fill the server takes as a bar's close within one millisecond: a bug, handed to the server's authors, not a policy limit.
Where the floor does block an answer, the agent now says so. dYdX traded in 409 minutes that day and the floor withheld 277, leaving 34 usable return pairs. The agent declined to name a leader and showed the withheld minutes. We grade that correctly declined: worth more than a plausible wrong answer.
For anything counted rather than weighed, 4% understates it badly. Measured by the server's authors on the deployed server, dydx/trades over two hours: BTC-USD 47.5% of trades withheld against 5.4% of volume, ETH-USD 21.1% of trades against 9.7% of volume. Thin minutes are numerous and tiny, so notional barely moves while a count-based statistic — trade frequency, average trade size, arrival rates — can be wrong by nearly half. Per instrument, for the tasks above:
| Instrument | Window | Minutes withheld | Trades withheld | Volume withheld |
|---|---|---|---|---|
hyperliquid/trades:BTC | 2026-09-21..28 | 0.0% | 0 (0.0%) | 0.00% |
edgex/trades:BTCUSDC | 2026-09-21..28 | 0.0% | 2 (0.0%) | 0.00% |
apex/trades:BTCUSDT | 2026-09-21..28 | 0.0% | 0 (0.0%) | 0.00% |
pacifica/trades:BTC | 2026-09-21..28 | 29.7% | 5,240 (6.1%) | 1.00% |
dydx/trades:BTC-USD | 2026-09-21..28 | 55.2% | 3,262 (17.6%) | 3.98% |
paradex/trades:BTC-USD-PERP | 2026-09-21..28 | 13.7% | 274 (1.9%) | 3.93% |
nado/trades:2 | 2026-09-21..28 | 4.8% | 695 (0.3%) | 0.13% |
hyperliquid/trades:BTC | 2026-09-27 | 0.0% | 0 (0.0%) | 0.00% |
edgex/trades:BTCUSDC | 2026-09-27 | 0.0% | 0 (0.0%) | 0.00% |
apex/trades:BTCUSDT | 2026-09-27 | 0.0% | 0 (0.0%) | 0.00% |
pacifica/trades:BTC | 2026-09-27 | 43.2% | 1,078 (13.0%) | 1.87% |
dydx/trades:BTC-USD | 2026-09-27 | 67.7% | 362 (20.3%) | 2.90% |
nado/trades:2 | 2026-09-27 | 9.3% | 187 (0.8%) | 0.47% |
Read from withheld_by_floor on the deployed server, public mode,
one call per instrument (the
responses). Minutes are the share of an instrument's trading minutes; volume is in that
instrument's own base unit, so no row is ever added to another.
A bar labelled one minute is often not one minute from the next, and that
dwarfs everything on this page. On a thin venue consecutive bars in a returned series are
not equally spaced: measured on dYdX BTC-USD over six hours, 6 of 7 consecutive pairs span
more than a minute, median gap 16 minutes, largest 106. Computing “1-minute”
realised volatility from that treats 16-minute returns as 1-minute returns, and volatility scales
with the square root of the interval — roughly 4× on the typical pair.
No control of ours causes it: an empty minute makes the same gap in private mode where there is no
floor at all, and the floor only makes gaps more frequent. run_query returns a
bar_spacing block — median gap, largest gap, and how many gaps exceed a minute
— so the interval can be computed instead of assumed. We quote no correction
factor, and the reason got stronger rather than weaker. Two measurements of the floor's own
effect on volatility disagreed and we published that we could not reconcile them. It has since been
reconciled, and the cause was the tree rather than the window: one side had measured on
archived shards that carried duplicate rows, and dYdX's were 71.9% duplicated.
Re-measured on deduplicated shards, the floor's effect on dYdX is 0.73× —
deflation, the opposite sign — while Hyperliquid sits at 1.16× and
Pacifica at 1.03×, both unchanged. So the floor does not bias second moments in a consistent
direction across venues; it inverts. There is no single factor to quote, which is a better reason not
to quote one than “we disagree” was. Compute the interval from
bar_spacing and the shortfall from withheld_by_floor instead.
run_query also raises a warning when a query asks for an extreme and the floor
removed anything. It fires too often to be read: on 14 of the 17 query results the venue-share
agent received, and on a plain daily sum, because any ORDER BY trips it, including the one inside
a lag(). We do not publish withheld bars' own high and low: where one bar was
withheld, that bar is the trade, and printing its price is the raw row the floor
exists to withhold. So for an extreme the honest ceiling is a warning, not a repair —
some questions cannot be answered under this floor, and that is a policy we chose rather than
a bug we have not fixed.
Where we win: one call you already know
When the agent knows the call, one number costs 14k–15k tokens and about ten seconds, against 39k–74k and 32–37 seconds for the parquet agent, and there is nothing to download. Finding the call through the MCP the first time cost more than working the parquet for every one of the three numbers, because the agent reads the dataset's quality and caveats before it trusts a figure, so the win is for questions your agent asks again, not the first time.
One SQL call against the whole route
| One number | One MCP call, known in advance | MCP, agent finds the call | Agent over the parquet | Parquet it had to hold | ||||||
|---|---|---|---|---|---|---|---|---|---|---|
| Tokens | Wall | Answer | Tokens | Wall | Answer | Tokens | Wall | Answer | ||
| BTC VWAP on Hyperliquid for one day | 14,226 | 10 s | correct | 49,031 | 17 s | correct | 39,076 | 32 s | correct | 560.6 MB |
| BTC traded notional on Nado for one day | 14,447 | 12 s | correct | 193,395 | 34 s | correct | 65,441 | 37 s | correct | 8.5 MB |
| ETH 1-minute realised vol on edgeX for one day | 14,589 | 10 s | correct | 81,782 | 33 s | correct | 73,532 | 37 s | correct | 234.2 MB |
The first column is a real agent run too: told the exact call, it made it and reported the result. The third is an agent that had to find the files, the columns, the symbol, the epoch and the dedup key before it could compute anything.
T1 · Funding carry across five perp venues
Question, word for word: Funding carry. For the BTC and ETH perpetuals on Hyperliquid, edgeX, Extended, Nado and Pacifica, over 2026-09-21 00:00 to 2026-09-28 00:00 UTC: give the mean funding rate each venue charged longs, annualised as a simple APR in percent, per venue and coin. Then name the single best delta-neutral carry trade (long the perp on one venue, short the same coin on another) and its annualised funding spread. Say how you handled each venue's funding interval.
Reference answer: APR, BTC / ETH: Hyperliquid 11.1 / 12.4, edgeX 6.7 / 10.95 time-weighted (7.1 / 10.95 sampled at its 42 settlements; rate is per 4 h), Extended 7.6 / 11.3, Pacifica 10.0 / 10.95. Nado, on the serving host's copy, to one significant figure: BTC 2, ETH 8 (a 24 h rate carried forward over a change-only feed). Best carry: long BTC on Nado, short BTC on Hyperliquid, about +10% APR. Unchanged from A20: funding is not touched by the trade floor.
Parquet the agent over the parquet had on disk for this: 2.0 GB.
Through the MCP: correct — Four get_funding calls plus checks. Hyperliquid 11.12/12.40, edgeX 6.65/10.95, Extended 7.59/11.26, Pacifica 10.02/10.95 % APR for BTC/ETH, and Nado BTC about 2%, ETH about 8% on the serving host's copy (the agent quoted the Nado range across copies and said why). Best trade long Nado, short Hyperliquid BTC, about +10% APR (it said 9.6%). Stated each interval. 165k tokens against 127k in A20 (1.31x, inside the noise) and 297k for the parquet agent: the funding win holds.
165,475 tokens processed over 6
turns and 13 tool calls (4,082 generated), 53 s,
$0.45 at list price.
Transcript ·
raw stream
Agent over the parquet: correct — Time-weighted every rate, carried Nado's change-only feed forward, converted edgeX from 4 h and Nado from 24 h. Same figures as the MCP agent; Nado about 2% BTC on the serving host's copy. 297k tokens against 319k in A20.
296,714 tokens processed over 16
turns and 15 tool calls (12,949 generated), 134 s,
$0.57 at list price.
Transcript ·
raw stream
T2 · Cross-venue lead-lag and price dislocation, BTC, one day
Question, word for word: Lead-lag and dislocation. For the BTC perpetual on Hyperliquid, edgeX, ApeX, Pacifica and dYdX on 2026-09-27 (UTC day), using 1-minute closing prices: (1) for each of the other four venues, the cross-correlation of its 1-minute log returns with Hyperliquid's at lags -3..+3 minutes, and which venue leads; (2) for each of the four, the largest absolute 1-minute close dislocation versus Hyperliquid in basis points and the minute it happened. Say how you handled minutes where a venue did not trade.
Reference answer: CORRECTED this run (fill order within a millisecond; see setup.tie_order). Correlation peaks at lag 0 for edgeX (0.96) and ApeX (0.92): no leader at 1 minute. Hyperliquid leads Pacifica (lag +1 about 0.30) and dYdX (lag +1, weaker, on about 197 returns). Largest dislocations: edgeX -5.51 bp 06:27, ApeX -5.68 bp 08:44, Pacifica -11.05 bp 13:31, dYdX +66.36 bp 14:29 (one order sweeping the book inside one millisecond). The floor withholds none of these four minutes.
Parquet the agent over the parquet had on disk for this: 859.7 MB.
Through the MCP: partly wrong — Lead-lag right for edgeX, ApeX and Pacifica, and dYdX's lead correctly declined: the floor withholds 277 of its 409 trading minutes and the agent said 34 pairs cannot decide it, naming the withheld minutes. That is the fourth grade, earned on the one cell the floor genuinely blocks. Dislocations right for edgeX and dYdX (+66.36 bp at 14:29). Pacifica is the right minute with the wrong value (-10.70 against -11.05 bp) and ApeX the wrong minute by 0.08 bp: both come from how the server picks a close among fills sharing a millisecond, not from the floor, which withholds none of the four maxima under fill order. 614k tokens against 309k in A20: dearer by more than the noise, from 15 model calls against 7, each re-reading the conversation (the tool output it received was smaller, about 113k characters against 128k); 4.6x the parquet agent.
614,489 tokens processed over 15
turns and 22 tool calls (10,803 generated), 129 s,
$0.80 at list price.
Transcript ·
raw stream
- lead-lag edgeX: correct
- lead-lag ApeX: correct
- lead-lag Pacifica: correct
- lead-lag dYdX: correctly declined — Said it cannot be determined from 34 usable return pairs, and tabled the 277 dYdX minutes the floor withheld. This is the one T2 cell the floor genuinely blocks.
- dislocation edgeX: correct
- dislocation ApeX: partly wrong — -5.60 bp at 07:12 against -5.68 bp at 08:44. Hyperliquid's last millisecond of 08:44 holds 11 fills walking 84,706 to 84,716 in fill order, and the server's close is one of the middle ones. Not the floor.
- dislocation Pacifica: partly wrong — Right minute (13:31), value -10.70 bp against -11.05 bp: the server's Hyperliquid close for 13:31 is a middle fill of a 29-fill sweep. The agent warned this maximum might sit in a withheld minute; that caution was reasonable but it was not the cause.
- dislocation dYdX: correct
Agent over the parquet: correct — Deduplicated on trade id and, on its own, kept same-millisecond fills in file order (fill order) rather than id order: edgeX -5.51 bp 06:27, ApeX -5.68 bp 08:44, Pacifica -11.05 bp 13:31, dYdX +66.36 bp 14:29, matching the corrected reference exactly. Hyperliquid leads Pacifica and dYdX (the latter on 197 returns, weaker). 134k tokens against 193k in A20 (0.69x, inside the noise).
134,223 tokens processed over 10
turns and 9 tool calls (8,620 generated), 88 s,
$0.39 at list price.
Transcript ·
raw stream
T3 · Seven-venue BTC volume share and realised volatility, one week
Question, word for word: Venue share and realised vol. For BTC perpetuals on Hyperliquid, edgeX, ApeX, Pacifica, dYdX, Paradex and Nado over 2026-09-21 00:00 to 2026-09-28 00:00 UTC: each venue's total traded notional in USD and its share of the seven-venue total; and each venue's realised volatility of 1-minute log returns, annualised. Is realised vol consistent across venues? If not, explain the outliers.
Reference answer: Notional: Hyperliquid 20.87B (73.8%), edgeX 2.99B, ApeX 2.11B, Pacifica 1.38B, Nado about 0.8B (one significant figure, the serving host's copy), dYdX 58.5M, Paradex 10.9M (only 28 h of data in the window). Realised vol about 36-37% on the liquid venues; dYdX and Paradex higher from thin trading. Added this run: on back-to-back minutes only (the MCP agent's definition) the full data gives Pacifica 36.4% and dYdX 68.0%; dropping minutes of fewer than 3 trades gives 43.8% and 88.5% (reference/t3_vol_floor.json).
Parquet the agent over the parquet had on disk for this: 2.9 GB.
Through the MCP: partly wrong — The sum is fixed: every notional and share is right, dYdX $58.6M against $58.5M and Paradex $10.9M, Pacifica 1.38B, Nado about 0.8B on the serving host's copy, because the agent read withheld_by_floor and added the withheld volume back. The vol half is now what the floor costs: Pacifica 43.8% against 36.4% and dYdX 88.5% against 68.0% on the agent's own definition (back-to-back minutes), because a minute with 3 or more trades is a busier, more volatile minute; the parquet reproduces both figures exactly once the floor's minutes are removed. A withheld total cannot repair a second moment, and nothing said so in terms the agent acted on: extremum_warning fired on 14 of the 17 query results it received, because it triggers on any ORDER BY. 963k tokens against 707k in A20 (1.36x, inside the noise); 2.9x the parquet agent.
963,396 tokens processed over 16
turns and 31 tool calls (18,082 generated), 185 s,
$1.50 at list price.
Transcript ·
raw stream
- notional and share, all seven venues: correct — Read withheld_by_floor and added the withheld volume back at the week's average price. A20's agent was 4% short on dYdX and Paradex.
- realised vol Hyperliquid, edgeX, ApeX: correct
- realised vol Pacifica, dYdX: partly wrong — 43.8% and 88.5% against 36.4% and 68.0% on the agent's own definition over all minutes. Removing the floor's minutes from the parquet reproduces the agent's figures to the pair (5,217 and 774 pairs). Attributed to thin trading, not to the floor.
- consistency and outliers: correct — dYdX and Paradex named as the outliers, from thin trading; it also called Pacifica high at 1 minute, which the full data does not show.
Agent over the parquet: correct — Deduplicated across file boundaries, excluded Pacifica settlement prints and Paradex unwind transfers, flagged Paradex's 28 hours. Notional matches the reference, Nado about 0.8B on the serving host's copy; vol with prices carried forward, Pacifica 35.9%, dYdX 39.9%. 328k tokens against 338k in A20.
328,034 tokens processed over 15
turns and 14 tool calls (14,510 generated), 156 s,
$0.65 at list price.
Transcript ·
raw stream
S1 · BTC VWAP on Hyperliquid for one day
Question, word for word: What was the volume-weighted average price of the BTC perpetual on Hyperliquid on 2026-09-27 (UTC day)? One number.
Reference answer: 84,600.71 (175,800 trades)
Parquet the agent over the parquet had on disk for this: 560.6 MB.
Through the MCP: correct — 84,600.71 from the same 175,800 trades. 49k tokens against 167k in A20 (0.29x, a real move): describe_dataset for one instrument now answers in about 9,000 to 12,000 characters, against 47,004 last time. Still dearer than the parquet agent's 39k.
49,031 tokens processed over 3
turns and 4 tool calls (1,237 generated), 17 s,
$0.17 at list price.
Transcript ·
raw stream
Agent over the parquet: correct — 84,600.71 from 175,800 trades.
39,076 tokens processed over 6
turns and 5 tool calls (2,517 generated), 32 s,
$0.14 at list price.
Transcript ·
raw stream
S2 · BTC traded notional on Nado for one day
Question, word for word: What was the total traded notional, in USD, of the BTC perpetual on Nado on 2026-09-27 (UTC day)? One number.
Reference answer: About $50M to one significant figure, on the serving host's copy (product 2; product 1 is spot). Both routes and the reference agree on the exact figure, which is in results and transcripts.
Parquet the agent over the parquet had on disk for this: 8.5 MB.
Through the MCP: correct — About $50M on the serving host's copy, the same figure to the dollar as the reference. It used hourly bars (nothing withheld) and cross-checked the minute bars, whose shortfall it matched to withheld_by_floor exactly: 187 trades, about 3 BTC. 193k tokens against 280k in A20 (0.69x, inside the noise); the parquet agent spent 65k.
193,395 tokens processed over 6
turns and 8 tool calls (2,913 generated), 34 s,
$0.39 at list price.
Transcript ·
raw stream
Agent over the parquet: correct — About $50M on the serving host's copy, the same figure to the dollar as the reference.
65,441 tokens processed over 7
turns and 6 tool calls (3,384 generated), 37 s,
$0.20 at list price.
Transcript ·
raw stream
S3 · ETH 1-minute realised vol on edgeX for one day
Question, word for word: What was the realised volatility of the ETH perpetual on edgeX on 2026-09-27 (UTC day): the standard deviation of 1-minute log returns of the close, annualised? One number.
Reference answer: 26.6% (0.2661)
Parquet the agent over the parquet had on disk for this: 234.2 MB.
Through the MCP: correct — 26.6% (0.2662). 82k tokens against 55k in A20 (1.48x, just inside the noise) and 74k for the parquet agent.
81,782 tokens processed over 5
turns and 6 tool calls (2,643 generated), 33 s,
$0.19 at list price.
Transcript ·
raw stream
Agent over the parquet: correct — 26.6% (0.2662); dropped 14 trades duplicated across files.
73,532 tokens processed over 8
turns and 8 tool calls (3,308 generated), 37 s,
$0.19 at list price.
Transcript ·
raw stream
K1 · BTC VWAP on Hyperliquid, the call already known
Question, word for word: Call mcp__peerfeed__get_ohlcv with instrument 'hyperliquid/trades:BTC', start '2026-09-27T00:00:00Z', end '2026-09-28T00:00:00Z', granularity '1d', and report the bar's vwap as the answer. Do nothing else.
Reference answer: 84,600.71
Through the MCP: correct — 84,600.71 in one call.
14,226 tokens processed over 2
turns and 1 tool calls (475 generated), 10 s,
$0.03 at list price.
Transcript ·
raw stream
K2 · BTC notional on Nado, the call already known
Question, word for word: Call mcp__peerfeed__get_ohlcv with instrument 'nado/trades:2', start '2026-09-27T00:00:00Z', end '2026-09-28T00:00:00Z', granularity '1d', and report volume x vwap of that bar as the answer. Do nothing else.
Reference answer: About $50M, as s2
Through the MCP: correct — About $50M in one call, on the serving host's copy; see s2.
14,447 tokens processed over 2
turns and 1 tool calls (718 generated), 12 s,
$0.03 at list price.
Transcript ·
raw stream
K3 · ETH realised vol on edgeX, the call already known
Question, word for word: Call mcp__peerfeed__run_query with dataset 'edgex/trades', instrument 'ETHUSDC', start '2026-09-27T00:00:00Z', end '2026-09-28T00:00:00Z' and sql "WITH r AS (SELECT ln(close / lag(close) OVER (ORDER BY ts)) AS lr FROM t) SELECT stddev_samp(lr) * sqrt(525600) AS rv FROM r", and report rv as the answer. Do nothing else.
Reference answer: 26.6%
Through the MCP: correct — 0.2661 in one call.
14,589 tokens processed over 2
turns and 1 tool calls (638 generated), 10 s,
$0.03 at list price.
Transcript ·
raw stream
Every file: results.json (every count on
this page) · verdicts.json (the grades, the reference
answers and the setup, the one hand-written input) · reference
computations · the server's audit lines for these
calls. The harness is scripts/run_research_agents.py; the reference was computed
independently from the same parquet, and where the parquet agent disagreed with our first
reference, the agent was right and the reference was corrected (same-millisecond fills). The earlier version of this section compared one tool call with pasting the raw rows into the agent's context and reported 78× to 11,728×. No competent agent does that, so we withdrew it; its files stay published.
What an agent re-deriving from raw rows gets wrong.
None of these raise an error. Each one produced a confident, plausible, wrong number in our own archive before we found it. We do not claim an agent cannot handle them: in our measurement above, a strong agent over the parquet handled every one it met. What the MCP does is apply the fix once, for every caller, instead of each agent rediscovering it.
Timestamps that declare milliseconds and store nanoseconds
Some venue streams write nanosecond epochs into a column typed as milliseconds. Read as declared, 1789516741100071262 lands in the year ~58,700, or the scan aborts on the unit overflow. Cast it by hand and you get plausible dates on some shards and garbage on the next, because the unit is declared per file.
What the MCP does: Each dataset carries its verified time column and its semantics. Where the exchange time is unusable the bars are timed by our observation clock and labelled ts_semantics: observation, so you know which clock you are reading.
A ten-month-old price inside today's shard
One venue's trade stream carries events whose exchange time decodes to 2025-11-05 inside a 2026-09-16 shard. Bucket by observation time and that old price lands in a current bar and moves its high or low. Nothing in the rows flags it.
What the MCP does: That dataset is withheld from the MCP until the question is settled upstream. An answer we cannot stand behind is refused rather than served.
Content keys that merge distinct trades
On one venue, keying trades by (symbol, time, price, size) would have collapsed 36.12% of 29,743,595 rows: distinct trades that share a millisecond, a price and a size. The venue's trade id finds 58 true duplicate groups instead. Worse, a key field that is missing is filled with a constant, not an error, so one mark-price feed keyed on a timestamp it did not carry silently became one record per market per minute.
What the MCP does: The aggregator deduplicates on an identity measured against real rows, and the MCP does it again on the venue's trade id, so a trade held in four shards is counted once. Our first research run found dYdX trades held up to four times in the archive and the server it used counted every copy (2.9× too much notional); counted once it is $58.5M, matching the reference. The archive fix is deployed for new data, but shards written before it still hold the copies, so the MCP's own dedup is what removes them. (In our latest run the agent summed minute bars instead and came back 4% short on dYdX: that was public mode's three-trade floor, not a duplicate, and it is graded as a loss below — see the floor's own entry, because 4% is the small half of what it does.)
295,735 files for one day of one feed
Partitioning an order-book feed by market turned one day into up to 295,735 files averaging about 10 KiB. An agent that lists a month of it opens millions of objects before it reads a row. New data has been written one file per two-hour shard since 2026-09-15; the history before it has not been rewritten yet.
What the MCP does: Shards are resolved from an index, never by listing directories, and one call opens at most 400 of them.
An hourly rate averaged with an eight-hour rate
Hyperliquid settles funding every hour and OKX every eight; Extended publishes its rate every minute and Nado every 20 seconds. Average “the funding rate” across them without normalising and you are off by up to 8×. One venue's history before 2026-09-28 is not a rate series at all: a recorder filter compared a field that did not exist, so it kept one row per restart.
What the MCP does: get_funding serves every venue's rate with the interval it is quoted over (Nado 24 h, edgeX 4 h, the rest hourly) and the same rate per hour, per 8 hours and as APR, time-weighted. The evidence for each interval is stated, including which ones are documented rather than measured. It does not make a change-only feed host-independent, and we will not pretend otherwise: the same call gave Nado BTC 1.52% APR on the host that serves this MCP and 1.72% on another of our hosts, because the other held 6% more rows and on a feed that only publishes changes an extra row also changes how long the previous rate stood. covered_fraction read 1.00 in both cases — it counts whether a rate was in force, not whether you saw every change. Read Nado to one significant figure, or take the venue's settled payments (1.58% for this window) where precision matters.
A gap that reads as a quiet market
A shard that holds only part of its two-hour window looks exactly like a market that did not trade. Bars built on it understate volume and clip the high and low, and the rows cannot tell you.
What the MCP does: Every answer carries the coverage of the shards behind it and, below a threshold, a PARTIAL DATA warning with the percentage.
Every limit is known up front, so your agent can plan against it.
describe_environment hands these to your agent before its first
query. An oversized call is refused before it reads a byte and costs nothing. These are the
values in the server's source today; why each exists is in the fourth column and the
footnote.
| Limit | Value | What it means for your agent | Why it exists | Set in |
|---|---|---|---|---|
MAX_ROWS_SCANNED |
60,000,000 rows | Up to 60 million rows read per call, summarised server-side. Past that the call is refused from catalogue counts before a byte is read, so asking too much costs you nothing. | One call monopolising a host that also records. | config.py |
MAX_FILES_SCANNED |
400 shards | One call can span up to 400 two-hour shards, about 33 days of one dataset, within the row cap. | A call that fans out across the whole archive. | config.py |
MAX_BARS |
5,000 bars | You get the resolution you asked for or a refusal naming the finest one that fits. Never a quietly coarser series. | Silent resampling. | config.py |
MAX_RESULT_ROWS |
5,000 rows | Answers, not tables: aggregate in the SQL. A result past the cap arrives flagged as truncated, never silently cut. | A query that returns a table instead of an answer. | query.py |
Per-user row budget |
rows scanned per user per day | Your free quota, metered in rows scanned. The meter is the only thing that changes as you use more. | Rebuilding the tape one small call at a time. This is the control that actually matters against bulk copying, and it is the meter for the free quota. | audit.py |
CALLS_PER_MINUTE |
60 per user | Room for an agent working quickly. At most 2 queries run at once across all callers, so nobody's loop slows your call. | An agent in a tight loop starving the recorders that share the host. | audit.py |
Audit log |
every call | Your usage is attributable to you, which is what makes a per-user quota possible. | Unattributed use. Who asked, which tool, which instrument, what range, rows returned and scanned, duration, error. | audit.py |
Read-only, single-SELECT SQL |
one statement, must read from t | Any read your SQL can express, over the table t: window functions, CTEs, aggregates. | Anything but a read. DuckDB is opened read-only; the statement must start with SELECT or WITH, and write or attach verbs are refused as a second line of defence. | query.py |
Public mode
- run_query's table t is 1-minute bars built from every trade, so your SQL starts from clean bars rather than a tape. It is also why a raw row cannot come back: structural, not a SQL parser guessing what is aggregate-only.
- Bars built from fewer than 3 trades are withheld and counted, because a one-trade bar is that trade. The cost is real: in our measurement this hid Pacifica's largest dislocation, which sat in a 2-trade minute.
The caveat, in the server's own words
Tokens are the identity boundary: the rate limits, the row budget and the audit log are all per user. Without a token every caller is one user, so a single agent can exhaust the budget for everyone and the log cannot say who did what. That is why the server fails closed: if the token file is missing or unreadable, no request authenticates.
Five tools. Start with describe_environment.
It tells an agent every dataset, the canonical columns, the granularities and every limit above, so it can plan a query instead of discovering the limits by hitting them.
| Tool | What it does |
|---|---|
list_instruments | List instruments this host can serve. |
describe_dataset | Describe a dataset, or one instrument in it. |
get_ohlcv | OHLCV bars aggregated from recorded trades. |
run_query | Run read-only SQL over a bounded slice of the data. |
get_funding | Funding rates, normalised across venues that quote different intervals. |
data_quality | Measured data-quality findings for the datasets this host indexes. |
describe_environment | Capabilities, limits and canonical schema — read this first. |
get_activity_bars | Bars that close on activity rather than on the clock. |
Not public yet. The endpoint is published here when access opens. Until then tokens are issued by hand, one per caller, because the limits below only mean something per user.
Footnote: why it answers rather than hands over rows. This is our constraint, not a feature we are selling you. Computing next to the archive is how centralized-exchange data becomes sellable at all: their raw ticks are licensed by the exchange, so we sell only what is derived from them (liquidations, funding, open interest). It is also why the limits above exist: a free tool that invites many cheap calls invites copying the archive a call at a time, and the per-user row budget and the audit log are what bound that. Pagination slows bulk copying down; it does not prevent it. An agent can rebuild a tick series across many calls. The daily row budget and the audit log are the controls that stop that, which is why they are per user and why the free quota is metered in rows scanned. If what you need is the rows, the subscription hands them over for every venue where we may sell them.