Tôi đã dành ba tuần cuối tháng 11 để đối chiếu dữ liệu tick-by-tick từ ba sàn lớn nhất cho một chiến lược grid trading chạy trên máy chủ riêng. Vấn đề lớn nhất không phải là lấy dữ liệu thế nào mà là làm sao để mọi sàn nói cùng một ngôn ngữ: schema khác nhau, múi giờ khác nhau, rate-limit khác nhau và quan trọng nhất là API của Bybit gần như không có endpoint public cho lịch sử trades quá 200 dòng. Bài viết này tổng hợp lại toàn bộ pipeline tôi đã chạy ổn định suốt 14 ngày liên tục, từ cách kéo CSV batch cho đến cách đổ vào TimescaleDB / DuckDB để truy vấn tức thì.
Tổng quan ba sàn và endpoint khả dụng
| Sàn | Endpoint lịch sử trades | Giới hạn / lần gọi | Phạm vi lùi tối đa | Định dạng |
|---|---|---|---|---|
| Binance Spot | /api/v3/trades, /api/v3/historicalTrades | 1000 dòng / request | Không giới hạn qua historicalTrades (cần API key) | JSON |
| OKX Spot | /api/v5/market/trades-history | 500 dòng / request | ~3 tháng | JSON (CSV cần tự chuyển) |
| Bybit Spot v5 | /v5/market/recent-trade, /v5/market/history-trade | 1000 dòng / request | Không giới hạn (cần auth cho lượng lớn) | JSON |
Đánh giá thực tế của tôi sau 14 ngày chạy song song ba pipeline trên VPS Frankfurt (1 vCPU, 2GB RAM, giá 4 USD/tháng):
- Độ trễ trung bình mỗi request: Binance 87 ms, OKX 112 ms, Bybit 134 ms (đo bằng
aiohttp+time.perf_counter). - Tỷ lệ thành công 24h: Binance 99.4%, OKX 98.7%, Bybit 97.1% (do rate-limit Bybit siết hơn vào khung giờ Á-Âu).
- Tiện thanh toán & độ phủ mô hình: không áp dụng trực tiếp cho việc tải CSV, nhưng khi tôi đẩy dữ liệu qua LLM để sinh tín hiệu, tôi đã dùng HolySheep AI vì cước tính theo ¥1=$1 giúp tiết kiệm 85%+ so với thanh toán thẻ quốc tế.
- Trải nghiệm bảng điều khiển: Binance UI rõ ràng cho export, OKX yêu cầu dựng script riêng, Bybit gần như không có UI cho tick data.
Code 1 — Batch download CSV từ Binance Spot
Đoạn script dưới dùng historicalTrades để lấy tick lịch sử từng trang một, ghi nối tiếp vào một file CSV duy nhất. Bạn cần API key có quyền đọc lịch sử.
import csv
import time
import requests
from pathlib import Path
API_KEY = "YOUR_BINANCE_API_KEY"
BASE = "https://api.binance.com"
SYMBOL = "BTCUSDT"
OUT = Path(f"{SYMBOL.lower()}_binance_trades.csv")
def fetch_page(symbol: str, from_id: int | None = None, limit: int = 1000):
params = {"symbol": symbol, "limit": limit}
if from_id is not None:
params["fromId"] = from_id
r = requests.get(f"{BASE}/api/v3/historicalTrades",
params=params,
headers={"X-MBX-APIKEY": API_KEY},
timeout=10)
r.raise_for_status()
return r.json()
def stream_to_csv(symbol: str, max_pages: int = 500):
write_header = not OUT.exists()
with OUT.open("a", newline="") as f:
w = csv.writer(f)
if write_header:
w.writerow(["trade_id", "price", "qty", "quote_qty",
"time_ms", "is_buyer_maker", "symbol"])
last_id = None
for page in range(max_pages):
data = fetch_page(symbol, last_id)
if not data:
break
for d in data:
w.writerow([d["id"], d["price"], d["qty"], d["quoteQty"],
d["time"], int(d["isBuyerMaker"]), symbol])