Tôi vẫn nhớ rõ ngày đầu tiên thử lấy dữ liệu Order Book từ Binance Futures để build chiến lược arbitrage. Kết quả nhận được không phải data đẹp đẽ như mong đợi mà là một loạt lỗi: ConnectionError: timeout, rồi 401 Unauthorized, và cuối cùng là 429 Too Many Requests. Mất 3 ngày debug, đọc tài liệu rải rác khắp internet, và test đi test lại — tôi mới thực sự hiểu cách Binance API hoạt động đúng cách.
Bài viết này là tổng hợp toàn bộ kiến thức tôi đã đúc kết được, giúp bạn tránh những sai lầm tương tự và bắt đầu lấy dữ liệu Order Book một cách hiệu quả ngay hôm nay.
Tại sao Order Book Data quan trọng với nhà giao dịch?
Order Book là bản đồ lực cung - cầu của thị trường. Nó cho bạn biết chính xác:
- Bid/Ask Depth: Khối lượng chờ mua/bán ở mỗi mức giá
- Spread: Chênh lệch giữa giá cao nhất mua và thấp nhất bán
- Whale Activity: Phát hiện các lệnh lớn có thể ảnh hưởng giá
- Market Liquidity: Đánh giá độ sâu thị trường để đặt lệnh slippage thấp
Với HolySheep AI, bạn có thể xử lý data này với độ trễ dưới 50ms — đủ nhanh cho hầu hết chiến lược giao dịch.
Binance API Foundations - Hiểu cách hệ thống hoạt động
Các loại endpoint Order Book trên Binance
Binance cung cấp 2 loại Order Book API chính:
| Endpoint Type | Use Case | Rate Limit | Độ trễ |
|---|---|---|---|
| REST /fapi/v1/depth | Lấy snapshot 1 lần | 1200 requests/phút | ~100-500ms |
| WebSocket streams | Real-time update | Không giới hạn | ~10-50ms |
| Combined streams | Nhiều cặp cùng lúc | 5 connections/IP | ~20-100ms |
Code Setup - Khởi tạo Project
Trước tiên, hãy setup môi trường Python với các thư viện cần thiết:
# Cài đặt dependencies
pip install websockets pandas numpy aiohttp python-dotenv
Tạo file config để lưu API credentials
QUAN TRỌNG: Không bao giờ commit file này lên git!
touch .env
echo "BINANCE_API_KEY=your_api_key_here" >> .env
echo "BINANCE_SECRET_KEY=your_secret_key_here" >> .env
Lưu ý quan trọng: Đối với việc đọc public Order Book data, bạn KHÔNG cần API key. Chỉ cần key khi giao dịch thật hoặc lấy account-specific data. Nếu bạn đang phát triển strategy và cần xử lý data nhanh hơn, có thể tích hợp HolySheep AI với chi phí chỉ $0.42/MTok cho model xử lý data phức tạp.
Method 1: REST API - Lấy Order Book Snapshot
Đây là cách đơn giản nhất để lấy dữ liệu Order Book tại một thời điểm:
import requests
import json
import time
class BinanceOrderBook:
"""Lấy Order Book data từ Binance Futures API"""
BASE_URL = "https://fapi.binance.com"
def __init__(self, symbol="BTCUSDT", limit=100):
self.symbol = symbol.upper()
self.limit = limit # 5, 10, 20, 50, 100, 500, 1000, 5000
def get_order_book(self):
"""
Lấy snapshot Order Book hiện tại
Document: https://developers.binance.com/docs/derivatives/usds-margined-futures/order-book
"""
endpoint = f"{self.BASE_URL}/fapi/v1/depth"
params = {
"symbol": self.symbol,
"limit": self.limit
}
try:
response = requests.get(endpoint, params=params, timeout=10)
response.raise_for_status()
data = response.json()
return {
"lastUpdateId": data["lastUpdateId"],
"bids": [[float(p), float(q)] for p, q in data["bids"]],
"asks": [[float(p), float(q)] for p, q in data["asks"]],
"timestamp": time.time()
}
except requests.exceptions.Timeout:
raise ConnectionError("Request timeout - thử tăng timeout hoặc kiểm tra network")
except requests.exceptions.HTTPError as e:
if e.response.status_code == 401:
raise ConnectionError("401 Unauthorized - kiểm tra API key")
elif e.response.status_code == 429:
raise ConnectionError("429 Too Many Requests - đã vượt rate limit")
else:
raise ConnectionError(f"HTTP Error: {e}")
except requests.exceptions.RequestException as e:
raise ConnectionError(f"Connection Error: {e}")
Sử dụng
book = BinanceOrderBook(symbol="BTCUSDT", limit=100)
try:
order_book = book.get_order_book()
print(f"Last Update ID: {order_book['lastUpdateId']}")
print(f"Số lượng Bid levels: {len(order_book['bids'])}")
print(f"Số lượng Ask levels: {len(order_book['asks'])}")
# Tính spread
best_bid = order_book['bids'][0][0]
best_ask = order_book['asks'][0][0]
spread = (best_ask - best_bid) / best_bid * 100
print(f"Best Bid: {best_bid}, Best Ask: {best_ask}")
print(f"Spread: {spread:.4f}%")
except ConnectionError as e:
print(f"Lỗi kết nối: {e}")
Method 2: WebSocket - Real-time Order Book Stream
Đối với trading thực sự, bạn cần real-time update. WebSocket là giải pháp tối ưu:
import asyncio
import websockets
import json
import time
from collections import deque
class BinanceWebSocketOrderBook:
"""WebSocket stream cho real-time Order Book updates"""
STREAM_URL = "wss://fstream.binance.com/ws"
def __init__(self, symbol="btcusdt", depth=100):
self.symbol = symbol.lower()
self.depth = depth
self.order_book = {"bids": {}, "asks": {}}
self.message_count = 0
self.last_update_time = None
self.update_buffer = deque(maxlen=1000) # Lưu 1000 updates gần nhất
def process_update(self, message):
"""Xử lý một message từ WebSocket"""
try:
data = json.loads(message)
# Xử lý order book update
if "e" in data and data["e"] == "depthUpdate":
self.last_update_time = data["E"]
# Update bids
for price, qty in data["b"]:
price = float(price)
qty = float(qty)
if qty == 0:
self.order_book["bids"].pop(price, None)
else:
self.order_book["bids"][price] = qty
# Update asks
for price, qty in data["a"]:
price = float(price)
qty = float(qty)
if qty == 0:
self.order_book["asks"].pop(price, None)
else:
self.order_book["asks"][price] = qty
self.message_count += 1
self.update_buffer.append({
"timestamp": self.last_update_time,
"bids": dict(self.order_book["bids"]),
"asks": dict(self.order_book["asks"])
})
return True
return False
except json.JSONDecodeError:
print(f"Lỗi parse JSON: {message[:100]}")
return False
except Exception as e:
print(f"Lỗi xử lý message: {e}")
return False
async def subscribe(self):
"""Kết nối và subscribe Order Book stream"""
stream_name = f"{self.symbol}@depth@100ms"
subscribe_msg = {
"method": "SUBSCRIBE",
"params": [stream_name],
"id": 1
}
print(f"Connecting to {stream_name}...")
try:
async with websockets.connect(self.STREAM_URL) as ws:
# Gửi subscribe request
await ws.send(json.dumps(subscribe_msg))
# Nhận confirmation
response = await asyncio.wait_for(ws.recv(), timeout=5)
print(f"Subscription confirmed: {response}")
# Lắng nghe messages
print("Đang nhận real-time data...")
start_time = time.time()
while True:
try:
message = await ws.recv()
if self.process_update(message):
# Log mỗi 100 messages
if self.message_count % 100 == 0:
top_bid = max(self.order_book["bids"].keys())
top_ask = min(self.order_book["asks"].keys())
elapsed = time.time() - start_time
print(f"[{elapsed:.1f}s] Messages: {self.message_count} | "
f"Bid: {top_bid} | Ask: {top_ask}")
except websockets.exceptions.ConnectionClosed:
print("Connection closed by server")
break
except asyncio.TimeoutError:
print("Timeout khi chờ subscription confirmation")
except websockets.exceptions.InvalidURI:
raise ConnectionError("Invalid WebSocket URL")
except OSError as e:
raise ConnectionError(f"Network error: {e}")
def get_current_state(self, top_n=10):
"""Lấy top N bid/ask hiện tại"""
sorted_bids = sorted(self.order_book["bids"].items(), reverse=True)[:top_n]
sorted_asks = sorted(self.order_book["asks"].items())[:top_n]
return {
"lastUpdateTime": self.last_update_time,
"messageCount": self.message_count,
"topBids": sorted_bids,
"topAsks": sorted_asks
}
Chạy WebSocket
async def main():
ws_book = BinanceWebSocketOrderBook(symbol="btcusdt", depth=100)
await ws_book.subscribe()
if __name__ == "__main__":
asyncio.run(main())
Advanced: Kết hợp REST + WebSocket cho Data Reliability
Trong production, bạn nên kết hợp cả hai phương pháp để đảm bảo data consistency:
import asyncio
import aiohttp
import websockets
import json
import time
from typing import Dict, List, Tuple
class HybridOrderBookManager:
"""
Kết hợp REST (snapshot) + WebSocket (updates) để đảm bảo data chính xác
Xử lý out-of-sync events theo Binance documentation
"""
REST_URL = "https://fapi.binance.com"
WS_URL = "wss://fstream.binance.com/ws"
def __init__(self, symbol="BTCUSDT", depth_limit=1000):
self.symbol = symbol.upper()
self.ws_symbol = symbol.lower()
self.depth_limit = depth_limit
self.last_update_id = 0
self.order_book = {"bids": {}, "asks": {}}
self.sync_buffer = []
self.is_synced = False
self.stats = {
"total_updates": 0,
"sync_errors": 0,
"last_sync_time": None
}
async def fetch_snapshot(self, session) -> Dict:
"""Lấy Order Book snapshot từ REST API"""
endpoint = f"{self.REST_URL}/fapi/v1/depth"
params = {"symbol": self.symbol, "limit": self.depth_limit}
async with session.get(endpoint, params=params) as response:
if response.status == 429:
raise ConnectionError("Rate limit exceeded - thử lại sau 1 phút")
if response.status == 418:
raise ConnectionError("IP banned - kiểm tra lại request pattern")
data = await response.json()
return {
"lastUpdateId": int(data["lastUpdateId"]),
"bids": {float(p): float(q) for p, q in data["bids"]},
"asks": {float(p): float(q) for p, q in data["asks"]}
}
def apply_update(self, update: Dict):
"""Áp dụng một update vào order book local"""
self.stats["total_updates"] += 1
for price, qty in update["b"]:
price = float(price)
qty = float(qty)
if qty == 0:
self.order_book["bids"].pop(price, None)
else:
self.order_book["bids"][price] = qty
for price, qty in update["a"]:
price = float(price)
qty = float(qty)
if qty == 0:
self.order_book["asks"].pop(price, None)
else:
self.order_book["asks"][price] = qty
def check_sync(self, update_id: int) -> bool:
"""
Kiểm tra xem update có hợp lệ không (theo Binance protocol)
Update hợp lệ khi: update_id > lastUpdateId
"""
if update_id <= self.last_update_id:
self.stats["sync_errors"] += 1
return False
self.last_update_id = update_id
return True
async def sync_order_book(self, session):
"""
Sync Order Book: Lấy snapshot + áp dụng buffer
"""
# 1. Lấy snapshot mới
snapshot = await self.fetch_snapshot(session)
self.last_update_id = snapshot["lastUpdateId"]
self.order_book["bids"] = snapshot["bids"]
self.order_book["asks"] = snapshot["asks"]
# 2. Clear buffer cũ
self.sync_buffer.clear()
self.is_synced = True
self.stats["last_sync_time"] = time.time()
print(f"Synced! Last Update ID: {self.last_update_id}")
return snapshot
async def websocket_listener(self, session):
"""Listen WebSocket stream và áp dụng updates"""
stream_name = f"{self.ws_symbol}@depth@100ms"
ws_url = f"{self.WS_URL}/{stream_name}"
async with websockets.connect(ws_url) as ws:
print(f"WebSocket connected: {stream_name}")
while True:
try:
message = await asyncio.wait_for(ws.recv(), timeout=30)
data = json.loads(message)
if "e" in data: # Depth update event
update_id = int(data["u"]) # Final update ID
first_id = int(data["U"]) # First update ID
# Buffer update nếu chưa sync
if not self.is_synced:
self.sync_buffer.append(data)
continue
# Check sync và apply
if self.check_sync(update_id):
self.apply_update(data)
# Re-sync định kỳ (mỗi 5 phút)
if time.time() - self.stats["last_sync_time"] > 300:
print("Periodic re-sync...")
await self.sync_order_book(session)
except asyncio.TimeoutError:
print("WebSocket timeout - checking connection...")
continue
except websockets.exceptions.ConnectionClosed:
print("Connection closed - reconnecting...")
break
def calculate_metrics(self) -> Dict:
"""Tính toán các chỉ số Order Book"""
sorted_bids = sorted(self.order_book["bids"].items(), reverse=True)
sorted_asks = sorted(self.order_book["asks"].items())
if not sorted_bids or not sorted_asks:
return {"error": "Order book empty"}
best_bid = sorted_bids[0][0]
best_ask = sorted_asks[0][0]
mid_price = (best_bid + best_ask) / 2
# Tính bid/ask volume
bid_volume = sum(qty for _, qty in sorted_bids[:20])
ask_volume = sum(qty for _, qty in sorted_asks[:20])
# Tính VWAP
bid_vwap = sum(p * q for p, q in sorted_bids[:20]) / bid_volume if bid_volume > 0 else 0
ask_vwap = sum(p * q for p, q in sorted_asks[:20]) / ask_volume if ask_volume > 0 else 0
return {
"best_bid": best_bid,
"best_ask": best_ask,
"spread": best_ask - best_bid,
"spread_pct": (best_ask - best_bid) / mid_price * 100,
"bid_volume_20": bid_volume,
"ask_volume_20": ask_volume,
"bid_ask_imbalance": (bid_volume - ask_volume) / (bid_volume + ask_volume),
"mid_price": mid_price,
"stats": self.stats
}
async def main():
manager = HybridOrderBookManager(symbol="BTCUSDT", depth_limit=1000)
async with aiohttp.ClientSession() as session:
# Sync ban đầu
await manager.sync_order_book(session)
# Start WebSocket listener
try:
await manager.websocket_listener(session)
except KeyboardInterrupt:
metrics = manager.calculate_metrics()
print(f"\n=== Final Metrics ===")
for k, v in metrics.items():
if k != "stats":
print(f"{k}: {v}")
if __name__ == "__main__":
asyncio.run(main())
Performance Optimization - Tối ưu cho High-Frequency
Nếu bạn cần xử lý data ở tốc độ cao cho machine learning hoặc real-time analytics, kết hợp với HolySheep AI có thể giảm đáng kể chi phí xử lý — chỉ từ $0.42/MTok với DeepSeek V3.2 hoặc miễn phí với tín dụng ban đầu khi đăng ký.
Lỗi thường gặp và cách khắc phục
1. Lỗi 401 Unauthorized
Mô tả: Request bị từ chối với thông báo "Signature verification failed" hoặc "Invalid API Key"
# Nguyên nhân và giải pháp:
1. API Key không đúng
Kiểm tra lại credentials trong .env
Đảm bảo không có khoảng trắng thừa
import os
from dotenv import load_dotenv
load_dotenv()
Cách đọc đúng
api_key = os.getenv("BINANCE_API_KEY", "").strip()
secret_key = os.getenv("BINANCE_SECRET_KEY", "").strip()
if not api_key or not secret_key:
raise ValueError("API credentials not found in .env file")
2. Sử dụng testnet thay vì mainnet (hoặc ngược lại)
Testnet: https://testnet.binancefuture.com
Mainnet: https://fapi.binance.com
3. Server time drift (sai lệch thời gian)
Cài đặt NTP để sync thời gian hệ thống
Windows: w32tm /resync
Linux: sudo ntpdate -s time.bora.net
2. Lỗi 429 Too Many Requests
Mô tả: Rate limit exceeded, request bị từ chối tạm thời
import time
import requests
from ratelimit import limits, sleep_and_retry
class RateLimitedClient:
"""Wrapper để xử lý rate limit tự động"""
def __init__(self):
self.request_count = 0
self.window_start = time.time()
self.max_requests = 1200 # Binance Futures: 1200/min
self.window = 60 # 1 phút
@sleep_and_retry
@limits(calls=10, period=1) # Max 10 requests/giây
def get_with_backoff(self, url, max_retries=5):
"""Gửi request với exponential backoff"""
# Kiểm tra rate limit window
elapsed = time.time() - self.window_start
if elapsed > self.window:
self.request_count = 0
self.window_start = time.time()
if self.request_count >= self.max_requests:
wait_time = self.window - elapsed + 1
print(f"Rate limit reached, waiting {wait_time:.1f}s...")
time.sleep(wait_time)
for attempt in range(max_retries):
try:
response = requests.get(url, timeout=10)
if response.status_code == 429:
# Exponential backoff
wait = 2 ** attempt + 0.5
print(f"Rate limited, retrying in {wait}s...")
time.sleep(wait)
continue
response.raise_for_status()
self.request_count += 1
return response.json()
except requests.exceptions.RequestException as e:
if attempt == max_retries - 1:
raise ConnectionError(f"Failed after {max_retries} retries: {e}")
time.sleep(2 ** attempt)
return None
Sử dụng
client = RateLimitedClient()
result = client.get_with_backoff("https://fapi.binance.com/fapi/v1/depth?symbol=BTCUSDT&limit=100")
3. Lỗi ConnectionError: timeout
Mô tả: Request timeout, không nhận được response
# Nguyên nhân và giải pháp:
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
import asyncio
import aiohttp
Solution 1: Retry với exponential backoff
def create_session_with_retries():
"""Tạo session với auto-retry"""
session = requests.Session()
retry_strategy = Retry(
total=5,
backoff_factor=1,
status_forcelist=[429, 500, 502, 503, 504],
allowed_methods=["GET", "POST"]
)
adapter = HTTPAdapter(max_retries=retry_strategy)
session.mount("https://", adapter)
session.mount("http://", adapter)
return session
Solution 2: Async với timeout riêng cho từng request
async def fetch_with_timeout():
"""Async fetch với configurable timeout"""
timeout = aiohttp.ClientTimeout(total=30, connect=10)
connector = aiohttp.TCPConnector(
limit=100,
ttl_dns_cache=300,
use_dns_cache=True
)
async with aiohttp.ClientSession(
timeout=timeout,
connector=connector
) as session:
# Thử 3 lần với timeout tăng dần
for attempt, wait in enumerate([5, 10, 20]):
try:
async with session.get(
"https://fapi.binance.com/fapi/v1/depth",
params={"symbol": "BTCUSDT", "limit": 100}
) as response:
return await response.json()
except asyncio.TimeoutError:
print(f"Attempt {attempt+1} timeout, retrying with {wait}s timeout...")
timeout = aiohttp.ClientTimeout(total=wait)
except aiohttp.ClientError as e:
print(f"Connection error: {e}")
await asyncio.sleep(2 ** attempt)
Solution 3: Kiểm tra DNS và network
Chạy lệnh sau để verify connectivity
"""
nslookup fapi.binance.com
ping fapi.binance.com
traceroute fapi.binance.com # Linux/Mac
tracert fapi.binance.com # Windows
"""
4. Lỗi WebSocket Disconnection liên tục
Mô tả: WebSocket connection bị ngắt kết nối sau vài giây/phút
import asyncio
import websockets
import json
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class RobustWebSocket:
"""WebSocket với auto-reconnect và heartbeat"""
MAX_RECONNECT_ATTEMPTS = 10
RECONNECT_DELAY = 2 # seconds
HEARTBEAT_INTERVAL = 30 # seconds
PING_TIMEOUT = 20 # seconds
def __init__(self, symbol, depth=100):
self.symbol = symbol.lower()
self.depth = depth
self.running = False
self.ws = None
self.reconnect_count = 0
async def heartbeat(self):
"""Ping định kỳ để giữ connection alive"""
while self.running:
await asyncio.sleep(self.HEARTBEAT_INTERVAL)
if self.ws and self.ws.open:
try:
await self.ws.ping()
logger.debug("Heartbeat sent")
except Exception as e:
logger.warning(f"Heartbeat failed: {e}")
break
async def connect_with_retry(self):
"""Kết nối với auto-reconnect logic"""
url = f"wss://fstream.binance.com/ws/{self.symbol}@depth@100ms"
while self.running and self.reconnect_count < self.MAX_RECONNECT_ATTEMPTS:
try:
self.ws = await asyncio.wait_for(
websockets.connect(
url,
ping_interval=self.HEARTBEAT_INTERVAL,
ping_timeout=self.PING_TIMEOUT
),
timeout=10
)
logger.info(f"Connected to {url}")
self.reconnect_count = 0 # Reset counter on success
return True
except asyncio.TimeoutError:
logger.warning("Connection timeout")
except OSError as e:
logger.warning(f"Connection failed: {e}")
self.reconnect_count += 1
delay = min(self.RECONNECT_DELAY * (2 ** self.reconnect_count), 60)
logger.info(f"Reconnecting in {delay}s (attempt {self.reconnect_count})...")
await asyncio.sleep(delay)
logger.error("Max reconnection attempts reached")
return False
async def listen(self):
"""Listen messages với error handling"""
self.running = True
# Start heartbeat task
heartbeat_task = asyncio.create_task(self.heartbeat())
try:
if not await self.connect_with_retry():
return
async for message in self.ws:
try:
data = json.loads(message)
# Xử lý message...
if "e" in data:
logger.debug(f"Received update: {data['u']}")
except json.JSONDecodeError:
logger.error(f"Invalid JSON: {message[:100]}")
except websockets.exceptions.ConnectionClosed as e:
logger.warning(f"Connection closed: {e}")
finally:
self.running = False
heartbeat_task.cancel()
try:
await heartbeat_task
except asyncio.CancelledError:
pass
async def main():
ws = RobustWebSocket("btcusdt")
await ws.listen()
asyncio.run(main())
Data Validation - Đảm bảo data chính xác
Một vấn đề tôi gặp phải là data race condition — khi WebSocket update đến trước khi REST snapshot được xử lý xong. Đây là cách validate:
import time
from typing import Dict, List, Tuple, Optional
class OrderBookValidator:
"""Validate Order Book data integrity"""
def __init__(self, max_spread_pct=1.0, max_age_seconds=60):
self.max_spread_pct = max_spread_pct
self.max_age = max_age_seconds
self.validation_errors = []
def validate_order_book(self, bids: List, asks: List,
update_time: Optional[int] = None) -> Tuple[bool, str]:
"""Validate Order Book data structure và values"""
# 1. Kiểm tra empty
if not bids or not asks:
return False, "Order book is empty"
# 2. Kiểm tra format
try:
for price, qty in bids:
if price <= 0 or qty < 0:
return False, f"Invalid bid: price={price}, qty={qty}"
for price, qty in asks:
if price <= 0 or qty < 0:
return False, f"Invalid ask: price={price}, qty={qty}"
except (ValueError, TypeError) as e:
return False, f"Invalid format: {e}"
# 3. Kiểm tra spread
best_bid = max(p for p, _ in bids)
best_ask = min(p for p, _ in asks)
mid = (best_bid + best_ask) / 2
spread_pct = (best_ask - best_bid) / mid * 100
if spread_pct > self.max_spread_pct:
return False, f"Spread too wide: {spread_pct:.4f}%"
# 4. Kiểm tra timestamp
if update_time:
age = time.time() - (update_time / 1000)
if age > self.max_age:
return False, f"Data too old: {age:.1f}s"
# 5. Kiểm tra cross (bid > ask = lỗi logic)
if best_bid >= best_ask:
return False, f"Crossed market: bid={best_bid}, ask={best_ask}"
return True, "Valid"
def detect_anomalies(self, bids: List, asks: List) -> List[str]:
"""Phát hiện anomalies trong Order Book"""
anomalies = []
# Kiểm tra spike in volume
if bids:
volumes = [qty for _, qty in bids]
avg_vol = sum(volumes) / len(volumes)
for price, qty in bids:
if qty > avg_vol * 10:
anomalies.append(f"Large bid at {price}: {qty}x average")
# Kiểm tra gap
sorted_bids = sorted(bids, reverse=True)
sorted_asks = sorted(asks)
if len(sorted_bids) > 1:
gaps = [sorted_bids[i][0] - sorted_bids[i+1][0]
for i in range(len(sorted_bids)-1)]
avg_gap = sum(gaps) / len(gaps)
for i, gap in enumerate(gaps):
if gap > avg_gap * 5:
anomalies.append(f"Large bid gap: {gap} vs avg {avg_gap:.2f}")
return anomalies
Sử dụng
validator = OrderBookValidator(max_spread_pct=0.5)
order_book = book.get_order_book() # Từ code ở trên
is_valid, message = validator.validate_order_book(
order_book["bids"],
order_book["asks"],
update_time=order_book.get("lastUpdateId")
)
if not is_valid:
print(f"⚠️ Validation failed: {message}")
else:
anomalies = validator.detect_anomalies(order_book["bids"], order_book["asks"])
if anomalies:
print(f"⚠️ Anomalies detected: {anomalies}")
else:
print("✅ Order book data is valid")
Cấu trúc dữ liệu đầu ra
Khi bạn đã lấy được Order Book, đây là cách tổ chức data để sử d