DEV Community

네이쳐스테이
네이쳐스테이

Posted on

I Built a Cross-Exchange Crypto Arbitrage Scanner in Python (Here's the Order-Book Math)

The bots didn't kill arbitrage. They made it invisible.

Quick question: if crypto arbitrage "died" when bots took over in 2021, why do cross-exchange BTC spreads still open to 0.4%–1.2% for 30–90 seconds during high-volume hours in 2026?

They didn't die. They got fast. A spread that a human eyeballing charts will never catch is trivially detectable if you poll two order books over WebSocket and diff them programmatically. This post is about the engineering of that detection — not a get-rich pitch.

Not financial advice. The numbers below describe order-book behavior, not guaranteed returns. Fees, withdrawal delays, and slippage routinely erase raw spreads. Treat this as a systems problem, not an income promise.

Why the gap exists: liquidity fragmentation

There are 250+ active exchanges in 2026, each pricing BTC off its own order book. When a large sell hits Kraken but not Coinbase, the two prices diverge until arbitrageurs converge them. Counterintuitively, the widest, most-repeatable gaps aren't on dead altcoins — they're on the most liquid pairs (BTC/USDT) during volume spikes, because big orders move faster than the correction.

That makes it a latency + data problem, which is exactly the kind of thing you solve with an API and a loop.

The scanner core (Python + ccxt)

ccxt gives you one interface across 100+ exchanges. Here's the minimal spread detector — it fetches top-of-book from three venues and flags anything above your fee threshold:

python
import ccxt
import asyncio
import ccxt.async_support as ccxt_async

EXCHANGES = ["binance", "kraken", "coinbase"]
SYMBOL = "BTC/USDT"
FEE_PER_SIDE = 0.001 # ~0.1% taker
MIN_MARGIN = 0.003 # 0.3% edge before we care

async def top_of_book(ex_id):
ex = getattr(ccxt_async, ex_id)()
try:
ob = await ex.fetch_order_book(SYMBOL, limit=1)
return ex_id, ob["bids"][0][0], ob["asks"][0][0] # best bid, best ask
finally:
await ex.close()

async def scan():
books = await asyncio.gather(*(top_of_book(e) for e in EXCHANGES))
# buy at lowest ask, sell at highest bid
buy = min(books, key=lambda b: b[2]) # lowest ask
sell = max(books, key=lambda b: b[1]) # highest bid
gross = (sell[1] - buy[2]) / buy[2]
net = gross - 2 * FEE_PER_SIDE
if net > MIN_MARGIN:
print(f"SIGNAL buy {buy[0]}@{buy[2]:.0f} "
f"sell {sell[0]}@{sell[1]:.0f} net={net*100:.2f}%")
return net

asyncio.run(scan())

The critical line is net = gross - 2 * FEE_PER_SIDE. Most "arbitrage" that looks profitable is fee-negative. You subtract both legs' fees before the signal fires, or you bleed capital on every trade.

Don't transfer coins — pre-position inventory

The rookie mistake is buying on Exchange A and transferring to Exchange B. On-chain confirmation kills the spread long before it lands. Instead, hold both sides of your inventory on both venues:

  • Split capital as, say, USDT on one, BTC on the other
  • Buy on the cheap venue, simultaneously sell existing BTC on the expensive venue
  • Rebalance inventory later, off the hot path

You never wait for a coin to "go up" — you capture the gap regardless of direction. That's the whole point of market-neutral arbitrage.

Production skeleton: Hummingbot + n8n

For execution, most solo operators wire API keys into Hummingbot (free, open-source) running its cross_exchange_market_making strategy rather than hand-rolling order management. Realistic first-time setup is 3–4 hours including paper-mode testing.

For observability, an n8n workflow beats staring at logs:

[Schedule: every 10s]
-> [HTTP Request: your /spread endpoint]
-> [IF: net_margin > 0.003]
-> [Discord/Telegram: alert]
-> [GPT-4 node: summarize why the gap opened
(volume spike? single large order?)]

The GPT-4 node is genuinely useful here: feed it the order-book delta and recent trades, and ask it to classify why a spread opened. Over weeks you build a labeled dataset of which conditions produce durable vs. instantly-closing gaps — which is the actual edge.

Practical takeaways

  1. Subtract fees before the signal, not after. net = gross - 2*fee is non-negotiable.
  2. Pre-position inventory on every venue. On-chain transfers are too slow to arbitrage.
  3. Paper-trade first. Hummingbot's paper mode costs $0 and exposes your latency reality.
  4. Instrument everything. An n8n + LLM loop turns raw signals into a learnable dataset.
  5. Backtest your latency, not just your logic. A 0.5% spread that closes in 40s is unreachable if your round-trip is 45s.

The interesting part was never the money. It's that a 40-line async loop sees market structure that no human refreshing a chart ever will.


Want the done-for-you AI automation templates from this post? Get the NSST AI toolkit.

Top comments (0)