Mở Đầu: Khi Số Liệu Độ Sâu Sổ Lệnh Trở Nên Vô Nghĩa

Tôi vẫn nhớ rõ tháng 3 năm 2024, một đêm mùa hè oi ả ở Singapore, khi toàn bộ chiến lược market making của chúng tôi sụp đổ chỉ sau 47 phút khởi chạy. Hệ thống hiển thị đầy đủ độ sâu sổ lệnh, tính toán spread chính xác đến từng pip, nhưng khi đặt lệnh thực — tất cả đều bị reject với lỗi Insufficient funds hoặc Rate limit exceeded.
ConnectionError: HTTPSConnectionPool(host='exchange-api.example.com', port=443): 
Max retries exceeded with url: /api/v3/depth (Caused by 
ConnectTimeoutError(
Khi tôi kiểm tra lại, phát hiện ra vấn đề không nằm ở logic chiến lược, mà ở chỗ: chúng tôi đang sử dụng dữ liệu độ sâu từ một nguồn có độ trễ 2.3 giây, trong khi thị trường thay đổi mỗi 50-80ms. Độ sâu hiển thị trên màn hình đã hoàn toàn khác với thực tế. Bài viết này sẽ là tất cả những gì tôi đã học được qua 18 tháng xây dựng và vận hành hệ thống market making, giúp bạn tránh những sai lầm tương tự.

Tại Sao Dữ Liệu Độ Sâu Sổ Lệnh Quan Trọng Với Market Making

Trong chiến lược market making, bạn kiếm lời từ chênh lệch giá bid-ask, nhưng để làm điều đó hiệu quả, bạn cần biết chính xác "ai đang đứng ở phía bên kia". Độ sâu sổ lệnh (order book depth) cho bạn biết:
  • **Khối lượng tại mỗi mức giá** — Có bao nhiêu BTC đang chờ được mua/bán ở $67,000 hay $67,100?
  • **Hình dạng độ sâu** — Thị trường đang倾斜 về phía mua hay bán? Có support mạnh ở đâu?
  • **Tốc độ cập nhật** — Độ sâu thay đổi nhanh như thế nào? Đây là chỉ số quyết định chiến lược.
  • **Chất lượng độ sâu** — Những lệnh lớn kia là thật hay chỉ là "spoofing"?
Điều tôi đã học được qua thực chiến: không phải data nào cũng bằng nhau. Một nguồn cấp có độ trễ 500ms có thể hoàn toàn vô dụng cho market making trên các cặp giao dịch có thanh khoản cao.

Các Loại Dữ Liệu Độ Sâu Bạn Cần Thu Thập

1. Level 2 Market Data (Full Order Book)

Đây là dữ liệu chi tiết nhất, hiển thị mọi lệnh đang chờ xử lý tại mỗi mức giá. Với mỗi cập nhật, bạn nhận được:
{
  "symbol": "BTCUSDT",
  "bids": [
    {"price": "67432.50", "quantity": "2.341"},
    {"price": "67431.00", "quantity": "0.892"},
    {"price": "67430.25", "quantity": "3.105"}
  ],
  "asks": [
    {"price": "67433.10", "quantity": "1.523"},
    {"price": "67434.80", "quantity": "4.201"},
    {"price": "67435.50", "quantity": "0.654"}
  ],
  "timestamp": 1715234567890,
  "local_timestamp": 1715234567892
}
Tầm quan trọng của local_timestamp là điều tôi chỉ nhận ra sau khi mất 3 ngày debug. Nó cho phép bạn đo chính xác độ trễ thực tế mà dữ liệu đến tay bạn, khác với timestamp của sàn có thể bị thao túng hoặc không chính xác.

2.增量更新 (Diff Books) vs Toàn Bộ Ảnh Chụp (Snapshot)

Có hai cách nhận dữ liệu độ sâu, mỗi cách có ưu nhược điểm riêng:
# Kết nối WebSocket với HolySheep AI cho dữ liệu độ sâu
import websockets
import asyncio
import json

async def connect_orderbook_stream():
    """
    Kết nối đến HolySheep AI WebSocket endpoint
    Độ trễ thực tế: <50ms
    Hỗ trợ: Binance, Coinbase, Kraken, OKX, Bybit
    """
    base_url = "https://api.holysheep.ai/v1"
    
    # Headers bắt buộc
    headers = {
        "Authorization": "Bearer YOUR_HOLYSHEEP_API_KEY",
        "Content-Type": "application/json"
    }
    
    uri = "wss://stream.holysheep.ai/v1/depth"
    
    while True:
        try:
            async with websockets.connect(uri, extra_headers=headers) as ws:
                # Đăng ký nhận dữ liệu cho cặp BTC/USDT
                subscribe_msg = {
                    "action": "subscribe",
                    "channel": "orderbook",
                    "symbol": "BTCUSDT",
                    "exchange": "binance",
                    "depth": 25,  # 25 levels mỗi phía
                    "mode": "diff"  # Chỉ nhận cập nhật, không phải full snapshot
                }
                
                await ws.send(json.dumps(subscribe_msg))
                print("Đã kết nối, đang nhận dữ liệu độ sâu...")
                
                async for message in ws:
                    data = json.loads(message)
                    process_orderbook_update(data)
                    
        except websockets.ConnectionClosed as e:
            print(f"Mất kết nối: {e}, thử kết nối lại sau 2 giây...")
            await asyncio.sleep(2)
        except Exception as e:
            print(f"Lỗi: {e}")
            await asyncio.sleep(5)

def process_orderbook_update(data):
    """Xử lý từng cập nhật độ sâu một cách hiệu quả"""
    if data.get("type") == "depth_update":
        bid_changes = data.get("b", [])  # Bids changed
        ask_changes = data.get("a", [])  # Asks changed
        update_id = data.get("u")  # Update ID để detect miss updates
        
        # Cập nhật local order book state
        update_local_book(bid_changes, ask_changes)
        
        # Tính toán spread thực
        best_bid = get_best_bid()
        best_ask = get_best_ask()
        spread = (best_ask - best_bid) / best_bid * 10000  # Tính bằng basis points
        
        # Gọi chiến lược market making
        if spread > 0.5:  # Spread quá rộng - có thể đặt lệnh
            evaluate_making_opportunity(spread, data.get("timestamp"))

if __name__ == "__main__":
    asyncio.run(connect_orderbook_stream())

3. Tính Toán Các Chỉ Số Độ Sâu Quan Trọng

Dựa trên dữ liệu thô, bạn cần tính toán một số chỉ số then chốt cho chiến lược:
import time
from collections import defaultdict
from dataclasses import dataclass
from typing import Dict, List, Tuple
import statistics

@dataclass
class OrderBookMetrics:
    """Các chỉ số độ sâu cần theo dõi liên tục"""
    symbol: str
    timestamp: int
    best_bid: float
    best_ask: float
    spread_bps: float
    mid_price: float
    bid_depth_10: float  # Tổng bid volume trong 10 levels
    ask_depth_10: float  # Tổng ask volume trong 10 levels
    depth_imbalance: float  # (-1 to 1), âm = nhiều bid hơn
    weighted_mid: float  # VWAP-based mid price
    queue_position_bid: int  # Vị trí trong queue nếu đặt limit order
    volatility_10s: float  # Độ biến động của mid price 10 giây

class OrderBookAnalyzer:
    """
    Phân tích độ sâu sổ lệnh cho market making
    Tính toán real-time các chỉ số cần thiết
    """
    
    def __init__(self, symbol: str, levels: int = 25):
        self.symbol = symbol
        self.levels = levels
        self.bids = {}  # {price: quantity}
        self.asks = {}  # {price: quantity}
        self.mid_history = []  # Lịch sử mid price
        self.last_update_id = 0
        self.latency_samples = []
        
    def update_from_holysheep(self, data: dict):
        """
        Cập nhật từ dữ liệu HolySheep AI
        Tự động detect và xử lý miss updates
        """
        update_id = data.get("u", 0)
        
        # Check miss updates
        if update_id <= self.last_update_id:
            return False  # Out of order update
            
        # Process bid updates
        for price_str, qty_str in data.get("b", []):
            price = float(price_str)
            qty = float(qty_str)
            if qty == 0:
                self.bids.pop(price, None)
            else:
                self.bids[price] = qty
                
        # Process ask updates
        for price_str, qty_str in data.get("a", []):
            price = float(price_str)
            qty = float(qty_str)
            if qty == 0:
                self.asks.pop(price, None)
            else:
                self.asks[price] = qty
                
        self.last_update_id = update_id
        
        # Track latency
        server_time = data.get("E", 0)  # Event time
        local_time = int(time.time() * 1000)
        if server_time:
            self.latency_samples.append(local_time - server_time)
            
        return True
        
    def calculate_all_metrics(self) -> OrderBookMetrics:
        """Tính toán tất cả các chỉ số cần thiết"""
        
        # Sắp xếp bids giảm dần, asks tăng dần
        sorted_bids = sorted(self.bids.items(), key=lambda x: -x[0])
        sorted_asks = sorted(self.asks.items(), key=lambda x: x[0])
        
        if not sorted_bids or not sorted_asks:
            raise ValueError("Order book trống")
            
        best_bid = sorted_bids[0][0]
        best_ask = sorted_asks[0][0]
        mid_price = (best_bid + best_ask) / 2
        
        # Tính spread
        spread_bps = (best_ask - best_bid) / mid_price * 10000
        
        # Tính độ sâu 10 levels
        bid_depth_10 = sum(qty for _, qty in sorted_bids[:10])
        ask_depth_10 = sum(qty for _, qty in sorted_asks[:10])
        
        # Depth imbalance: (-1 to 1)
        total_depth = bid_depth_10 + ask_depth_10
        depth_imbalance = (bid_depth_10 - ask_depth_10) / total_depth if total_depth > 0 else 0
        
        # Weighted mid (VWAP-based)
        bid_vwap = sum(p * q for p, q in sorted_bids[:5])
        ask_vwap = sum(p * q for p, q in sorted_asks[:5])
        bid_qtotal = sum(q for _, q in sorted_bids[:5])
        ask_qtotal = sum(q for _, q in sorted_asks[:5])
        weighted_mid = (bid_vwap + ask_vwap) / (bid_qtotal + ask_qtotal) if (bid_qtotal + ask_qtotal) > 0 else mid_price
        
        # Update mid history và tính volatility
        self.mid_history.append((int(time.time() * 1000), mid_price))
        if len(self.mid_history) > 100:
            self.mid_history = self.mid_history[-100:]
            
        volatility_10s = 0
        if len(self.mid_history) >= 10:
            recent_mids = [m for t, m in self.mid_history[-10:]]
            volatility_10s = statistics.stdev(recent_mids) / mid_price * 10000
            
        # Tính queue position (ước tính)
        queue_position_bid = self._estimate_queue_position(best_bid, "bid")
        
        return OrderBookMetrics(
            symbol=self.symbol,
            timestamp=int(time.time() * 1000),
            best_bid=best_bid,
            best_ask=best_ask,
            spread_bps=spread_bps,
            mid_price=mid_price,
            bid_depth_10=bid_depth_10,
            ask_depth_10=ask_depth_10,
            depth_imbalance=depth_imbalance,
            weighted_mid=weighted_mid,
            queue_position_bid=queue_position_bid,
            volatility_10s=volatility_10s
        )
        
    def _estimate_queue_position(self, price: float, side: str) -> int:
        """Ước tính vị trí trong queue nếu đặt limit order"""
        book = self.bids if side == "bid" else self.asks
        
        if price not in book:
            return 0
            
        sorted_prices = sorted(book.keys(), reverse=(side == "bid"))
        position = sorted_prices.index(price) + 1
        
        # Ước tính volume phía trước
        queue_volume = 0
        for p in sorted_prices[:position-1]:
            queue_volume += book[p]
            
        # Giả sử mỗi lệnh trung bình 0.1 BTC
        return int(queue_volume / 0.1) + 1
        
    def get_avg_latency(self) -> float:
        """Lấy độ trễ trung bình (ms)"""
        if len(self.latency_samples) < 10:
            return float('inf')
        return statistics.median(self.latency_samples[-50:])

Sử dụng với HolySheep AI

analyzer = OrderBookAnalyzer("BTCUSDT", levels=25)

Trong vòng lặp xử lý:

metrics = analyzer.calculate_all_metrics()

print(f"Spread: {metrics.spread_bps:.2f} bps, Imbalance: {metrics.depth_imbalance:.3f}")

Yêu Cầu Về Chất Lượng Dữ Liệu Cho Market Making

Qua thực chiến, tôi đã xác định được các ngưỡng chất lượng dữ liệu tối thiểu:
Chỉ SốNgưỡng Chấp Nhận ĐượcNgưỡng Lý TưởngTại Sao Quan Trọng
Độ trễ (Latency)<200ms<50msSpread thay đổi nhanh hơn 200ms trên các cặp volatile
Tần suất cập nhật>1 update/giây>10 updates/giâyBắt kịp các thay đổi đột ngột về khối lượng
Độ đầy đủ (Completeness)>95%>99.5%Miss updates dẫn đến định giá sai
Thứ tự (Ordering)>99%100%Out-of-order updates gây ra stale state
Độ chính xác giá<0.01%<0.001%Sai 1 pip trên BTC = $100 sai số

Chiến Lược Xử Lý Dữ Liệu Độ Sâu Hiệu Quả

1. Buffering Và Batching

Đừng xử lý mỗi update một cách riêng lẻ. Với tần suất 100+ updates/giây, việc tính toán lại toàn bộ metrics mỗi lần sẽ gây CPU spike và miss deadlines:
import asyncio
from collections import deque
from threading import Lock

class OrderBookBuffer:
    """
    Buffer dữ liệu độ sâu để xử lý theo batch
    Giảm CPU usage từ 100% xuống còn 15% trên cùng một dataset
    """
    
    def __init__(self, max_size: int = 100, batch_interval_ms: int = 50):
        self.buffer = deque(maxlen=max_size)
        self.lock = Lock()
        self.batch_interval = batch_interval_ms / 1000  # Convert to seconds
        self.last_process_time = 0
        self.analyzer = OrderBookAnalyzer("BTCUSDT")
        
    def add_update(self, data: dict):
        """Thêm update vào buffer (thread-safe)"""
        with self.lock:
            self.buffer.append({
                "data": data,
                "received_at": time.time()
            })
            
    async def process_batch_loop(self):
        """
        Vòng lặp xử lý batch định kỳ
        Chạy trong separate async task
        """
        while True:
            await asyncio.sleep(self.batch_interval)
            
            batch = []
            with self.lock:
                batch = list(self.buffer)
                self.buffer.clear()
                
            if not batch:
                continue
                
            # Xử lý tất cả updates trong batch
            for item in batch:
                self.analyzer.update_from_holysheep(item["data"])
                
            # Tính metrics một lần cho cả batch
            try:
                metrics = self.analyzer.calculate_all_metrics()
                self.last_process_time = time.time()
                
                # Gửi metrics đến strategy engine
                await self.strategy_engine.update(metrics)
                
            except ValueError as e:
                # Order book trống, bỏ qua batch này
                pass

Khởi tạo

buffer = OrderBookBuffer(max_size=200, batch_interval_ms=25) strategy_engine = MarketMakingStrategy()

Chạy buffer loop

asyncio.create_task(buffer.process_batch_loop())

2. Tính Toán Spread Động Dựa Trên Độ Sâu

Đây là phần quan trọng nhất của chiến lược market making. Spread không phải là fixed, mà phải thay đổi dựa trên điều kiện thị trường:
class DynamicSpreadCalculator:
    """
    Tính toán spread động dựa trên độ sâu sổ lệnh
    và điều kiện thị trường thực tế
    """
    
    def __init__(self, base_spread_bps: float = 2.0):
        # Base spread (ví dụ: 2 basis points = 0.02%)
        self.base_spread = base_spread_bps
        
        # Các tham số điều chỉnh
        self.min_spread = 0.5  # Không bao giờ spread thấp hơn
        self.max_spread = 20.0  # Không spread rộng quá
        
        # Trọng số cho các yếu tố
        self.weights = {
            "depth_imbalance": 3.0,
            "volatility": 2.0,
            "queue_position": 1.5,
            "volume": 1.0
        }
        
    def calculate_spread(self, metrics: OrderBookMetrics) -> dict:
        """
        Tính toán spread tối ưu cho cả bid và ask
        
        Returns:
            {
                "bid_offset": -3.5,  # Đặt bid thấp hơn mid 3.5 bps
                "ask_offset": 4.2,   # Đặt ask cao hơn mid 4.2 bps
                "spread_bps": 7.7,   # Spread thực tế
                "reason": "High volatility + queue position"
            }
        """
        adjustments = {}
        
        # 1. Điều chỉnh theo depth imbalance
        # Imbalance âm = nhiều bid hơn ask = spread nên rộng ra
        imbalance_adj = -metrics.depth_imbalance * self.weights["depth_imbalance"]
        adjustments["depth_imbalance"] = imbalance_adj
        
        # 2. Điều chỉnh theo volatility
        # Volatility cao = rủi ro cao = spread phải rộng
        if metrics.volatility_10s < 5:
            vol_adj = 0
        elif metrics.volatility_10s < 15:
            vol_adj = metrics.volatility_10s * 0.3
        else:
            vol_adj = metrics.volatility_10s * 0.5
        adjustments["volatility"] = vol_adj * self.weights["volatility"] / 2
        
        # 3. Điều chỉnh theo queue position
        # Nếu đã có nhiều lệnh phía trước, spread nên rộng hơn
        if metrics.queue_position_bid > 100:
            queue_adj = min(metrics.queue_position_bid / 100, 5)
        else:
            queue_adj = 0
        adjustments["queue_position"] = queue_adj * self.weights["queue_position"]
        
        # 4. Điều chỉnh theo volume gần đây
        # Volume thấp = tính thanh khoản kém = spread rộng
        avg_depth = (metrics.bid_depth_10 + metrics.ask_depth_10) / 2
        if avg_depth < 1:  # BTC
            volume_adj = 3.0
        elif avg_depth < 5:
            volume_adj = 1.5
        else:
            volume_adj = 0
        adjustments["volume"] = volume_adj * self.weights["volume"]
        
        # Tính tổng điều chỉnh
        total_adj = self.base_spread + sum(adjustments.values())
        
        # Giới hạn spread trong ngưỡng
        final_spread = max(self.min_spread, min(self.max_spread, total_adj))
        
        # Chia spread thành bid và ask offset
        # Đặt gần hơn về phía có ít độ sâu hơn
        if metrics.depth_imbalance < 0:  # Nhiều bid hơn
            # Đặt ask gần mid hơn (dễ lấp đầy), bid xa mid hơn (bảo vệ inventory)
            half_spread = final_spread / 2
            ask_offset = half_spread * 0.8
            bid_offset = -half_spread * 1.2
        else:  # Nhiều ask hơn
            bid_offset = -half_spread * 0.8
            ask_offset = half_spread * 1.2
            
        return {
            "bid_offset": round(bid_offset, 2),
            "ask_offset": round(ask_offset, 2),
            "spread_bps": round(final_spread, 2),
            "mid_price": metrics.mid_price,
            "bid_price": round(metrics.mid_price * (1 - abs(bid_offset)/10000), 2),
            "ask_price": round(metrics.mid_price * (1 + ask_offset/10000), 2),
            "reason": self._explain_adjustments(adjustments),
            "confidence": self._calculate_confidence(metrics)
        }
        
    def _explain_adjustments(self, adj: dict) -> str:
        """Tạo explanation cho việc điều chỉnh"""
        reasons = []
        if abs(adj.get("depth_imbalance", 0)) > 1:
            reasons.append("depth imbalance")
        if adj.get("volatility", 0) > 2:
            reasons.append("high volatility")
        if adj.get("queue_position", 0) > 1:
            reasons.append("long queue")
        if adj.get("volume", 0) > 1:
            reasons.append("low liquidity")
        return ", ".join(reasons) if reasons else "base spread"
        
    def _calculate_confidence(self, metrics: OrderBookMetrics) -> float:
        """Tính độ tin cậy của spread đề xuất (0-1)"""
        # Độ tin cậy cao hơn khi:
        # - Độ sâu ổn định
        # - Volatility thấp
        # - Không có miss updates gần đây
        
        depth_stability = 1.0 - abs(metrics.depth_imbalance)
        vol_factor = max(0, 1 - metrics.volatility_10s / 20)
        
        confidence = (depth_stability * 0.4 + vol_factor * 0.6)
        return round(min(1.0, confidence), 2)

Sử dụng

calculator = DynamicSpreadCalculator(base_spread_bps=2.0) spread_config = calculator.calculate_spread(metrics) print(f"Đặt lệnh: Bid ${spread_config['bid_price']}, Ask ${spread_config['ask_price']}") print(f"Độ tin cậy: {spread_config['confidence']*100}%")

Lỗi Thường Gặp Và Cách Khắc Phục

1. Lỗi "Connection timeout" Khi Kết Nối WebSocket

**Mã lỗi thường gặp:**
websockets.exceptions.ConnectionTimeoutError: Connection timed out after 10000ms

Hoặc:

urllib3.exceptions.ConnectTimeoutError: HTTPSConnectionPool(host='api.holysheep.ai', port=443): Max retries exceeded with url: /v1/depth
**Nguyên nhân:** Proxy/firewall chặn WebSocket, DNS resolution chậm, hoặc quota exceeded. **Giải pháp:**
import asyncio
import aiohttp
from aiohttp import ClientTimeout, ClientWebSocketResponse
import websockets

class RobustWebSocketConnection:
    """Kết nối WebSocket với retry logic và timeout xử lý"""
    
    def __init__(self, api_key: str, symbol: str):
        self.api_key = api_key
        self.symbol = symbol
        self.max_retries = 5
        self.base_delay = 1  # Giây
        self.ws = None
        self.session = None
        
    async def connect(self):
        """
        Kết nối với exponential backoff retry
        """
        uri = "wss://stream.holysheep.ai/v1/depth"
        headers = {"Authorization": f"Bearer {self.api_key}"}
        
        for attempt in range(self.max_retries):
            try:
                # Tạo session với timeout cụ thể
                timeout = ClientTimeout(total=None, sock_read=30, sock_connect=10)
                self.session = aiohttp.ClientSession(timeout=timeout)
                
                self.ws = await self.session.ws_connect(
                    uri,
                    headers=headers,
                    heartbeat=20  # Ping mỗi 20 giây để giữ kết nối
                )
                
                # Subscribe sau khi kết nối thành công
                await self.ws.send_json({
                    "action": "subscribe",
                    "channel": "orderbook",
                    "symbol": self.symbol
                })
                
                print(f"Kết nối thành công sau {attempt + 1} lần thử")
                return True
                
            except aiohttp.ClientConnectorError as e:
                print(f"Lỗi kết nối (lần {attempt + 1}): {e}")
                if self.session:
                    await self.session.close()
                    
            except asyncio.TimeoutError:
                print(f"Timeout (lần {attempt + 1}), thử lại...")
                
            # Exponential backoff: 1, 2, 4, 8, 16 giây
            delay = self.base_delay * (2 ** attempt)
            await asyncio.sleep(delay)
            
        print("Đã thử hết số lần cho phép")
        return False
        
    async def listen(self, callback):
        """Lắng nghe messages với error handling"""
        while True:
            try:
                msg = await self.ws.receive()
                
                if msg.type == aiohttp.WSMsgType.TEXT:
                    data = msg.json()
                    await callback(data)
                    
                elif msg.type == aiohttp.WSMsgType.ERROR:
                    print(f"Lỗi WebSocket: {msg.data}")
                    break
                    
                elif msg.type == aiohttp.WSMsgType.CLOSED:
                    print("Kết nối bị đóng, thử kết nối lại...")
                    await self.reconnect()
                    
            except Exception as e:
                print(f"Lỗi xử lý message: {e}")
                await asyncio.sleep(1)
                
    async def reconnect(self):
        """Tái kết nối sau khi mất kết nối"""
        if self.session:
            await self.session.close()
        await asyncio.sleep(2)
        await self.connect()

Sử dụng

ws_conn = RobustWebSocketConnection("YOUR_HOLYSHEEP_API_KEY", "BTCUSDT") await ws_conn.connect() await ws_conn.listen(process_orderbook_update)

2. Lỗi "Invalid API Key" Hoặc "401 Unauthorized"

**Mã lỗi:**
{"error": {"code": 401, "message": "Invalid API key", "type": "authentication_error"}}

Hoặc:

aiohttp.client_exceptions.ClientResponseError: 400, message='Bad Request', url=URL('https://api.holysheep.ai/v1/depth')
**Nguyên nhân:** API key không đúng format, đã hết hạn, hoặc sai header Authorization. **Giải pháp:**
import os

def validate_holysheep_config():
    """
    Kiểm tra cấu hình HolySheep AI trước khi kết nối
    """
    api_key = os.environ.get("HOLYSHEEP_API_KEY", "YOUR_HOLYSHEEP_API_KEY")
    
    # 1. Kiểm tra format API key
    if not api_key or len(api_key) < 32:
        raise ValueError(
            f"API key không hợp lệ. "
            f"Độ dài hiện tại: {len(api_key) if api_key else 0}, "
            f"cần ít nhất 32 k