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êu và nên dự trữ bao nhiêu tại mỗi thời điểm. Trong thị trường tài chính:
- mid price = giá trung bình giữa lệnh mua và bán tốt nhất.
- inventory = số coin bạn đang giữ (dương = đang "kho" nhiều, âm = đang bán khô).
- sigma (σ) = độ "giật" của giá — giá nhảy mạnh thì spread phải nới rộng ra.
- gamma (γ) = mức bạn sợ rủi ro inventory. Người thận trọng chọn γ lớn.
Công thức cốt lõi (mình viết lại bằng tiếng Việt cho dễ hiểu):
- Giá tham chiếu (reservation price) = mid − inventory × γ × σ² × τ
- Nửa spread tối ưu = (γ × σ² × τ) / 2 + (1 / γ) × ln(1 + γ / κ)
- Giá bid = reservation − nửa spread
- Giá ask = reservation + nửa spread
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:
- Lưu trữ lịch sử nhiều năm cho hơn 30 sàn (Binance, Bybit, Deribit, OKX…).
- Định dạng CSV nén gzip dễ mở bằng Pandas.
- Có gói miễn phí dùng thử cho trader cá nhân.
Giá tham khảo công khai của Tardis (cập nhật 2026):
- Binance book_snapshot_25: 0,10 USD / 1 triệu messages.
- Binance trades: 0,05 USD / 1 triệu messages.
- Deribit options book: 0,30 USD / 1 triệu messages.
4. Chuẩn bị môi trường — từng bước (có gợi ý ảnh chụp)
- 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.
- 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.
- Tạo thư mục dự án: mở terminal gõ
mkdir avellaneda-backtest && cd avellaneda-backtest. - Tạo môi trường ảo:
python -m venv venvrồivenv\Scripts\activate(Windows) hoặcsource venv/bin/activate(macOS/Linux). - Cài thư viện:
pip install pandas numpy requests matplotlib. - 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".
- 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 ask và mid 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