สัปดาห์ที่แล้วผมนั่งเขียนบอท delta-neutral market-making ให้ลูกค้าที่เป็น prop trading ขนาดเล็กในสิงคโปร์ โจทย์คือต้องดึง orderbook จาก Binance (CLOB ตรง) และ Hyperliquid (perp DEX on-chain) ในเวลาเดียวกัน เพื่อหา spread ที่เป็นไปได้ระหว่าง BTC-PERP ทั้งสองเวที ผมตกม้าตายตั้งแต่ข้อแรก: subscribe ได้ แต่ parse ออกมาแล้วโครงสร้างไม่เหมือนกันเลย บทความนี้คือบันทึกการแก้ปัญหาจริง พร้อมโค้ดตัวอย่างที่ copy รันได้ทันที
1. ภาพรวมความแตกต่างเชิงสถาปัตยกรรม
Binance เป็นการแลกเปลี่ยนแบบรวมศูนย์ที่ใช้ matching engine ภายใน ส่วน Hyperliquid เป็น L1 blockchain ของตัวเองที่ใช้ HyperBFT consensus ทำให้ message format, ความถี่อัปเดต และการรับประกัน ordering ต่างกันโดยสิ้นเชิง แม้ทั้งคู่จะห่อหุ้มด้วย JSON ผ่าน WebSocket ก็ตาม
- Binance: ส่ง partial book ทุก 100ms หรือ diff ทุก event ขึ้นกับ subscription
- Hyperliquid: ส่ง L2 snapshot เต็มทุก event เพราะต้นทุน matching อยู่บนเชน
- Binance ใช้ symbol ตัวพิมพ์เล็กทั้งหมด เช่น
btcusdt; Hyperliquid ใช้ base asset ตัวพิมพ์ใหญ่ เช่นBTC - Binance เก็บ price เป็น string ทศนิยม 8 ตำแหน่ง (ความแม่นยำระดับ tick); Hyperliquid ใช้ tick ที่ผูกกับ asset metadata
2. สกีมา Binance WebSocket OrderBook
Binance มี endpoint หลักคือ depth20@100ms (partial book stream) และ depth@100ms (diff stream) ตัวอย่าง payload ที่ได้รับ:
{
"lastUpdateId": 160,
"bids": [
["65000.10000000", "0.50000000"],
["65000.05000000", "1.20000000"]
],
"asks": [
["65000.50000000", "0.30000000"],
["65000.80000000", "0.80000000"]
]
}
import asyncio, json, websockets
async def binance_orderbook(symbol="btcusdt", depth=20):
url = f"wss://stream.binance.com:9443/ws/{symbol}@depth{depth}@100ms"
async with websockets.connect(url, ping_interval=20) as ws:
async for msg in ws:
data = json.loads(msg)
return {
"venue": "binance",
"symbol": data["s"],
"bids": [[float(p), float(q)] for p, q in data["bids"]],
"asks": [[float(p), float(q)] for p, q in data["asks"]],
"updateId": data["lastUpdateId"],
}
3. สกีมา Hyperliquid WebSocket OrderBook
Hyperliquid ใช้ channel l2Book ที่ห่อ state ไว้ใน field data.levels โดย levels[0] คือ bids และ levels[1] คือ asks แต่ละ level เป็น array ของ object {px, sz, n} ที่ n คือจำนวน order รวม ณ price นั้น
{
"channel": "l2Book",
"data": {
"coin": "BTC",
"time": 1700000000000,
"levels": [
[
{"px": "65000.1", "sz": "0.5", "n": 1},
{"px": "65000.0", "sz": "1.2", "n": 2}
],
[
{"px": "65000.5", "sz": "0.3", "n": 1},
{"px": "65000.8", "sz": "0.8", "n": 1}
]
]
}
}
import asyncio, json, websockets
async def hyperliquid_orderbook(coin="BTC"):
url = "wss://api.hyperliquid.xyz/ws"
sub = {"method": "subscribe",
"subscription": {"type": "l2Book", "coin": coin}}
async with websockets.connect(url, ping_interval=20) as ws:
await ws.send(json.dumps(sub))
async for msg in ws:
data = json.loads(msg)
if data.get("channel") != "l2Book":
continue
lvls = data["data"]["levels"]
return {
"venue": "hyperliquid",
"coin": data["data"]["coin"],
"bids": [[float(l["px"]), float(l["sz"])] for l in lvls[0]],
"asks": [[float(l["px"]), float(l["sz"])] for l in lvls[1]],
"time": data["data"]["time"],
}
4. ตารางเปรียบเทียบสกีมาแบบ Field-by-Field
| มิติ | Binance | Hyperliquid |
|---|---|---|
| Endpoint | wss://stream.binance.com:9443/ws | wss://api.hyperliquid.xyz/ws |
| Subscription | ระบุใน URL path | ส่ง JSON method: subscribe |
| Symbol format | lowercase + quote (เช่น btcusdt) | base asset uppercase (เช่น BTC) |
| Update mode | partial (ทุก 100ms) / diff | full snapshot ทุก event |
| Price field | bids[][0], asks[][0] = string | levels[][][i].px = string |
| Size field | bids[][1], asks[][1] = string | levels[][][i].sz = string |
| Synthetic field | ไม่มี | n = จำนวน order รวม ณ price |
| Timestamp | ไม่มีใน partial book (มีใน depthUpdate) | data.time = unix ms |
| Top-level key | lastUpdateId | channel + data |
| Heartbeat | client ต้องส่ง ping ทุก 30s | server ส่ง pong อัตโนมัติ |
5. โค้ดรวมสกีมาด้วย HolySheep AI
เมื่อต้อง normalize payload จำนวนมากเพื่อเทรนโมเดลหา mispricing ผมใช้โมเดล DeepSeek V3.2 ผ่าน HolySheep AI (gateway เดียวที่ชำระด้วย WeChat/Alipay ได้ และ latency ต่ำกว่า 50ms) แทนที่จะเขียน parser เองหลายรอบ:
import requests, json
API_URL = "https://api.holysheep.cn/v1/chat/completions"
HEADERS = {
"Authorization": "Bearer YOUR_HOLYSHEEP_API_KEY",
"Content-Type": "application/json",
}
def normalize_orderbook(raw_binance, raw_hyper):
prompt = (
"แปลง orderbook ทั้งสองรูปแบบนี้ให้เป็น JSON array เดียวกัน "
"โดยแต่ละ row มีฟิลด์: venue, side, price, size, ts.\n"
f"Binance: {json.dumps(raw_binance)}\n"
f"Hyperliquid: {json.dumps(raw_hyper)}\n"
"ตอบเป็น JSON array เท่านั้น ไม่ต้องมีคำอธิบาย"
)
body = {
"model": "deepseek
แหล่งข้อมูลที่เกี่ยวข้อง