比較最佳價格:五間交易所的階段 1

套利課程第 4 課。買賣價差、手續費、報價貨幣和單位,以及一個掃描五間交易所並回報哪些價差扣完手續費後仍存在的短 Python 程式。

課程「建立自己的套利機器人」第 4 課。今天你機器人的階段 1 開始用真實資料運作。

我們要做什麼

第 1 課比較一枚幣在兩間交易所的價格。真正機器人的階段 1 會比較每一對交易所和許多枚幣,並且只回報扣掉手續費後仍為正數的價差。今天它涵蓋五間交易所和一枚幣,你會看到能留下的價差有多少。

讓價差看起來存在的三個陷阱

寫程式前,先認識三種會產生不存在價差的情況。

陷阱 1:手續費。每間交易所都會從每筆交易收取一個百分比,當你吃掉既有委託時,這叫吃單手續費。假設買方手續費是 0.1%,賣方手續費是 0.26%。那麼價差至少要超過 0.36% 才能打平。

陷阱 2:報價貨幣。價格會用某一種貨幣表示。BTC/USDT 是用與美元掛鉤的幣 USDT 報價。BTC/USD 是用真正的美元報價。這兩個不是同樣的價格。USDT 的交易價格會略高或略低於一美元,有時差得更多。如果你不先換算就比較一間交易所的 BTC/USDT 和另一間的 BTC/USD,「價差」可能只是在反映 USDT 與美元的差異。規則:比較使用相同報價貨幣的價格。

陷阱 3:單位。一間交易所可能報一枚幣的價格,另一間可能報一千枚幣的價格,還有少數會報一份有自己規格的合約價格。一定要確認一個單位代表什麼。

公式

在交易所 X 買進、交易所 Y 賣出時:

gap in percent = (bid on Y − ask on X) / ask on X × 100

net gap = gap − fee on X − fee on Y

如果淨價差高於零,這一對就通過階段 1。

計算例子。X 的賣價是 100.00,X 的手續費是 0.10%。Y 的買價是 100.50,Y 的手續費是 0.10%。 Gap = (100.50 − 100.00) / 100.00 × 100 = 0.50%。 Net gap = 0.50 − 0.10 − 0.10 = 0.30%。這一對通過。

同樣的價格,但 Y 的手續費是 0.40%:net gap = 0.50 − 0.10 − 0.40 = 0.00。它不能通過。

程式

這支程式向五間交易所詢問 BTC/USDT 的最佳買價與賣價,嘗試每一個有順序的交易所配對,並印出最好的五個。

這是直接硬做的方法:兩層迴圈,所有人對上所有人,每次執行時每間交易所各發出一個網頁請求。我們刻意這樣寫。這是最短、能呈現問題的程式,而你會在下一節感受到它的限制。不要把這個設計用在正式環境。

import json, urllib.request

def get(url):
    req = urllib.request.Request(url, headers={"User-Agent": "course-scanner"})
    with urllib.request.urlopen(req, timeout=10) as r:
        return json.load(r)

def binance(s):  d = get(f"https://api.binance.com/api/v3/ticker/bookTicker?symbol={s}USDT"); return float(d["bidPrice"]), float(d["askPrice"])
def kraken(s):   d = next(iter(get(f"https://api.kraken.com/0/public/Ticker?pair={s}USDT")["result"].values())); return float(d["b"][0]), float(d["a"][0])
def okx(s):      d = get(f"https://www.okx.com/api/v5/market/ticker?instId={s}-USDT")["data"][0]; return float(d["bidPx"]), float(d["askPx"])
def bitstamp(s): d = get(f"https://www.bitstamp.net/api/v2/ticker/{s.lower()}usdt/"); return float(d["bid"]), float(d["ask"])
def coinbase(s): d = get(f"https://api.exchange.coinbase.com/products/{s}-USDT/ticker"); return float(d["bid"]), float(d["ask"])

# 名稱:(函式、吃單手續費百分比)。這些手續費只是範例:請查看每間交易所的手續費頁面。
VENUES = {"binance": (binance, 0.10), "kraken": (kraken, 0.26), "okx": (okx, 0.10),
          "bitstamp": (bitstamp, 0.30), "coinbase": (coinbase, 0.40)}

def scan(symbol):
    quotes = {}
    for name, (fn, fee) in VENUES.items():
        try:
            quotes[name] = fn(symbol)
        except Exception as e:
            print("skip", name, "-", type(e).__name__)
    rows = []
    for buy, (_, ask) in quotes.items():
        for sell, (bid, _) in quotes.items():
            if buy == sell:
                continue
            gap = (bid - ask) / ask * 100
            fees = VENUES[buy][1] + VENUES[sell][1]
            rows.append((gap - fees, gap, buy, sell))
    rows.sort(reverse=True)
    for net, gap, buy, sell in rows[:5]:
        print(f"buy {buy:9} sell {sell:9} gap {gap:+.3f}%  after fees {net:+.3f}%")

scan("BTC")

把它存成 scan5.py,然後執行 python3 scan5.py。

應該看到什麼結果

五行。一次測試執行印出這些內容:

buy binance   sell okx       gap +0.004%  after fees -0.196%
buy okx       sell binance   gap -0.004%  after fees -0.204%
buy binance   sell kraken    gap +0.003%  after fees -0.357%

你的數字會不同,但情況會一樣:價差非常小,只有百分之幾的千分之一,而手續費大兩個數量級,因此每個淨價差都是負數。這是大型幣在大型交易所進行階段 1 的誠實第一個結果。如果你看到高於零的淨價差,先別慶祝。檢查三件事:報價貨幣、單位,以及報價是否新鮮(已經一分鐘沒更新的交易場所顯示的是舊價格)。

如果跳過某間交易所,它可能封鎖了你的國家。把它從 VENUES 移除,或換掉它。

為什麼正式環境不能這樣做

這個直接硬做的程式能運作,對五間交易所和一枚幣也沒問題。現在來算算機器人成長後要付出多少成本。

  • 要比較的配對:有 N 間交易所時,就有 N × (N − 1) 個有順序的配對。5 間交易所有 20 個。30 間交易所有 870 個。每一枚幣都是如此。
  • 請求:程式每次執行都下載新的報價,每間交易所、每枚幣各發出一個網頁請求。30 間交易所和 200 枚幣,每次掃描就是 6000 個請求。交易所會限制請求並封鎖你,而且 6000 個請求要花好幾分鐘,一個價差卻可能只存在幾秒。
  • 過時的價格:最後一個請求回來時,第一個報價已經舊了,所以比較混合了不同時間點的價格。

正式環境的機器人會改做這些事,簡單說:

  1. 它會和每間交易所保持即時連線(websocket),在每次價格變動時接收資料,因此完全不輪詢。
  2. 它會把每間交易所、每枚幣的最佳買價和最佳賣價存放在記憶體中。
  3. 對一枚幣來說,它只需要所有交易所中的最高買價和最低賣價:掃過 N 個報價一次即可,而不是做 N × (N − 1) 次比較。即使最好的買價減去最好的賣價,都無法超過最便宜那一對的手續費,就不必檢查其他任何組合。

今天你不會把這些全部做出來。重點是了解簡單程式隱藏了什麼,這樣當你的機器人變慢或被封鎖時,你就知道該替換哪個部分。第 9 到第 12 課會在機器人成為完整系統時回到這件事。

真正出現價差的地方

小型、交易不頻繁的幣,在小型交易所和價格快速變動時,價差會更大。手續費較低時,價差也會更大。許多交易所會給交易量大或持有交易所自家代幣的人較低費率。專業機器人的費率等級不是你第一天能取得的。這也是明顯交易對的競爭如此困難的原因之一。

讓 AI 代理人撰寫並擴充它

一個模糊的要求:

比較多間交易所的價格。

一個包含重要細節的要求:

擴充 scan5.py。如果交易所有提供時間戳記,加入一個函式跳過超過 5 秒的報價。把手續費和幣種清單放在 config.py。掃描 BTC、ETH 和 SOL。只印出淨價差高於 0 的配對;如果沒有,就印出最好的三組配對並加上 NEAR 這個詞。不要加入任何交易程式碼。把輸出顯示給我。

接著手算檢查:從程式顯示的兩個價格中,挑一行印出的結果,用計算機計算 (bid − ask) / ask × 100,並和 gap 欄比較。它必須精確到小數點後三位都一致。

你的練習

  1. 每隔一分鐘執行 scan5.py 五次。有幾次任何淨價差高於零?
  2. 把 VENUES 的兩邊手續費都改成 0.02%(非常大型交易者的等級)。現在有任何配對能通過嗎?這告訴你誰能從這種價差獲利?
  3. 再加入一枚幣,例如 ETH。它最佳的價差比 BTC 的大還是小?
  4. 將你的檔案複製一份,存為 scan5_v1.py。這就是本課的已保存版本。

接下來

第 4 課比較了交易所。在第 5 課,我們換一個問題:如果價差不是存在於兩間交易所之間,而是在同一間交易所中一圈貨幣周圍呢?為此,必須把價格畫成一張圖。