Crypto Arbitrage Monitor
- What
- A live monitor of BTC top-of-book across Kraken and Coinbase, with fee and size sliders that recompute apparent arbitrage into net edge over a real 15-minute window — so you discover, by hand, why the visible spread is not money.
- Why it was hard
- Venue APIs share nothing — different payload shapes and symbol conventions, and a Coinbase ticker endpoint that returns last-trade size rather than resting depth, which would silently make a size control lie. Venue choice is its own trap — the venue showing the widest, most tempting spreads quotes against USDT rather than USD, so much of that gap is a stablecoin off its peg rather than an opportunity. And every failure mode — geo-blocks, rate limits, outages — has to render as an honest labelled state rather than a spinner.
- What I decided
- Browser-direct feeds with a committed recorded window as the guaranteed floor — no poller, no server, no keys, nothing written at runtime. Every venue normalised behind a VenueAdapter seam to one BookTop shape, and the dataset stores raw observations rather than verdicts, so fee and size are re-derived over the whole window instantly, in the page. Execution is simulated only — paper trading, unmistakably labelled.
- What happened
- The monitor below is live. Over the recorded window, 97% of moments look profitable at zero fees — and at 2 bps, far below any real retail fee, every single one is gone. I first worked this problem for real in 2016–17, co-founding a multi-exchange cryptocurrency arbitrage trading system.
- ○Krakenconnectingfirst request pending
- ○Coinbaseconnectingfirst request pending
hover or tap the chart for any single moment
per leg, both legs charged · approximate — venue tiers vary · ~10 bps is a typical retail taker rate
leftmost = whatever the top of book offers · then 0.001–2 BTC, log scale
the same window as numbers
Drag the fee slider from zero to a realistic taker rate and watch the green drain out of the window. Then push size up and watch opportunities turn amber — the spread is still on the screen, but the book can’t fill you. Those are the two reasons retail crypto arbitrage doesn’t work, and you just demonstrated both against real order books.
What the window shows
The chart is a real 15-minute capture — 13 Aug 2026, 15:32–15:47 UTC, 284 top-of-book snapshots at 3-second intervals from Kraken and Coinbase, both quoting BTC/USD — or, when your browser can reach both venues, the same page driven live. Every figure below is scoped to that window; this page keeps no longer history, deliberately (more on that at the end).
| Taker fee (per leg) | Moments with positive net edge |
|---|---|
| 0 bps | 275 / 284 (97%) |
| 1 bps | 32 |
| 2 bps | 0 |
| 5 bps | 0 |
| 10 bps | 0 |
At zero fees, virtually every moment looks like free money. By 2 bps — far below any real retail fee — every single one is gone. The sliders above recompute this table from the raw books; nothing is pre-baked.
Depth kills what fees miss. The median top-of-book on the binding side is 0.014 BTC on Kraken and 0.019 BTC on Coinbase — roughly $900–1,200 of notional. Ask for half a bitcoin and almost every “opportunity” becomes a spread you can see but not trade. One Coinbase snapshot in the window quotes a top-of-book of 0.0000008 BTC — five cents of bitcoin. The monitor treats zero-ish depth as what it is: nothing to trade against, not an infinite opportunity.
And the best single moment of the whole window — $23.73 gross on 1.30 BTC, buying Kraken into a Coinbase bid at 15:35:29? At a 10 bps taker fee that 1.30 BTC trade pays $165 in fees and nets −$142. Shrink the trade until fees hurt least and the best achievable outcome in that best moment is −$0.12. Even the window’s finest instant loses money at any fee tier that exists.
The spread that wasn’t there
While testing venues for this page, global Binance answered with HTTP 451 — geo-blocked — a small lesson in market infrastructure by itself. Binance.US was reachable, and showed the most tempting number of the day: its bid sat $55.44 above Coinbase’s ask — an apparent 87 bps arbitrage, tens of times wider than anything Kraken–Coinbase showed. It earned its exclusion from this monitor twice over:
gross 0.03136 BTC × $55.44 = $1.74
fees 2 legs × 0.10% × $2,000 = −$4.00 ← best-case institutional tier
───────
net −$2.26
Binance.US would take just 0.031 BTC at that bid — and it quotes BTC/USDT, a different instrument, so part of that gap is a stablecoin off its peg rather than a venue discrepancy. A thin book quoting a different instrument shows enormous apparent spreads that are pure illusion. Kraken and Coinbase both quote real USD, which makes the comparison clean — and makes the honest result undramatic: two deep venues locked together within a basis point, because arbitrage is competed away in milliseconds by people with colocated infrastructure.
Why the naive design cannot work
Detect spread → buy on A → transfer → sell on B fails on arithmetic, not execution: a BTC transfer needs confirmations measured in tens of minutes, while the discrepancy lives for seconds. By the time the coin lands, the spread is gone and you are long an asset you didn’t want at a price you didn’t choose.
The real architecture inverts it: pre-position inventory on both venues, execute both legs simultaneously against capital already sitting there, and rebalance asynchronously as a separate, latency-insensitive concern. Transfers leave the hot path entirely. That inversion is the whole insight — everything else is engineering around it.
The seam
The two venues agree on nothing:
Kraken {"result":{"XXBTZUSD":{"asks":[["63575.1","6.727",…]],"bids":[["63575.0","0.048",…]]}}}
Coinbase {"bids":[["63564.23","0.2865",1]],"asks":[["63564.24","0.0901",1]],"sequence":…}
Different shapes, different symbol conventions (XXBTZUSD vs BTC-USD), and
one genuine trap: Coinbase’s simpler /ticker endpoint reports last-trade
size, not resting depth — feed that to a size slider and it lies silently.
This monitor uses the /book endpoint on both venues, so the depth the slider
tests is depth that was actually resting there.
Each venue lives behind a VenueAdapter — one URL plus one parse function —
normalising to a common BookTop { venue, bid, bidQty, ask, askQty, ts }.
Everything above the seam (the maths, the chart, the sliders) cannot tell the
venues apart, which is the same instinct as Echo’s TTSBackend and the OMS’s
injected clock: isolate the thing that varies behind one interface. A third
venue is a one-file change — deliberately not made until there’s a reason.
What this page refuses to do
No server, no schedule, no keys. The recorded window is a committed build artifact; live data comes from your browser calling the venues’ public endpoints directly. A static page cannot hold a secret, so it holds none — and it never touches a trading-authenticated endpoint. Everything is paper.
No verdicts in the data. The dataset stores raw book snapshots, never conclusions — if it stored “3 opportunities,” the sliders would have nothing to recompute. Fee and size are pure functions of a snapshot, which is why dragging a slider re-derives the entire window instantly with no refetch.
No hidden failure. Every failure mode renders as a labelled state — a geo-block, a rate limit, a timeout, a malformed payload, stale data — and if either venue is unreachable, the pair comparison is impossible, so the page says so and falls back to the recorded window as a whole. A visitor behind a corporate proxy gets the same working page as one who reaches both venues; the banner just tells the truth about which they are looking at.
No long-run claims. Without accumulated history, this page cannot honestly say “412 apparent opportunities this week, 3 real” — so it doesn’t. Every number is scoped to the 15-minute recorded window or your live session. The lesson survives the honesty: the sliders demonstrate why apparent spreads aren’t real, over any window you hand them.