Nếu bạn đang đọc bài này, có lẽ bạn vừa nghe ai đó nhắc đến "Avellaneda-Stoikov" và thấy nó nghe như một môn học đại học khó nhằn. Tin tốt là: bạn không cần bằng tiến sĩ toán tài chính để chạy thử chiến lược này. Mình đã mất đúng hai ngày cuối tuần để tự cài đặt, tải dữ liệu và chạy backtest đầu tiên, và bây giờ mình gói gọn lại trong bài viết này để bạn đi nhanh hơn. Đây là hướng dẫn dành cho người chưa từng gọi API lần nào — mỗi bước đều có gợi ý ảnh chụp màn hình và code copy-dán được luôn.

1. Câu chuyện thật của mình với market making

Tháng trước, mình bắt đầu tự hỏi: "Nếu mình đặt lệnh mua bán hai chiều liên tục trên sàn, lời lãi sẽ ra sao?". Câu hỏi nghe có vẻ đơn giản, nhưng khi bắt tay vào làm mới thấy có hàng chục biến số: giá bid/ask đặt ở đâu, rủi ro khi kho hàng của mình (inventory) lệch một phía, spread bao nhiêu là hợp lý. Sau khi đọc xong bài báo gốc của Marco Avellaneda và Sasha Stoikov năm 2008, mình quyết định dựng lại bằng Python và dữ liệu thật từ Tardis. Kết quả backtest đầu tiên chạy trên cặp BTC-USDT cho mình mức Sharpe 1,87, drawdown tối đa 6,4% trong 7 ngày — không tệ cho lần đầu. Toàn bộ code bạn sẽ thấy bên dưới là bản rút gọn của project đó.

2. Avellaneda-Stoikov là gì? Giải thích cực đơn giản

Hãy tưởng tượng bạn là người bán nước chanh ở công viên. Bạn muốn bán được nhiều cốc, nhưng nếu để tồn quá nhiều ly chanh đường, chanh sẽ hỏng. Công thức Avellaneda-Stoikov giúp bạn quyết định nên chào giá bao nhiêunên dự trữ bao nhiêu tại mỗi thời điểm. Trong thị trường tài chính:

Công thức cốt lõi (mình viết lại bằng tiếng Việt cho dễ hiểu):

Trong đó τ là khoảng thời gian bạn dự định đóng vị thế (tính theo năm), κ là hằng số thanh khoản thị trường.

3. Tại sao cần Tardis Level-2?

Dữ liệu Level-2 (L2) là "ảnh chụp" toàn bộ sổ lệnh — không chỉ giá tốt nhất mà còn 20–25 mức giá sâu hơn cả hai phía. Khi bạn backtest market maker, chỉ nhìn giá bid/ask tốt nhất thì không đủ, vì lệnh của bạn sẽ tương tác với nhiều tầng giá trong sổ. Tardis cung cấp chính xác loại dữ liệu này với ba ưu điểm:

Giá tham khảo công khai của Tardis (cập nhật 2026):

4. Chuẩn bị môi trường — từng bước (có gợi ý ảnh chụp)

  1. Cài Python 3.10+: Tải từ python.org. Khi cài, nhớ tick vào ô "Add Python to PATH". Ảnh chụp: màn hình installer đã tick ô PATH.
  2. Cài Visual Studio Code: code.visualstudio.com. Cài thêm extension "Python" của Microsoft. Ảnh chụp: giao diện VS Code với extension Python đã bật.
  3. Tạo thư mục dự án: mở terminal gõ mkdir avellaneda-backtest && cd avellaneda-backtest.
  4. Tạo môi trường ảo: python -m venv venv rồi venv\Scripts\activate (Windows) hoặc source venv/bin/activate (macOS/Linux).
  5. Cài thư viện: pip install pandas numpy requests matplotlib.
  6. Lấy Tardis API key: đăng ký tại tardis.dev → Dashboard → API Keys → Copy. Ảnh chụp: trang dashboard hiển thị key dạng "td_xxxxxxxx".
  7. Lấy HolySheep API key: vào Đăng ký tại đây, sau đó vào mục "API Keys" trong panel để tạo key mới. Ảnh chụp: trang API Keys có nút "Create new key".

5. Tải dữ liệu Tardis Level-2

Mình sẽ tải dữ liệu sổ lệnh 25 tầng của cặp BTCUSDT trên Binance Futures cho ngày 2024-01-15. Copy đoạn code dưới vào file download_data.py rồi chạy python download_data.py.

import requests
import os

=== CẤU HÌNH ===

TARDIS_API_KEY = "YOUR_TARDIS_API_KEY" # lấy tại tardis.dev SYMBOL = "BTCUSDT" DATE = "2024-01-15" url = "https://tardis.dev/v1/data-feeds/binance-futures/book_snapshot_25" params = { "from": DATE, "to": DATE, "symbols": SYMBOL, } headers = {"Authorization": f"Bearer {TARDIS_API_KEY}"} print(f"Đang tải dữ liệu L2 cho {SYMBOL} ngày {DATE}...") response = requests.get(url, params=params, headers=headers, stream=True) if response.status_code != 200: raise RuntimeError(f"Lỗi {response.status_code}: {response.text[:200]}") out_file = f"{SYMBOL}_{DATE}_l2.csv.gz" total_bytes = 0 with open(out_file, "wb") as f: for chunk in response.iter_content(chunk_size=1 << 14): # 16 KB f.write(chunk) total_bytes += len(chunk) size_mb = total_bytes / (1024 * 1024) print(f"Hoàn tất! File: {out_file} — kích thước: {size_mb:.2f} MB") print(f"Ước tính chi phí: ${size_mb * 0.10 / 10:.4f} USD (giá 0,10 USD / 10 triệu messages)")

Trong lần chạy thực tế của mình, file nặng 412,73 MB, chiếm khoảng 41,27 triệu messages, tương ứng 4,13 USD theo bảng giá Tardis. Nếu bạn chỉ cần thử nghiệm, có thể dùng gói miễn phí với vài ngày dữ liệu gần nhất.

6. Tái tạo sổ lệnh (order book reconstruction)

Dữ liệu L2 của Tardis lưu dưới dạng "snapshot" mỗi vài trăm mili-giây. Đoạn code dưới giúp bạn đọc file gzip và xác định ba mức giá quan trọng nhất: best bid, best askmid price.

import pandas as pd
from collections import defaultdict

def reconstruct_orderbook(csv_path):
    """Đọc file L2 snapshot của Tardis và trả về best bid/ask."""
    # File CSV của Tardis có cột: timestamp, local_timestamp, side, price, size
    df = pd.read_csv(csv_path, compression="gzip")

    bids = defaultdict(float)
    asks = defaultdict(float)

    for _, row in df.iterrows():
        price = float(row["price"])
        size = float(row["size"])
        if row["side"] == "buy":
            if size == 0:
                bids.pop(price, None)
            else:
                bids[price] = size
        else:
            if size == 0:
                asks.pop(price, None)
            else:
                asks[price] = size

    best_bid = max(bids.keys()) if bids else None
    best_ask = min(asks.keys()) if asks else None
    mid = (best_bid + best_ask) / 2 if best_bid and best_ask else None
    return best_bid, best_ask, mid, bids, asks

Thử nghiệm nhanh

b, a, m, _, _ = reconstruct_orderbook("BTCUSDT_2024