เคสลูกค้า: ทีม Quant สตาร์ทอัพในกรุงเทพฯ ที่ลดบิลได้เกือบ 84% ใน 30 วัน

เมื่อต้นปี 2026 ทีม Quant สตาร์ทอัพแห่งหนึ่งย่านอโศม กรุงเทพฯ ที่กำลังรัน market-making bot บนคู่เทรด BTC-USDT, ETH-USDT และ SOL-USDT พบว่า pipeline การวิเคราะห์ L2 order book ของตัวเองเริ่มเป็นปัญหา:

L2 Depth Snapshot คืออะไร และทำไมบอทเทรดถึงต้องสนใจ

L2 depth snapshot คือ ภาพนิ่งของ order book ทั้งฝั่ง bid และ ask ณ เวลาใดเวลาหนึ่ง ต่างจาก L1 ที่เห็นเพียงราคา top-of-book L2 จะให้ความลึกหลายระดับ เช่น 20, 50, 200 หรือ 1,000 ด่าน ซึ่งจำเป็นสำหรับการคำนวณ:

ทั้ง 3 exchange รองรับ L2 ผ่าน REST snapshot (เหมาะกับ bootstrap และ replay) และ WebSocket diff stream (เหมาะกับ live) แต่ endpoint, schema และ rate-limit ต่างกันอย่างมีนัยสำคัญ

เปรียบเทียบ Endpoint สำหรับดึง L2 Snapshot

มิติBinanceOKXBybit
Endpoint/api/v3/depth/api/v5/market/books/v5/market/orderbook
Max levels ต่อ request5,000 (param limit)400 (param sz, ต้องรวมหลายหน้า)200 (param limit)
Rate limit (IP)6,000 น้ำหนัก/นาที (~120 req ที่ limit 1000)20 req/2s ต่อ endpoint600 req/5s
Update ID fieldlastUpdateIddata[0].ts + checksumresult.u + result.seq
p50 latency (ภายในเอเชีย)~25 ms~45 ms~60 ms
Success rate (24h)99.94%99.81%99.72%

ตัวอย่าง async fetcher ที่ดึงพร้อมกันทั้ง 3 exchange และ normalize เป็น schema เดียว:

import aiohttp, asyncio, time

ENDPOINTS = {
    "binance": "https://api.binance.com/api/v3/depth",
    "okx":     "https://www.okx.com/api/v5/market/books",
    "bybit":   "https://api.bybit.com/v5/market/orderbook",
}

def ok_symbol(ex, sym):
    # Binance/Bybit = BTCUSDT, OKX = BTC-USDT
    return sym if ex == "okx" else sym.replace("-", "")

async def fetch_depth(session, ex, symbol, limit=200):
    base = ENDPOINTS[ex]
    if ex == "binance":
        url = f"{base}?symbol={ok_symbol(ex, symbol)}&limit={min(limit,1000)}"
    elif ex == "okx":
        url = f"{base}?instId={ok_symbol(ex, symbol)}&sz={min(limit,400)}"
    else:  # bybit
        url = f"{base}?category=spot&symbol={ok_symbol(ex, symbol)}&limit={min(limit,200)}"
    t0 = time.perf_counter()
    async with session.get(url, timeout=aiohttp.ClientTimeout(total=2)) as r:
        data = await r.json()
    latency_ms = (time.perf_counter() - t0) * 1000
    return ex, symbol, data, latency_ms

async def snapshot_triplet(symbol="BTC-USDT"):
    async with aiohttp.ClientSession() as s:
        results = await asyncio.gather(
            fetch_depth(s, "binance", symbol),
            fetch_depth(s, "okx",     symbol),
            fetch_depth(s, "bybit",   symbol),
            return_exceptions=True,
        )
    for r in results:
        if isinstance(r, Exception):
            print("ERR", r); continue
        ex, sym, data, ms = r
        print(f"{ex:7s} {sym:10s} {ms:6.1f}ms  bids={len(data.get('bids', data.get('data',[{}])[0].get('bids',[])))}")

asyncio.run(snapshot_triplet())

เปรียบเทียบ Schema และข้อจำกัดที่มักถูกมองข้าม