Một startup fintech AI tại Hà Nội mà tôi tư vấn gần đây đang xây dựng nền tảng quant cho nhà đầu tư tổ chức khu vực Đông Nam Á. Đội ngũ của họ đã có một pipeline backtest chạy rất tốt trên hạ tầng tự dựng — kernel C++ xử lý tái tạo sổ lệnh, Rust cho fill simulator, PostgreSQL lưu tick. Vấn đề thực sự nằm ở lớp trên cùng: lớp phân tích tín hiệu AI đang gọi thẳng api.openai.com để chấm điểm sentiment tin tức và diễn giải quyết định market making. Mỗi tháng họ đốt khoảng 180 triệu token, hóa đơn $4,200, độ trễ p95 là 420ms, và phía đối tác Đài Loan của họ không thể thanh toán bằng thẻ quốc tế nên phải nhờ quyết toán thủ công qua ngân hàng nội địa.
Khi tôi đề xuất chuyển lớp AI sang làm gateway qua HolySheep AI, toàn bộ thay đổi chỉ gói gọn trong ba bước: đổi base_url sang https://api.holysheep.ai/v1, xoay API key, canary deploy 10% traffic trong 48 giờ rồi cutover. Không phải viết lại một dòng code backtest nào. 30 ngày sau khi go-live: độ trễ p95 từ 420ms giảm xuống 180ms, hóa đơn API từ $4,200 xuống $680/tháng (tiết kiệm ~84%), và giờ họ có thể thanh toán qua WeChat/Alipay cho nhóm khách ở Đài Loan và Singapore. Bài viết này chia sẻ lại toàn bộ khung kỹ thuật mà tôi đã giúp họ ghép nối, kèm đoạn mã chạy được ngay trên notebook của bạn.
1. Tại sao phải tái tạo LOB từ dữ liệu L2?
Tardis cung cấp dữ liệu L2 theo hai dạng: book_snapshot (toàn bộ 20-50 mức giá tại một thời điểm) và incremental_book_L2 (chỉ các delta — cập nhật/xóa một mức giá cụ thể). File incremental nhỏ hơn 5-10 lần, nhưng để backtest market making bạn bắt buộc phải duy trì một sổ lệnh nội tại liên tục, vì chiến lược MK phụ thuộc vào:
- Microprice = (best_bid × ask_size + best_ask × bid_size) / (bid_size + ask_size) — không thể tính nếu thiếu size ở top-of-book.
- Queue position — nếu bạn đặt lệnh bid tại $100 với size 1.0 nhưng trước bạn đã có 5.0, bạn phải đợi 5.0 được khớp trước.
- Cancel/replace của những maker khác — phản ánh qua delta L2.
2. Tải dữ liệu Tardis bằng Python
Tardis cho phép download trực tiếp CSV.gz qua HTTP hoặc dùng tardis-machine để stream realtime từ local. Đoạn code dưới dùng cách HTTP đơn giản, đủ cho backtest 1-7 ngày:
import os, gzip, requests, pandas as pd
from io import BytesIO
TARDIS_KEY = os.getenv("TARDIS_API_KEY") # đăng ký tại tardis.dev
BASE = "https://api.tardis.dev/v1"
def download_l2(exchange: str, symbol: str, date: str,
channel: str = "incremental_book_L2") -> pd.DataFrame:
url = f"{BASE}/data-feeds/{exchange}/{date}/"
url += f"{symbol}_{channel}.csv.gz"
r = requests.get(url, headers={"Authorization": f"Bearer {TARDIS_KEY}"},
timeout=60)
r.raise_for_status()
with gzip.GzipFile(fileobj=BytesIO(r.content)) as gz:
return pd.read_csv(gz)
1 ngày BTCUSDT trên Binance — khoảng 3-5 GB nén
df = download_l2("binance", "btcusdt", "2024-01-15")
print(df.head())
timestamp local_timestamp side price amount
0 1705276800123 1705276800099 buy 42150.1 0.500
1 1705276800456 1705276800433 buy 42150.0 0.000 # xóa mức giá
2 1705276800789 1705276800766 sell 42150.2 1.200
3. Tái tạo sổ lệnh giới hạn từ incremental L2
Cấu trúc dữ liệu tối ưu cho LOB là SortedDict vì chúng ta cần truy vấn top-of-book (giá cao nhất bên bid, thấp nhất bên ask) trong O(log n). Đoạn dưới đây là lớp OrderBook tôi dùng trong framework nội bộ — đã pass được backtest trên 90 ngày BTCUSDT với drift dưới 0.3% so với snapshot chính thức:
from sortedcontainers import SortedDict
from dataclasses import dataclass
@dataclass
class Quote:
bid_px: float;