What each feed shows¶
Two captures of the same market can tell you different things. One source shows every order and another only the top 100. One reports every change and another polls once a second. One names the taker of a trade and another cannot. These properties belong to the source and the venue, not to the analysis, and they decide which results you can trust.
This page describes each property once, then sets out its value for every source and venue in four tables:
Each how-to page for a source opens with its row of these tables.
The values were checked in September 2026 with ccxt 4.5.84 and cryptofeed 2.4.1, by reading their code and by short live captures of public data. A value that could not be checked says why. How the values were checked gives the details.
The properties¶
Each property is described in the same way:
- What it is.
- Values: the values it can take.
- Sources: which sources have which value (the tables below give every source).
- Lets you conclude: what a value allows.
- Stops you concluding: what a value rules out, and how that shows up.
- In the package: where ob-analytics declares or checks it.
Level¶
Whether the feed shows single orders or only the total size at each price.
- Values: L3, one row per order, with the order's id. L2, one row per price level, with no ids.
- Sources: L3 is native Bitstamp, cryptofeed on its four per-order venues, LOBSTER and Databento. L2 is everything else.
- Lets you conclude: at L3, order lifetimes, queue position, order types, and the order behind each trade.
- Stops you concluding: at L2, anything about single orders. A level that grows could be one new order or ten.
- In the package:
Source.level. The pipeline skips the per-order stages at L2 (see Process L2 feeds).
Depth shown¶
How much of the book the feed shows.
- Values: the whole book; the top N orders a side; the top N price levels a side; whatever each poll returns.
- Sources: most feeds show the whole book. cryptofeed's Bitstamp and
Bitfinex books show the top 100 orders a side. ccxt's Kraken book shows the
top
--depth-limitlevels, and its Binance book 1,000 levels by default. - Lets you conclude: depth, the depth summary's rings and the depth heatmap are right out to the edge of what is shown.
- Stops you concluding: anything past that edge. With a window of the top N, a level or order that leaves the window looks the same as one that was cancelled.
- In the package:
--depth-limitfor ccxt. The cryptofeed source keeps an order that leaves Bitstamp's top-100 window, and records itdeletedonly once a snapshot shows its price without it.
Why Binance stops at 5,000 levels. Binance's REST book returns at most 5,000
levels a side, and past its opening book ccxt learns a level only when it
changes. A deeper book would have gaps at the bottom, so the capture refuses a
--depth-limit above 5,000. For BTC/USDT, 5,000 levels reach about 1% from the
price. See Binance.
Orders shown¶
At L3: whether the feed shows the orders that trade on arrival, the takers.
- Values: every order, takers included; resting orders only.
- Sources: native Bitstamp shows every order. All other L3 sources show resting orders only.
- Lets you conclude: with every order, which order took liquidity in each
trade, so orders can be labelled
marketormarket-limit. - Stops you concluding: with resting orders only, anything about takers. It
shows up in the order types: no order is labelled
marketormarket-limitfrom the feed alone. - In the package:
Source.trade_attribution(below).
Update form¶
How the feed reports the book changing.
- Values: every change, one message each; changes merged, where one update can hold several; repeated snapshots of the book; polls.
- Sources: native Bitstamp, LOBSTER, Databento and cryptofeed's Bitfinex, Blockchain.com and Independent Reserve books report every change. ccxt's websocket venues merge changes. cryptofeed's Bitstamp L3 book is a snapshot about 10 times a second. Kalshi is polled once a second.
- Lets you conclude: with every change, short-lived orders, cancel rates and every flicker of the spread.
- Stops you concluding: with merged changes, snapshots or polls, anything that starts and ends between two updates. It shows up as too few changes: a cancel rate from such a feed is too low, and short lifetimes are missing.
- In the package: not declared. The how-to page for each source describes it.
Why ccxt merges changes. ccxt applies every message the venue sends to its own copy of the book. The capture reads that copy when it asks for the next update, so all messages that arrived since the last read reach the capture as one update.
What can be missed¶
What a correct capture can still leave out.
- Values: nothing; changes between snapshots or polls; changes merged into one; a lost message that goes unnoticed.
- Sources: a file (LOBSTER, Databento) misses nothing. Snapshots and polls miss what happens between them. ccxt's Coinbase and Kraken books, and the native Bitstamp feed, cannot notice a lost message.
- Lets you conclude: where nothing can be missed, a quiet book was quiet.
- Stops you concluding: elsewhere, that a quiet book was quiet. A lost message shows up only later, as a stale order or level, or not at all.
- In the package: the sequence check (below), where the venue sends a sequence number.
After a lost message¶
What happens to the capture's book when a message is lost.
- Values: drifts, where the book stays wrong until the capture ends or the level changes again; resyncs, where the source takes a new opening book; corrected by the next snapshot or poll.
- Sources: native Bitstamp drifts. ccxt's Binance book and cryptofeed's per-change L3 books resync. Snapshots and polls correct themselves.
- Lets you conclude: with resync or snapshots, a long capture stays right.
- Stops you concluding: with drift, that a level or order in the book is still there. It shows up as stale orders, or as a price level the venue removed long ago.
- In the package:
audit'sstale_orderscheck at L3 and the crossing check at L2.meta.jsoncounts ccxt's and cryptofeed'sbook_resyncs, andauditwarns about them for a capture of several segments.
Why snapshots correct themselves and change streams drift. A snapshot states the whole book, or the whole window, so it replaces whatever the capture held. A change stream states only what changed. A lost removal is never sent again, so the order or level stays. On an L2 stream each change carries the level's new size, so a level that is wrong is put right the next time it changes, but a level that should have gone stays until something else touches it.
Sequence¶
Whether the venue numbers its messages, and what a skipped number means.
- Values: contiguous, where every message adds one and a skip is a lost message; only rises, where skips are normal; none.
- Sources: cryptofeed's per-change L3 venues only rise; see the note below. ccxt's Binance book only rises. Databento only rises within one instrument. Most other feeds send no number.
- Lets you conclude: with a contiguous number, whether a message was lost.
- Stops you concluding: with none, anything about lost messages.
auditthen reports0 row(s) numberedfor the venue sequence. - In the package:
Source.sequence_kindand thesequence_gapscheck (see Check data quality).
cryptofeed's numbers on Bitfinex and Blockchain.com count every message on the
connection, trades and heartbeats included. On Independent Reserve, cryptofeed
passes on no row for a message about an order it does not hold. So the book rows
skip numbers although nothing was lost: 60 skips in 5 minutes on Bitfinex, 20
in 13 minutes on Independent Reserve. The cryptofeed source declares these
numbers as only rising, so audit checks only that they never go back.
cryptofeed itself checks every message on the connection, and on a lost one it
reconnects and takes a new opening book, which the capture counts as a
book_resyncs. On Bitfinex and Blockchain.com the count starts again on the
new connection; the capture records that step back as a sequence_restarts,
and audit does not count it as out of order.
Clocks¶
Which times each row carries.
- Values:
both, venue and receive;receive_only;venue_only. - Sources: most live feeds carry both. Kalshi books and cryptofeed's Bitfinex, Blockchain.com and Kraken books carry receive time only. LOBSTER carries venue time only.
- Lets you conclude: with both clocks, latency, and whether messages arrived out of order.
- Stops you concluding: with one clock, either of those.
- In the package: the
Clocksdeclaration, andaudit'sexchange_time_after_receiveandexchange_time_reorderedchecks, at L2 and L3.
The schema has a column for each clock, so a row with one clock copies it into
the other column, as LOBSTER does. Where a live venue sends no time with its
book, exchange_timestamp holds the receive time. A live capture counts the
books that came without a venue time (books_without_venue_time in
meta.json). When none came with one, it records "clocks": "receive_only".
With one clock the two columns are equal, so audit does not run the clock
checks. It says why in their place.
Independent Reserve and Coinbase send a time with every message except the
opening book. The opening book's rows carry the receive time in both columns,
and the capture keeps both clocks. Those rows are marked as the opening book
(origin is snapshot), and the clock checks leave them out.
Where cryptofeed fetches the opening book over REST (Binance, Independent Reserve), it does so while it handles the first live message, and hands the book over first. The book is stamped with a time taken after that message arrived. So the capture holds a book that has no changes listed until the next book arrives. If the next one was received earlier, the held book is placed 1 ms before it and marked as the opening book. The message keeps its own receipt time, and replay applies the book first.
Crossing¶
Whether the book can show a bid at or above an ask.
- Values:
matched_book, never crossed;diff_feed, can be crossed and still faithful;price_levels, an L2 book, which the venue does not publish crossed. - Sources: native Bitstamp is a diff feed. The other L3 sources are matched
books. Every L2 source is
price_levels. - Lets you conclude: in a matched book or price levels, a crossed book is a fault in the data or the capture.
- Stops you concluding: in a diff feed, that a crossed book is a fault.
auditreports it as information, not as an error. - In the package:
Source.feed_typeand thecrossed_bookcheck. See Data quality for more, and foruncross=, which removes a crossing for display.
Three cryptofeed L3 captures failed the crossing check in testing:
- Bitstamp: crossed for 0.125% of the time, above the 0.05% allowed. Bitstamp's own snapshots can show a bid at the price of a resting ask for a moment, as its public order feed does. In the first case, the bid was there for 87 ms.
- Bitfinex: crossed for 58% of the time. cryptofeed reports every change to a Bitfinex order, of price or of size, as a delete and a create with the same id, so the capture holds ids that are created more than once. The cause of the crossing was not found.
- Independent Reserve: crossed for 58% of a 20-minute capture on 1 October
auditreported 321 stale resting orders. 223 of them were still in the venue's book a day later: trades from the venue's other markets, at prices in another currency, made them look stale. The cryptofeed source now leaves those trades out (see the note under Trades). Without the other 98, the book was never crossed. Of those 98:- 52 came from the opening book, which cryptofeed fetches from the venue's REST interface, and the stream never mentioned them again. Those files are gone, but captures on 3 October 2026 showed the cause: an opening book older than the stream (see the note under Timing and integrity). The source now fetches a newer book.
- 7 had a change, and cryptofeed then ignored their cancel. The source now passes such cancels on (see the note under Timing and integrity).
- 39 were deleted more than a second after a trade at a price past them.
Trade sides named¶
Which orders of a trade the order events can name: the maker, which was resting, and the taker, which arrived.
- Values: both; maker only; neither.
- Sources: native Bitstamp names both. cryptofeed's Bitstamp and
Independent Reserve L3 captures, LOBSTER and Databento name the maker only.
cryptofeed's Bitfinex and Blockchain.com captures name neither, although the
cryptofeed source declares maker only for all four venues. So
auditreports every Bitfinex trade as unmatched. L2 names neither. - Lets you conclude: with both, maker–taker links and order types.
- Stops you concluding: with maker only, which order took liquidity.
auditthen looks only for makers, and the order types have nomarketorders. - In the package:
Source.trade_attribution. Theunmatched_tradescheck looks only for the orders the feed can name.
Why ITCH does not name the aggressor. Nasdaq's ITCH feed, from which LOBSTER is built, reports each execution against the resting order it hit. It does not identify the incoming order. This protects traders who split a large order into small ones: publishing the id would let others link the pieces. Databento's MBO data follows the same exchange convention. The LOBSTER reader guesses the taker; see LOBSTER.
Why Bitstamp publishes both ids. Bitstamp's trade messages carry
buy_order_id and sell_order_id, and its live_orders channel reports every
order the venue accepts, takers included. So both orders of a trade can be
found. One kind of order does not report what it executed: an instant order
bought by quote amount is created with size 0 and then shows 0 or one lot,
never what it bought. The trade reader then finds the order by its id alone,
and audit counts such trades as matched by order. Independent Reserve's
trade messages also carry both ids, BidGuid and OfferGuid, and the
cryptofeed source writes them to trades.csv. But its book does not show
every taker: some takers appear as new limit orders, and most never do. So
the source declares maker only.
Taker side¶
Whether a trade's buy or sell comes from the venue or is worked out.
- Values: from the venue; classified with Lee–Ready.
- Sources: every live source reports it. A
depth_csvfile without asidecolumn is classified. - Lets you conclude: trade signs and flow toxicity (VPIN, order flow imbalance).
- Stops you concluding: with a classified side, that each sign is right. Lee–Ready is wrong for some trades, and a wrong sign looks like any other.
- In the package: the trades
directioncolumn, and trade signs when there is no side.
The ccxt source reverses Coinbase's sides. Coinbase reports the side of
the maker, and ccxt passes it on unchanged. In a test capture that recorded
ccxt's side, 93% of trades marked buy printed at or below the best bid, and
96% of trades marked sell at or above the best ask. So the ccxt source
reverses the side of each Coinbase trade, and trades.csv names the taker, as
for every other source. meta.json records "trade_side_reversed": true. A
Coinbase capture whose meta.json has no trade_side_reversed was made before
this change and still holds the maker's side: reverse its signs before you use
them.
Fills¶
Where the capture learns that an order was filled rather than cancelled.
- Values: per fill, from the order feed; from execution rows; from the trade tape; not linked.
- Sources: native Bitstamp reports each fill. LOBSTER has execution rows, Databento fill records. cryptofeed's Bitstamp and Independent Reserve L3 captures take fills from the trade tape. On Bitfinex and Blockchain.com an order's size drops, and nothing links that to the trade.
- Lets you conclude: where fills are linked, filled against cancelled, and order lifetimes.
- Stops you concluding: where they are not, whether an order that went away
was filled or cancelled. Both show up as
deleted, so filled orders count as cancelled. - In the package: the order
changedanddeletedevents, and the order types built from them.
Trade tape gaps¶
Whether the trade tape can miss or repeat trades.
- Values: none; polled; repeated trades removed; trades from before the capture.
- Sources: Kalshi is polled, and trades from before the opening book are dropped. Polymarket can send a trade twice, and the capture drops the repeat. ccxt's Coinbase tape and cryptofeed's Bitfinex tape start with trades from before the capture.
- Lets you conclude: where there are no gaps, trade counts and volumes.
- Stops you concluding: that a trade near the start of a capture happened
after the book was captured. Check its
exchange_timestamp. - In the package:
meta.jsoncountsduplicate_trades.
Price grid¶
The smallest step between two prices.
- Values: fixed; mixed, where the step depends on the price; changes during the capture.
- Sources: most crypto books use a fixed step. Bitfinex quotes five significant figures, so its step changes with the price. Kalshi steps depend on the market. Polymarket makes its step finer near 0 and 1, during the capture.
- Lets you conclude: with the right tick size, every price is stored exactly.
- Stops you concluding: that a depth ring in basis points holds the same number of ticks on every market. On a coarse grid, a narrow ring can hold no tick at all.
- In the package:
PipelineConfig.tick_size. A ccxt capture records it inmeta.json, and the loaders refuse prices off the grid. ccxt ignores Polymarket's messages that change the step. The capture finds the finer step from the prices instead, records the final one, and counts the changes intick_size_changes.
What the book means¶
How to read a price in this book.
- Values: normal; built from Yes and No bids; one book per outcome.
- Sources: Kalshi's book is built from Yes and No bids. Polymarket has one book per outcome. All others are normal.
- Lets you conclude: on a prediction market, the price is the probability of the outcome.
- Stops you concluding: that depth in basis points means the same at a price of 0.04 as at 100,000. At 0.04, a ring 25 basis points wide holds no tick, so most rings are empty.
- In the package: see Kalshi and Polymarket.
Why the Kalshi book is built from Yes and No bids. A Yes contract and a No
contract together pay $1. So a bid to buy No at p is an offer to sell Yes at
1 - p. Kalshi publishes only bids on both sides, and ccxt turns the No bids
into Yes asks.
Access¶
What you need to capture the feed.
- Values: public; an API key; a supported location; a paid subscription.
- Sources: most crypto venues are public. Binance refuses some locations. LOBSTER and Databento are paid. Kalshi's websocket and Coinbase's per-order feed need a key.
- Lets you conclude: whether you can capture it at all.
- Stops you concluding: that one venue id means one market everywhere.
From a refused location,
binanceusis a separate venue with its own book. A feed that needs a key stops at the start with an authentication error. - In the package: ob-analytics does not use API keys yet.
What the book shows¶
| Source | Level | Depth shown | Orders shown |
|---|---|---|---|
bitstamp (native) |
L3 | whole book | every order, takers included |
| cryptofeed, Bitstamp | L3 | top 100 orders a side | resting only |
| cryptofeed, Bitfinex | L3 | top 100 orders a side | resting only |
| cryptofeed, Blockchain.com | L3 | not checked: the BTC-USD book held between 1 and 34 orders | resting only |
| cryptofeed, Independent Reserve | L3 | whole book | resting only |
lobster |
L3 | whole displayed book | resting only |
databento |
L3 | whole displayed book | resting only |
| cryptofeed, Bitstamp L2 | L2 | whole book | — |
| cryptofeed, Kraken L2 | L2 | top 1,000 levels a side; levels that leave it are kept | — |
| cryptofeed, Coinbase L2 | L2 | not checked: cryptofeed 2.4.1 needs an API key for Coinbase | — |
| cryptofeed, other L2 venues | L2 | not checked | — |
| ccxt, Binance | L2 | 1,000 levels a side by default, up to 5,000 with --depth-limit (about 0.2–0.3% and 1.0–1.4% from the price on BTC/USDT) |
— |
| ccxt, Coinbase | L2 | whole book | — |
| ccxt, Kraken | L2 | top --depth-limit levels (100 by default), kept filled by Kraken |
— |
| ccxt, Kalshi | L2 | whole book, each poll | — |
| ccxt, Polymarket | L2 | whole book | — |
depth_csv |
L2 | what the file holds | — |
Timing and integrity¶
| Source | Update form | What can be missed | After a lost message | Sequence | Clocks | Crossing |
|---|---|---|---|---|---|---|
bitstamp |
every change | a lost message | drifts: the order stays until the capture ends (stale orders) | none | venue + receive | diff feed |
| cryptofeed, Bitstamp | snapshots, about 10 a second | orders that come and go between snapshots | the next snapshot corrects it | none | venue + receive | matched book; failed the check in testing |
| cryptofeed, Bitfinex | opening book, then every change | nothing | cryptofeed reconnects and takes a new opening book | counts every message on the connection; only rises | receive only | matched book; failed the check in testing |
| cryptofeed, Blockchain.com | opening book, then every change | nothing | cryptofeed reconnects and takes a new opening book | counts every message on the connection; only rises | receive only | matched book |
| cryptofeed, Independent Reserve | REST book newer than the stream, then every change | nothing found in testing | cryptofeed reconnects and takes a new opening book | skips messages for orders cryptofeed does not hold; only rises | venue + receive; the opening book receive only | matched book; failed the check in testing (58% crossed) |
lobster |
every change | nothing (a file) | — | none | venue only | matched book |
databento |
every change | nothing (a file) | — | venue's where sent; only rises | venue + Databento receive | matched book |
| cryptofeed, Bitstamp L2 | REST book after 5 s, then changes | a lost message | drifts until the level changes again | none | venue + receive | price levels |
| cryptofeed, Kraken L2 | changes | a lost message | drifts until the level changes again | none | receive only | price levels |
| cryptofeed, Coinbase L2 | not checked: needs an API key | not checked | not checked | not checked | not checked | price levels |
| cryptofeed, other L2 venues | not checked | not checked | not checked | not checked | not checked | price levels |
| ccxt, Binance | changes, merged | merged changes | ccxt takes a new opening book, up to 10 times | only rises | venue + receive | price levels |
| ccxt, Coinbase | changes, merged | merged changes; a lost message | drifts until the level changes again | none | venue + receive | price levels |
| ccxt, Kraken | changes, merged | merged changes; a lost message | drifts until the level changes again | none | venue + receive | price levels |
| ccxt, Kalshi | polls, 1 a second | anything shorter than a poll | the next poll corrects it | none | receive only | price levels |
| ccxt, Polymarket | changes, merged, and a whole-book snapshot every second or two while the market trades | merged changes | the next snapshot corrects it | none | venue + receive | price levels |
depth_csv |
as recorded | as recorded | — | if present | as recorded | price levels |
Notes on this table:
- Kraken: Kraken sends a checksum of its book, but neither ccxt nor cryptofeed checks it by default, so a lost message is not noticed.
- Coinbase: Coinbase numbers its messages, but ccxt does not pass the number on.
- Polymarket: each snapshot carries a hash of the book, which ccxt does not check.
- cryptofeed, Independent Reserve: before 3.0, cryptofeed forgets an order after its first change, so it ignores any later change or cancel, and a cancelled order stays in the book. In 10 minutes of the venue's own messages on 1 October 2026, 14 orders changed, and 10 of them had a later message that cryptofeed ignored: 9 cancels and 3 changes. The source uses a subclass of cryptofeed's feed that keeps the order while it rests, so those messages reach the capture. In a 12-minute capture on 3 October 2026, one order was changed and then cancelled, and the capture recorded the cancel.
- cryptofeed, Independent Reserve, opening book: the venue serves its REST
book from a cache, and the book's orders are older than the book's own
CreatedTimestampUtc: by 0.5 to 0.75 s, and once by 2 s, in 48 books compared with the stream. cryptofeed fetches the book when the first stream message arrives, so the book could be older than the stream. An order cancelled between the two stayed in the book until the capture ended: 13 orders in a 12-minute capture on 3 October 2026, two of them at the best ask. The cryptofeed source now fetches the book again, once a second, until it was created at least 3 s after the first message. This takes about 3 to 6 s, and the stream waits meanwhile, losing nothing. It does the same after each reconnect. In an 8-minute capture made this way, none of the 2,530 orders in the opening book stayed after the venue removed them. - cryptofeed, Bitstamp L2: cryptofeed waits 5 seconds, then fetches the REST book. It drops changes stamped in any second before the REST book's second, and applies those from the same second, even ones older than the book.
- Kalshi: a poll that times out ends the capture segment, and the capture starts a new one from a new opening book.
Trades¶
| Source | Maker named | Taker named | Taker side | Fills | Tape gaps |
|---|---|---|---|---|---|
bitstamp |
yes | yes | venue | per fill | none |
| cryptofeed, Bitstamp | yes, from the tape | never | venue | from the tape | a few trades from the 5 s before the first snapshot |
| cryptofeed, Bitfinex | never (the venue's trades carry no order ids) | never | venue | not linked | starts with the last 30 trades before the capture |
| cryptofeed, Blockchain.com | never (the venue's trades carry no order ids) | never | venue (not checked: 1 trade in 20 minutes) | not linked | not checked (1 trade in 20 minutes) |
| cryptofeed, Independent Reserve | yes, from the tape | only when the book shows it (6 of 26 takers) | venue | from the tape | none found |
lobster |
yes | guessed | from the execution row | execution rows | — |
databento |
yes | never | venue | fill records | — |
| cryptofeed, Bitstamp L2 | — | — | venue | — | a few trades from the 5 s before the REST book |
| cryptofeed, Kraken L2 | — | — | venue | — | none |
| cryptofeed, Coinbase L2 | — | — | not checked | — | not checked |
| cryptofeed, other L2 venues | — | — | not checked | — | not checked |
| ccxt, Binance | — | — | venue (the m flag) |
— | none |
| ccxt, Coinbase | — | — | venue; Coinbase names the maker's side, so the capture reverses it | — | starts with trades from before the capture |
| ccxt, Kraken | — | — | venue | — | none |
| ccxt, Kalshi | — | — | venue (buy = the taker bought Yes) | — | polled; trades from before the opening book dropped |
| ccxt, Polymarket | — | — | venue | — | repeated trades removed |
depth_csv |
— | — | the side column, or Lee–Ready |
— | as recorded |
Note on this table:
- cryptofeed, Independent Reserve: the trade channel for one market also
carries trades from the venue's other markets for the same coin, at prices in
their own currency. In 40 minutes of BTC-AUD on 1 October 2026, 3 of 26
trades came from BTC-NZD or BTC-SGD, at about 148,700 and 107,000, while
BTC-AUD traded near 120,000. The cryptofeed source leaves these trades out of
trades.csvand counts them asother_market_tradesinmeta.json. The venue keeps one book for all its currencies, so these trades name orders in the capture, and the source still records their fills. A capture made before this change holds them intrades.csv, andauditreads them as trades through the book and reports orders that are still resting as stale.
Venue rules¶
| Source | Price grid | What the book means | Access |
|---|---|---|---|
bitstamp |
fixed (0.01 on BTC/USD) | normal | public |
| cryptofeed, Bitstamp | fixed (0.01 on BTC/USD) | normal | public |
| cryptofeed, Bitfinex | five significant figures: 1 on BTC/USD at 83,000 | normal | public |
| cryptofeed, Blockchain.com | fixed (0.01 on BTC/USD) | normal | public |
| cryptofeed, Independent Reserve | fixed (0.01 on BTC/AUD); some trade prices are not on it | normal | public |
| cryptofeed, Kraken L2 | fixed (0.1 on BTC/USD) | normal | public |
| cryptofeed, Bitstamp L2 | fixed (0.01 on BTC/USD) | normal | public |
| cryptofeed, Coinbase L2 | not checked | normal | API key, in cryptofeed 2.4.1 |
| cryptofeed, other L2 venues | not checked | normal | not checked |
lobster |
from the file | normal | paid files |
databento |
from the file | normal | paid, API key |
| ccxt, Binance | fixed (0.01 on BTC/USDT) | normal | refused in some locations: use binanceus or --market-data-mirror |
| ccxt, Coinbase | fixed (0.01 on BTC/USD) | normal | coinbase is public; coinbaseexchange and the per-order feed need a key |
| ccxt, Kraken | fixed (0.1 on BTC/USD) | normal | public |
| ccxt, Kalshi | by market: cents; tenths of a cent near 0 and 1; tenths or hundredths of a cent throughout | Yes book, from Yes and No bids; price = probability of Yes | REST public; websocket needs a key |
| ccxt, Polymarket | 0.01 or 0.001, finer near 0 and 1; can change during the capture, and the capture finds the finer step from the prices | one book per outcome | public |
depth_csv |
you supply it | as recorded | — |
Bitstamp at L3: which source¶
Two sources capture Bitstamp orders, and each has a limit the other does not:
- Native
bitstampshows every order, takers included, and every fill. But it is a stream of changes with no sequence number, so a lost message leaves an order in the book until the capture ends. - cryptofeed's Bitstamp book shows only the top 100 orders a side and no takers. But each snapshot states that window in full, so an error there is corrected within a tenth of a second.
Use native bitstamp for analysis. Use a cryptofeed capture of the same period
as an independent check of the top of the native book.
How the values were checked¶
On 28 September 2026 we made captures of about 5 minutes on every live source
above, and of 13 to 20 minutes on the quieter venues (Kalshi, Polymarket,
Blockchain.com and Independent Reserve). We ran audit on each, and read the
ccxt 4.5.84 and cryptofeed 2.4.1 code for each venue. The table says how each
source's values were found. A value not listed for a source came from reading
the code.
| Source | Measured in a capture | Found another way |
|---|---|---|
bitstamp |
crossing, clocks, tape gaps | this package's code, tests and the bundled sample |
| cryptofeed, Bitstamp | crossing, clocks, tape gaps, price grid | the window and fills: this package's code and tests |
| cryptofeed, Bitfinex | depth, repeated ids, crossing, sequence skips, clocks, taker side, tape gaps, price grid | why ids repeat: cryptofeed's code |
| cryptofeed, Blockchain.com | number of orders and trades | — |
| cryptofeed, Independent Reserve | opening-book size, sequence skips, clocks, taker side, price grid; on 1 October 2026, crossing and trades linked to their maker; on 3 October 2026, a cancel after a change, with the fix, and the opening book's age, against REST books taken every 15 s for 12 minutes | the ignored changes and cancels, and which takers the book shows: a recording of Independent Reserve's websocket messages |
lobster, databento |
— | this package's code, tests and docs |
| cryptofeed, Bitstamp L2 | depth, taker side, clocks, tape gaps, price grid, crossing | — |
| cryptofeed, Kraken L2 | depth, taker side, clocks, tape gaps, price grid, crossing | — |
| cryptofeed, Coinbase L2 | — | the capture stopped with HTTP 401: it needs a key |
| ccxt, Binance | depth, taker side, sequence, crossing | reach from the price and the check against Binance's REST book: the Binance page's own test |
| ccxt, Coinbase | depth, taker side, tape gaps, sequence, crossing | — |
| ccxt, Kraken | depth, taker side, tape gaps, price grid, crossing | — |
| ccxt, Kalshi | depth, compared with Kalshi's REST book; clocks, taker side, timeouts, crossing | price grids: Kalshi's market list |
| ccxt, Polymarket | depth, compared with Polymarket's REST book; taker side, repeated trades, crossing | snapshot frequency and hash: a direct listen to Polymarket's websocket |
The measurements:
- Taker side: each trade's price against the best bid and ask just before it, on the venue's clock. A taker who buys pays the ask. Every trade agreed with its side on Binance (2,372 trades), Kraken through ccxt (265), Kalshi (71) and Polymarket (36). Through cryptofeed, 96% to 100% agreed on Bitstamp L2 and Kraken L2, and 92% to 96% on Bitfinex. On Independent Reserve, 18 of 22 trades agreed. On Coinbase, 93% to 96% disagreed.
- Depth shown: the most price levels or orders the capture held at once.
- Crossing: the share of time
auditfound the book crossed. No L2 capture was crossed for any measurable time; see Data quality. - Sequence: skipped numbers counted in
orders.csv. - Trades linked: trades that
auditmatched to their maker inorders.csv. On Independent Reserve, all 13 trades of a 20-minute capture on 1 October 2026 were matched. - Takers the book shows: in 40 minutes of Independent Reserve's own messages on 1 October 2026, 6 of 26 takers appeared in the book, each as a new limit order. The other 20 never did.
Blockchain.com's BTC-USD market held at most 34 orders and traded once in 20 minutes, so its depth and trade values are not checked. cryptofeed's other L2 venues were not captured.