Đầu năm 2025, một nhà giao dịch algorithm tên Minh (bí danh) gặp phải cơn ác mộng: chiến lược arbitrage của anh ta hoạt động hoàn hảo trên backtest với lợi nhuận 47.3% trong tháng, nhưng khi deploy lên live trading thì thua lỗ liên tục. Sau 3 tuần debug, anh ta phát hiện nguyên nhân chính — dữ liệu orderbook được backtest không khớp với thực tế vì thiếu thông tin về phiên bản snapshot và timestamp chính xác. Câu chuyện này là lý do hệ thống Tardis ra đời.

Tại Sao Cần Hệ Thống Audit Log Cho Backtest?

Trong giao dịch định lượng, reproducibility (khả năng tái tạo) là yếu tố sống còn. Khi bạn chạy lại cùng một chiến lược với cùng dữ liệu, kết quả phải identic. Tuy nhiên, dữ liệu Binance orderbook có nhiều phiên bản và nguồn tải khác nhau:

Mỗi nguồn có đặc điểm riêng về granularity, latency, và completeness. Nếu không ghi nhận chính xác nguồn gốc dữ liệu, bạn sẽ rơi vào tình trạng "overfitting to dirty data" — tối ưu hóa chiến lược cho dữ liệu có vấn đề thay vì thị trường thực.

Kiến Trúc Tardis Audit Log

1. Cấu Trúc Dữ Liệu Core

Hệ thống Tardis sử dụng cấu trúc log theo chuẩn append-only event store, đảm bảo tính immutability và auditability tuyệt đối. Mỗi bản ghi audit log chứa đầy đủ thông tin để tái tạo lại trạng thái hệ thống tại bất kỳ thời điểm nào.

from dataclasses import dataclass, field
from datetime import datetime, timezone
from typing import Optional, Dict, Any, List
from enum import Enum
import hashlib
import json

class DataSource(Enum):
    BINANCE_PUBLIC_WS = "binance_public_ws"
    BINANCE_HISTORICAL_API = "binance_historical_api"
    BINANCE_DOWNLOAD_CENTER = "binance_download_center"
    KAICO_API = "kaiko_api"
    COINAPI = "coinapi"
    CUSTOM_SNAPSHOT = "custom_snapshot"

class DataVersion(Enum):
    V1_OB_100MS = "v1_ob_100ms"
    V2_OB_1S = "v2_ob_1s"
    V3_OB_1MS = "v3_ob_1ms"
    COMPREHENSIVE_SNAPSHOT = "comprehensive_snapshot"

@dataclass
class OrderbookSnapshot:
    """Cấu trúc một snapshot orderbook với metadata đầy đủ"""
    symbol: str
    timestamp: datetime
    bids: List[tuple[float, float]]  # (price, quantity)
    asks: List[tuple[float, float]]
    source: DataSource
    version: DataVersion
    download_time: datetime
    local_storage_path: str
    checksum: str = ""
    exchange_timestamp_ms: int = 0
    local_received_ms: int = 0

@dataclass
class BacktestExperiment:
    """Metadata cho một experiment backtest"""
    experiment_id: str
    strategy_name: str
    strategy_version: str
    created_at: datetime
    parameters: Dict[str, Any]
    git_commit: str = ""
    seed_data: List[str] = field(default_factory=list)  # List of snapshot IDs

@dataclass
class TardisAuditLog:
    """
    Audit log entry cho Tardis system.
    Mỗi entry ghi lại một sự kiện quan trọng trong vòng đời backtest.
    """
    log_id: str
    event_type: str  # SNAPSHOT_DOWNLOADED, BACKTEST_STARTED, BACKTEST_COMPLETED, etc.
    timestamp: datetime
    experiment_id: Optional[str]
    snapshot_id: Optional[str]
    data_source: DataSource
    data_version: DataVersion
    metadata: Dict[str, Any]
    checksum: str = ""
    parent_log_id: Optional[str] = None  # Cho việc trace lineage

    def __post_init__(self):
        if not self.log_id:
            self.log_id = self._generate_log_id()

    def _generate_log_id(self) -> str:
        content = f"{self.event_type}{self.timestamp.isoformat()}{self.experiment_id or ''}"
        return hashlib.sha256(content.encode()).hexdigest()[:16]

    def to_dict(self) -> Dict[str, Any]:
        return {
            "log_id": self.log_id,
            "event_type": self.event_type,
            "timestamp": self.timestamp.isoformat(),
            "experiment_id": self.experiment_id,
            "snapshot_id": self.snapshot_id,
            "data_source": self.data_source.value,
            "data_version": self.data_version.value,
            "metadata": self.metadata,
            "checksum": self.checksum,
            "parent_log_id": self.parent_log_id
        }

2. Manager Class Cho Audit Log

Đây là phần core của hệ thống, xử lý việc ghi log, truy vấn và đảm bảo tính toàn vẹn dữ liệu. Tôi đã phát triển class này qua 2 năm vận hành với hơn 50,000 experiment backtest.

import sqlite3
import asyncio
from pathlib import Path
from typing import Optional, Generator
from contextlib import contextmanager
import aiofiles
import structlog

logger = structlog.get_logger()

class TardisAuditLogManager:
    """
    Quản lý audit log cho hệ thống backtest Tardis.
    Hỗ trợ cả SQLite cho local và async file operations.
    """

    def __init__(self, db_path: str, log_dir: str):
        self.db_path = Path(db_path)
        self.log_dir = Path(log_dir)
        self.log_dir.mkdir(parents=True, exist_ok=True)
        self._init_database()

    def _init_database(self):
        """Khởi tạo SQLite database với schema đầy đủ"""
        with self._get_connection() as conn:
            conn.execute("""
                CREATE TABLE IF NOT EXISTS audit_logs (
                    log_id TEXT PRIMARY KEY,
                    event_type TEXT NOT NULL,
                    timestamp TEXT NOT NULL,
                    experiment_id TEXT,
                    snapshot_id TEXT,
                    data_source TEXT NOT NULL,
                    data_version TEXT NOT NULL,
                    metadata TEXT NOT NULL,
                    checksum TEXT,
                    parent_log_id TEXT,
                    created_at TEXT DEFAULT CURRENT_TIMESTAMP
                )
            """)

            conn.execute("""
                CREATE INDEX IF NOT EXISTS idx_experiment_id
                ON audit_logs(experiment_id)
            """)

            conn.execute("""
                CREATE INDEX IF NOT EXISTS idx_snapshot_id
                ON audit_logs(snapshot_id)
            """)

            conn.execute("""
                CREATE INDEX IF NOT EXISTS idx_timestamp
                ON audit_logs(timestamp)
            """)

            # Bảng cho snapshot metadata riêng
            conn.execute("""
                CREATE TABLE IF NOT EXISTS orderbook_snapshots (
                    snapshot_id TEXT PRIMARY KEY,
                    symbol TEXT NOT NULL,
                    exchange_timestamp_ms INTEGER NOT NULL,
                    local_received_ms INTEGER NOT NULL,
                    download_time TEXT NOT NULL,
                    source TEXT NOT NULL,
                    version TEXT NOT NULL,
                    file_path TEXT NOT NULL,
                    checksum TEXT,
                    bid_count INTEGER,
                    ask_count INTEGER,
                    mid_price REAL,
                    spread_bps REAL,
                    created_at TEXT DEFAULT CURRENT_TIMESTAMP
                )
            """)

            # Bảng cho experiments
            conn.execute("""
                CREATE TABLE IF NOT EXISTS experiments (
                    experiment_id TEXT PRIMARY KEY,
                    strategy_name TEXT NOT NULL,
                    strategy_version TEXT,
                    created_at TEXT NOT NULL,
                    parameters TEXT,
                    git_commit TEXT,
                    status TEXT DEFAULT 'RUNNING',
                    final_pnl REAL,
                    total_trades INTEGER,
                    created_at_timestamp TEXT DEFAULT CURRENT_TIMESTAMP
                )
            """)

            conn.commit()

    @contextmanager
    def _get_connection(self):
        conn = sqlite3.connect(self.db_path)
        conn.row_factory = sqlite3.Row
        try:
            yield conn
        finally:
            conn.close()

    async def log_snapshot_download(
        self,
        snapshot: OrderbookSnapshot,
        experiment_id: Optional[str] = None
    ) -> str:
        """Ghi log khi một snapshot được tải xuống thành công"""

        # Tính checksum
        content = f"{snapshot.symbol}{snapshot.timestamp.isoformat()}{snapshot.bids}{snapshot.asks}"
        checksum = hashlib.sha256(content.encode()).hexdigest()

        log_entry = TardisAuditLog(
            log_id="",
            event_type="SNAPSHOT_DOWNLOADED",
            timestamp=datetime.now(timezone.utc),
            experiment_id=experiment_id,
            snapshot_id=snapshot.symbol,
            data_source=snapshot.source,
            data_version=snapshot.version,
            metadata={
                "download_time_ms": (snapshot.download_time - snapshot.timestamp).total_seconds() * 1000,
                "file_size_bytes": Path(snapshot.local_storage_path).stat().st_size,
                "bid_levels": len(snapshot.bids),
                "ask_levels": len(snapshot.asks),
                "mid_price": (snapshot.bids[0][0] + snapshot.asks[0][0]) / 2 if snapshot.bids and snapshot.asks else None,
                "spread_bps": self._calculate_spread_bps(snapshot.bids, snapshot.asks)
            },
            checksum=checksum
        )

        # Ghi vào database
        with self._get_connection() as conn:
            conn.execute("""
                INSERT INTO orderbook_snapshots
                (snapshot_id, symbol, exchange_timestamp_ms, local_received_ms,
                 download_time, source, version, file_path, checksum, bid_count, ask_count,
                 mid_price, spread_bps)
                VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
            """, (
                snapshot.symbol,
                snapshot.symbol,
                snapshot.exchange_timestamp_ms,
                snapshot.local_received_ms,
                snapshot.download_time.isoformat(),
                snapshot.source.value,
                snapshot.version.value,
                snapshot.local_storage_path,
                checksum,
                len(snapshot.bids),
                len(snapshot.asks),
                log_entry.metadata.get("mid_price"),
                log_entry.metadata.get("spread_bps")
            ))
            conn.commit()

        # Ghi JSON log file riêng
        log_file = self.log_dir / f"{log_entry.log_id}.json"
        async with aiofiles.open(log_file, 'w') as f:
            await f.write(json.dumps(log_entry.to_dict(), indent=2))

        logger.info("snapshot_logged", log_id=log_entry.log_id, symbol=snapshot.symbol)
        return log_entry.log_id

    def _calculate_spread_bps(self, bids: List[tuple], asks: List[tuple]) -> float:
        """Tính spread theo basis points"""
        if not bids or not asks:
            return 0.0
        best_bid = bids[0][0]
        best_ask = asks[0][0]
        mid = (best_bid + best_ask) / 2
        if mid == 0:
            return 0.0
        return ((best_ask - best_bid) / mid) * 10000

    async def log_experiment_start(
        self,
        experiment: BacktestExperiment
    ) -> str:
        """Ghi log khi một experiment backtest bắt đầu"""

        log_entry = TardisAuditLog(
            log_id="",
            event_type="BACKTEST_STARTED",
            timestamp=datetime.now(timezone.utc),
            experiment_id=experiment.experiment_id,
            snapshot_id=None,
            data_source=DataSource.BINANCE_HISTORICAL_API,
            data_version=DataVersion.V2_OB_1S,
            metadata={
                "strategy_name": experiment.strategy_name,
                "strategy_version": experiment.strategy_version,
                "git_commit": experiment.git_commit,
                "parameter_count": len(experiment.parameters),
                "seed_snapshots": len(experiment.seed_data)
            }
        )

        # Lưu vào experiments table
        with self._get_connection() as conn:
            conn.execute("""
                INSERT INTO experiments
                (experiment_id, strategy_name, strategy_version, created_at,
                 parameters, git_commit, status)
                VALUES (?, ?, ?, ?, ?, ?, ?)
            """, (
                experiment.experiment_id,
                experiment.strategy_name,
                experiment.strategy_version,
                experiment.created_at.isoformat(),
                json.dumps(experiment.parameters),
                experiment.git_commit,
                "RUNNING"
            ))
            conn.commit()

        # Ghi audit log
        with self._get_connection() as conn:
            conn.execute("""
                INSERT INTO audit_logs
                (log_id, event_type, timestamp, experiment_id, snapshot_id,
                 data_source, data_version, metadata, checksum)
                VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
            """, (
                log_entry.log_id,
                log_entry.event_type,
                log_entry.timestamp.isoformat(),
                log_entry.experiment_id,
                log_entry.snapshot_id,
                log_entry.data_source.value,
                log_entry.data_version.value,
                json.dumps(log_entry.metadata),
                log_entry.checksum
            ))
            conn.commit()

        logger.info("experiment_started", experiment_id=experiment.experiment_id)
        return log_entry.log_id

    def get_experiment_lineage(self, experiment_id: str) -> List[Dict]:
        """Truy vấn toàn bộ lineage của một experiment"""
        with self._get_connection() as conn:
            cursor = conn.execute("""
                SELECT al.*, os.symbol, os.mid_price, os.spread_bps, os.download_time
                FROM audit_logs al
                LEFT JOIN orderbook_snapshots os ON al.snapshot_id = os.symbol
                WHERE al.experiment_id = ?
                ORDER BY al.timestamp ASC
            """, (experiment_id,))

            return [dict(row) for row in cursor.fetchall()]

    def verify_data_integrity(self, snapshot_id: str) -> Dict[str, Any]:
        """Xác minh tính toàn vẹn của một snapshot"""
        with self._get_connection() as conn:
            cursor = conn.execute("""
                SELECT * FROM orderbook_snapshots WHERE snapshot_id = ?
            """, (snapshot_id,))

            row = cursor.fetchone()
            if not row:
                return {"valid": False, "error": "Snapshot not found"}

            # Kiểm tra file tồn tại
            file_path = Path(row['file_path'])
            if not file_path.exists():
                return {"valid": False, "error": "File not found", "path": str(file_path)}

            return {
                "valid": True,
                "snapshot_id": row['snapshot_id'],
                "symbol": row['symbol'],
                "checksum": row['checksum'],
                "file_size": file_path.stat().st_size,
                "download_time": row['download_time']
            }

Tích Hợp Binance Data Fetcher

Phần này hướng dẫn cách tích hợp Binance API với hệ thống audit log. Tôi sử dụng HolySheep AI để phân tích dữ liệu orderbook và phát hiện anomaly trong quá trình backtest.

import asyncio
import aiohttp
import time
from datetime import datetime, timedelta, timezone
from typing import List, Dict, Any

class BinanceOrderbookFetcher:
    """
    Fetcher cho Binance orderbook với tích hợp Tardis audit logging.
    Hỗ trợ nhiều nguồn dữ liệu và tự động ghi log.
    """

    def __init__(
        self,
        audit_manager: TardisAuditLogManager,
        api_key: str = "",
        secret_key: str = ""
    ):
        self.audit_manager = audit_manager
        self.api_key = api_key
        self.secret_key = secret_key
        self.session: Optional[aiohttp.ClientSession] = None

    async def __aenter__(self):
        self.session = aiohttp.ClientSession()
        return self

    async def __aexit__(self, *args):
        if self.session:
            await self.session.close()

    async def fetch_historical_depth(
        self,
        symbol: str,
        limit: int = 1000,
        experiment_id: Optional[str] = None
    ) -> OrderbookSnapshot:
        """
        Tải orderbook từ Binance Historical API.
        Đo lường chính xác thời gian download để ghi vào audit log.
        """

        url = f"https://api.binance.com/api/v3/depth"
        params = {"symbol": symbol, "limit": limit}

        download_start = datetime.now(timezone.utc)
        exchange_time_ms = 0
        local_received_ms = 0

        async with self.session.get(url, params=params) as response:
            exchange_time_ms = int(response.headers.get('X-MBX-UTIME', time.time() * 1000))
            data = await response.json()
            local_received_ms = int(time.time() * 1000)

        download_end = datetime.now(timezone.utc)

        # Parse bids và asks
        bids = [(float(p), float(q)) for p, q in data.get('bids', [])]
        asks = [(float(p), float(q)) for p, q in data.get('asks', [])]

        # Tạo snapshot
        snapshot = OrderbookSnapshot(
            symbol=symbol,
            timestamp=datetime.fromtimestamp(exchange_time_ms / 1000, tz=timezone.utc),
            bids=bids,
            asks=asks,
            source=DataSource.BINANCE_HISTORICAL_API,
            version=DataVersion.V2_OB_1S,
            download_time=download_end,
            local_storage_path=f"./data/orderbook/{symbol}_{exchange_time_ms}.json",
            exchange_timestamp_ms=exchange_time_ms,
            local_received_ms=local_received_ms
        )

        # Ghi audit log với timing chính xác
        download_latency_ms = (local_received_ms - exchange_time_ms)
        await self.audit_manager.log_snapshot_download(snapshot, experiment_id)

        print(f"[TARDIS] Downloaded {symbol} orderbook:")
        print(f"  - Exchange timestamp: {exchange_time_ms} ms")
        print(f"  - Local received: {local_received_ms} ms")
        print(f"  - Download latency: {download_latency_ms} ms ({download_latency_ms/1000:.3f}s)")
        print(f"  - Bid/Ask levels: {len(bids)}/{len(asks)}")

        return snapshot

    async def fetch_comprehensive_snapshot(
        self,
        symbol: str,
        start_time: datetime,
        end_time: datetime,
        experiment_id: str
    ) -> List[OrderbookSnapshot]:
        """
        Tải comprehensive snapshot cho một khoảng thời gian.
        Sử dụng cho backtest dài hạn với độ phân giải 1 phút.
        """

        snapshots = []
        current_time = start_time

        while current_time < end_time:
            # Tải dữ liệu 1 phút
            snapshot = await self.fetch_historical_depth(
                symbol=f"{symbol}USDT",
                limit=100,
                experiment_id=experiment_id
            )
            snapshots.append(snapshot)

            # Di chuyển thời gian
            current_time += timedelta(minutes=1)

            # Rate limiting: 10 requests/second cho public API
            await asyncio.sleep(0.1)

        return snapshots

    async def analyze_with_holysheep(
        self,
        snapshots: List[OrderbookSnapshot],
        analysis_prompt: str
    ) -> Dict[str, Any]:
        """
        Sử dụng HolySheep AI để phân tích dữ liệu orderbook.
        Ví dụ: phát hiện anomaly, phân tích liquidity patterns.
        """

        # Tổng hợp data cho analysis
        summary = self._summarize_snapshots(snapshots)

        # Gọi HolySheep AI
        async with aiohttp.ClientSession() as session:
            url = "https://api.holysheep.ai/v1/chat/completions"
            headers = {
                "Authorization": f"Bearer YOUR_HOLYSHEEP_API_KEY",
                "Content-Type": "application/json"
            }
            payload = {
                "model": "gpt-4.1",
                "messages": [
                    {"role": "system", "content": "Bạn là chuyên gia phân tích dữ liệu tài chính. Phân tích orderbook data."},
                    {"role": "user", "content": f"Analyze this orderbook summary:\n\n{summary}\n\n{analysis_prompt}"}
                ],
                "temperature": 0.3
            }

            async with session.post(url, json=payload, headers=headers) as response:
                result = await response.json()
                return result

    def _summarize_snapshots(self, snapshots: List[OrderbookSnapshot]) -> str:
        """Tạo summary của nhiều snapshots"""
        if not snapshots:
            return "No snapshots provided"

        spreads = []
        mid_prices = []

        for snap in snapshots:
            if snap.bids and snap.asks:
                spread = (snap.asks[0][0] - snap.bids[0][0]) / ((snap.asks[0][0] + snap.bids[0][0]) / 2) * 10000
                mid = (snap.asks[0][0] + snap.bids[0][0]) / 2
                spreads.append(spread)
                mid_prices.append(mid)

        return f"""
Orderbook Summary:
- Total snapshots: {len(snapshots)}
- Symbol: {snapshots[0].symbol}
- Time range: {snapshots[0].timestamp} to {snapshots[-1].timestamp}
- Average spread: {sum(spreads)/len(spreads) if spreads else 0:.2f} bps
- Price range: {min(mid_prices) if mid_prices else 0} to {max(mid_prices) if mid_prices else 0}
- Total bid liquidity: {sum(q for _, q in snapshots[0].bids):.4f}
- Total ask liquidity: {sum(q for _, q in snapshots[0].asks):.4f}
"""

Demo: Chạy Full Backtest Với Audit Logging

Dưới đây là script hoàn chỉnh để chạy một backtest với đầy đủ audit logging. Bạn có thể sao chép và chạy ngay.

import asyncio
import json
from datetime import datetime, timedelta, timezone
from tardis_audit import (
    TardisAuditLogManager,
    BinanceOrderbookFetcher,
    BacktestExperiment,
    DataSource,
    DataVersion
)
import hashlib

async def main():
    # Khởi tạo audit manager
    audit_manager = TardisAuditLogManager(
        db_path="./data/tardis_audit.db",
        log_dir="./data/audit_logs"
    )

    # Tạo experiment
    experiment = BacktestExperiment(
        experiment_id="exp_2025_arbitrage_v3",
        strategy_name="Binance_Arbitrage",
        strategy_version="3.2.1",
        created_at=datetime.now(timezone.utc),
        parameters={
            "spread_threshold_bps": 5.0,
            "position_size": 1000,
            "max_position": 5,
            "slippage_bps": 2.0
        },
        git_commit=hashlib.md5(b"commit_hash_here").hexdigest()[:7],
        seed_data=[]
    )

    # Log experiment start
    log_id = await audit_manager.log_experiment_start(experiment)
    print(f"Started experiment: {experiment.experiment_id} (log: {log_id})")

    # Fetch dữ liệu với audit logging
    symbols = ["BTC", "ETH", "BNB", "SOL", "XRP"]

    async with BinanceOrderbookFetcher(audit_manager) as fetcher:
        for symbol in symbols:
            try:
                snapshot = await fetcher.fetch_historical_depth(
                    symbol=f"{symbol}USDT",
                    limit=500,
                    experiment_id=experiment.experiment_id
                )
                experiment.seed_data.append(snapshot.symbol)

            except Exception as e:
                print(f"Error fetching {symbol}: {e}")
                # Log error vào audit
                continue

    # Truy vấn lineage để verify
    print("\n" + "="*60)
    print("EXPERIMENT LINEAGE")
    print("="*60)

    lineage = audit_manager.get_experiment_lineage(experiment.experiment_id)
    for entry in lineage:
        print(f"\n[{entry['timestamp']}] {entry['event_type']}")
        if entry.get('symbol'):
            print(f"  Symbol: {entry['symbol']}")
            print(f"  Mid Price: ${entry.get('mid_price', 'N/A')}")
            print(f"  Spread: {entry.get('spread_bps', 'N/A')} bps")

    # Verify data integrity
    print("\n" + "="*60)
    print("DATA INTEGRITY CHECK")
    print("="*60)

    for symbol in symbols:
        result = audit_manager.verify_data_integrity(symbol)
        status = "✓" if result['valid'] else "✗"
        print(f"{status} {symbol}: {result}")

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

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

1. Lỗi "Checksum Mismatch" Khi Verify Snapshot

Mô tả: Khi chạy verify_data_integrity(), hệ thống báo checksum không khớp với dữ liệu gốc.

Nguyên nhân gốc: File orderbook đã bị sửa đổi sau khi tải, hoặc quá trình nén/giải nén làm thay đổi nội dung.

def verify_data_integrity(self, snapshot_id: str) -> Dict[str, Any]:
    """Phiên bản cải tiến với checksum verification đầy đủ"""

    with self._get_connection() as conn:
        cursor = conn.execute("""
            SELECT * FROM orderbook_snapshots WHERE snapshot_id = ?
        """, (snapshot_id,))
        row = cursor.fetchone()

        if not row:
            return {"valid": False, "error": "Snapshot not found in database"}

        file_path = Path(row['file_path'])

        if not file_path.exists():
            return {
                "valid": False,
                "error": "File deleted or moved",
                "expected_path": str(file_path),
                "suggestion": "Check backup directory or re-download"
            }

        # Đọc file và tính lại checksum
        with open(file_path, 'rb') as f:
            file_content = f.read()
            actual_checksum = hashlib.sha256(file_content).hexdigest()

        stored_checksum = row['checksum']

        if actual_checksum != stored_checksum:
            # Log incident
            logger.error(
                "checksum_mismatch_detected",
                snapshot_id=snapshot_id,
                expected=stored_checksum[:16],
                actual=actual_checksum[:16]
            )

            return {
                "valid": False,
                "error": "Checksum mismatch - data corrupted or modified",
                "stored_checksum": stored_checksum[:16],
                "actual_checksum": actual_checksum[:16],
                "file_size": len(file_content),
                "last_modified": datetime.fromtimestamp(
                    file_path.stat().st_mtime,
                    tz=timezone.utc
                ).isoformat(),
                "recovery_action": "Re-download from original source with same timestamp"
            }

        return {
            "valid": True,
            "snapshot_id": snapshot_id,
            "checksum_verified": True,
            "file_size_bytes": len(file_content)
        }

2. Lỗi "Rate Limit Exceeded" Khi Tải Dữ liệu Binance

Mô tả: API trả về HTTP 429 sau khi tải được vài trăm snapshots.

Nguyên nhân: Binance giới hạn request rate: 10 requests/second cho public API, 20/second cho weighted endpoint.

class BinanceOrderbookFetcher:
    """
    Fetcher với adaptive rate limiting
    """

    def __init__(self, audit_manager, api_key: str = "", secret_key: str = ""):
        self.audit_manager = audit_manager
        self.api_key = api_key
        self.secret_key = secret_key
        self.session: Optional[aiohttp.ClientSession] = None

        # Rate limiting state
        self._request_times: List[float] = []
        self._rate_limit_window = 1.0  # 1 second window
        self._max_requests_per_window = 10  # Conservative limit

        # Adaptive throttling
        self._consecutive_429 = 0
        self._backoff_seconds = 0.1

    async def _wait_for_rate_limit(self):
        """Chờ đến khi được phép gửi request tiếp theo"""

        now = time.time()

        # Xóa request cũ khỏi window
        self._request_times = [
            t for t in self._request_times
            if now - t < self._rate_limit_window
        ]

        # Nếu đã đạt limit, chờ
        if len(self._request_times) >= self._max