What Arbitrage Is: Your First Scanner in 10 Minutes

Lesson 1 of the arbitrage course. What arbitrage means, why price gaps exist, and a ten-line Python scanner that compares the best bid and best ask of one coin on two exchanges.

Lesson 1 of the course "Arbitrage: from zero to your own scanner". You need no trading experience. You need Python and ten minutes.

An example with a pencil

Imagine two shops on one street. Shop A sells a sneaker pair for 100 dollars. Shop B buys the same pair from anyone for 108 dollars. You buy in A, walk twenty meters, sell in B, and keep 8 dollars. You took no bet on whether sneakers get more or less popular. The only thing you needed was to notice that the two prices disagree.

That is arbitrage: buy something where it is cheap and sell the same thing where it is expensive, at the same moment, and keep the difference.

Two parts of that sentence matter:

  • "the same thing": the pair of sneakers is identical in both shops;
  • "at the same moment": if you wait a week, the prices may change and the gap may be gone.

Why do gaps exist?

On a crypto exchange, thousands of people and programs buy and sell Bitcoin every second. Each exchange has its own crowd, so each one has a slightly different price. News reaches them at slightly different times. Money moves between them slowly. So the price of the same coin on two exchanges is almost the same, but rarely exactly the same.

Programs that watch these gaps and close them are the reason the gaps stay small. You will build a very small version of such a program today.

Two words you must know: bid and ask

An exchange keeps a list of offers. People who want to buy say at which price they would buy. People who want to sell say at which price they would sell.

  • Bid is the highest price someone is ready to pay for the coin right now.
  • Ask is the lowest price someone is ready to sell the coin for right now.

If you want to buy immediately, you pay the ask. If you want to sell immediately, you receive the bid. The ask is always higher than the bid on the same exchange. The distance between them is called the spread. It is the exchange's built-in price of doing business.

A tiny example with numbers:

Bid (you sell here)Ask (you buy here)
Exchange A100.00100.10
Exchange B100.30100.40

Buy on A at its ask, 100.10. Sell on B at its bid, 100.30. The gap is 100.30 − 100.10 = 0.20 per coin. On 1000 coins that is 200 before fees.

Now flip it: buy on B at 100.40, sell on A at 100.00. You lose 0.40 per coin. So a gap exists in only one direction, if at all.

The rule in one line:

Gap = bid where you sell − ask where you buy.

If the gap is positive, there is something to look at. If it is zero or negative, there is nothing.

Three honest warnings before the code

  1. Fees. Every exchange takes a small percentage of each trade. A gap of 0.20 on a price of 100 is 0.2 percent. If the two fees together are 0.2 percent, the profit is zero. A later lesson covers fees carefully.
  2. Size. The best bid and best ask are real only for a small amount of coin. If you want to trade more, you "eat" the next offers at worse prices. Lessons 9 and 10 cover this.
  3. Speed. By the time you see a gap, someone else may have closed it. Lesson 11 covers this.

Today's program is a thermometer: it shows whether a gap exists, and says nothing about whether you could earn from it.

The mini-scanner

This scanner is deliberately the head-on version: one request per exchange, one subtraction, no fees, no sizes, no error handling. It is the simplest thing that works, and it is wrong in ways you will find in the next lessons (fees in lesson 4, order book depth in lesson 9, timing in lesson 11). Build the blunt version first, so that each later fix answers a problem you have already seen.

The program asks two exchanges for the best bid and best ask of the same coin and prints the gap in both directions. It uses only public data. It needs no account, no API key and no money.

import json
import urllib.request

def get(url):
    with urllib.request.urlopen(url, timeout=10) as r:
        return json.load(r)

# Exchange A: Binance, coin pair BTC / USDT
a = get("https://api.binance.com/api/v3/ticker/bookTicker?symbol=BTCUSDT")
a_bid, a_ask = float(a["bidPrice"]), float(a["askPrice"])

# Exchange B: Kraken, the same coin
b = get("https://api.kraken.com/0/public/Ticker?pair=XBTUSDT")
b = next(iter(b["result"].values()))
b_bid, b_ask = float(b["b"][0]), float(b["a"][0])

print("A bid/ask:", a_bid, a_ask)
print("B bid/ask:", b_bid, b_ask)
print("buy A, sell B:", round(b_bid - a_ask, 2))
print("buy B, sell A:", round(a_bid - b_ask, 2))

Save it as scanner.py and run python3 scanner.py.

What result you should see

You will see four prices and two numbers. For Bitcoin on two large exchanges, both numbers are usually negative or close to zero. Sometimes one of them is a small positive number. A test run gave bid and ask near 75,782 on one exchange and 75,786 on the other, so one direction showed +3.89 and the other −4.00. The positive one is 3.89 / 75,782 = 0.005 percent, far below the fees of about 0.2 percent. This is normal and it is the main lesson: on liquid coins at liquid exchanges, the market is efficient, and a gap big enough to pay the fees is rare and short.

Run the program a few times, a minute apart. Watch how the two numbers move. Write down the best one you see.

If one request fails, the exchange may block your country or your network. Replace it with another exchange that has a public price endpoint. The shape of the program stays the same.

Let an AI agent do the typing

In this course you build your own arbitrage bot together with an AI agent such as Claude Code or Codex. The agent writes the code. You decide what to build and you check the result. Checking is the part that cannot be handed over: an agent can print a confident number that is wrong.

Compare two ways to ask for today's program.

A vague request:

Make me an arbitrage scanner.

The agent has to guess everything: which exchanges, which coin, what "scanner" prints, whether fees count. It may build something large that you cannot read.

A request that names what you need:

Write scanner.py in Python with no extra libraries. Fetch the best bid and best ask of BTC/USDT from Binance and from Kraken using their public REST endpoints. Print both quotes and the gap in both directions as bid_sell minus ask_buy. No API keys, no trading. Keep the code under 40 lines and put the symbol at the top of the file.

With this request you can check the answer yourself: the gap in each direction must equal one bid minus one ask, and you can verify it with a calculator. Keep that habit for the whole course. Ask for a small piece, run it, check one number by hand, then save a version (a git commit, or just a copy of the file).

Your exercise

  1. Run the scanner three times and write down the four prices and both gaps each time.
  2. Change BTCUSDT and XBTUSDT to another coin that both exchanges list (for example ETH). Is the gap larger or smaller than for Bitcoin? Smaller, less traded coins usually have wider gaps. Think about why.
  3. Compute by hand: the gap as a percentage of the buy price, gap / ask * 100. Compare it with a fee of 0.1 percent on each exchange, 0.2 percent in total. Is any of your three runs above that line?

What comes next

Today you compared two numbers. A real arbitrage system does the same thing for thousands of pairs and many exchanges, then checks the best candidates in detail. Lesson 2 shows the different kinds of arbitrage, and lesson 3 shows the stages that every system goes through.