Kinh Doanh Chênh Lệch Thống Kê và Giao Dịch Theo Cặp trên Thị Trường Crypto: Từ Đồng Tích Hợp đến Bộ Lọc Kalman
Năm 1987, một nhóm các nhà vật lý tại Morgan Stanley kiếm được 50 triệu đô la trong một năm bằng cách giao dịch các cặp cổ phiếu theo thuật toán mà không ai trong số họ có thể giải thích đầy đủ cho ban quản lý ngân hàng. Ban quản lý không phản đối. Năm 2026, bạn có thể áp dụng ý tưởng tương tự trên các sàn giao dịch crypto — với hợp đồng tương lai vĩnh cửu, thị trường hoạt động 24/7 và thanh khoản mà Nunzio Tartaglia chắc hẳn sẽ thèm muốn. Nhưng có một điều cần lưu ý: những gì hoạt động với cổ phiếu Ford và GM trong kỷ nguyên tiền internet đòi hỏi sự thích ứng nghiêm túc cho một thế giới mà BTC có thể giảm 20% chỉ trong một đêm và lãi suất tài trợ có thể đổi chiều chỉ trong một block.
Bài viết này là phân tích toàn diện về kinh doanh chênh lệch thống kê và giao dịch theo cặp trên thị trường crypto. Từ lý thuyết toán học (đồng tích hợp, quá trình Ornstein-Uhlenbeck, bộ lọc Kalman) đến code Python hoạt động thực tế mà bạn có thể chạy trên dữ liệu thực. Phong cách kỹ thuật: chúng tôi giải thích các công thức, trình bày code và không che giấu những cạm bẫy.
1. Lịch Sử Ngắn Gọn: Từ Tu Sĩ Dòng Tên đến Các Quant
Kinh doanh chênh lệch thống kê ở dạng hiện đại được sinh ra tại bàn giao dịch của Morgan Stanley vào giữa những năm 1980. Nunzio Tartaglia — cựu tu sĩ Dòng Tên với bằng tiến sĩ vật lý — đã tập hợp một nhóm các nhà toán học, nhà vật lý và nhà khoa học máy tính. Mục tiêu: tìm kiếm các mô hình trong giá cổ phiếu mà các nhà giao dịch truyền thống không thể thấy.
Ý tưởng rất đơn giản đến ngây thơ. Nếu cổ phiếu Coca-Cola và Pepsi lịch sử di chuyển cùng nhau (điều này hợp lý — họ bán cùng loại nước có đường trong chai màu khác nhau), thì sự phân kỳ trong giá của chúng là một bất thường tạm thời. Mua cổ phiếu tụt hậu, bán cổ phiếu dẫn đầu, chờ hội tụ, chốt lợi nhuận. Đây là chiến lược trung tính thị trường: hướng của thị trường không quan trọng với chúng ta.
Nhóm của Tartaglia bao gồm những người sau này sẽ thay đổi toàn bộ Phố Wall:
- David Shaw — sau đó thành lập D.E. Shaw & Co., một trong những quỹ đầu tư định lượng lớn nhất
- Peter Muller — thành lập PDT Partners, nhóm stat arb huyền thoại bên trong Morgan Stanley
- Robert Frey — sau đó gia nhập Renaissance Technologies dưới sự lãnh đạo của Jim Simons
Nhóm hoạt động như một phòng nghiên cứu bên trong một ngân hàng đầu tư. Tự động hóa là hiện đại nhất: các cụm VAX tạo ra tín hiệu, và lệnh thực thi qua các terminal. Trong những năm tốt nhất (1987-1988), chiến lược kiếm được hàng chục triệu đô la. Rồi đến hai năm thua liên tiếp, và năm 1989 Morgan Stanley đóng cửa bàn giao dịch.
Nhưng ý tưởng đã lan rộng. Các cựu thành viên của nhóm đã truyền bá khái niệm giao dịch theo cặp trên toàn Phố Wall. Gatev, Goetzmann và Rouwenhorst công bố bài báo học thuật kinh điển vào năm 2006 — "Pairs Trading: Performance of a Relative-Value Arbitrage Rule" — cho thấy chiến lược giao dịch theo cặp đơn giản liên tục mang lại lợi nhuận hàng năm ~11% từ năm 1962 đến năm 2002 trên cổ phiếu Mỹ. Đây là câu trả lời thuyết phục cho giả thuyết thị trường hiệu quả: thị trường nói chung có thể hiệu quả, nhưng các cặp tài sản cụ thể có hệ thống lệch khỏi trạng thái cân bằng.
Ngày nay, kinh doanh chênh lệch thống kê là một ngành với hàng trăm tỷ đô la AUM, và các thị trường crypto mang lại mảnh đất màu mỡ đặc biệt: thanh khoản phân mảnh, cấu trúc vi mô còn non trẻ, giao dịch suốt ngày đêm, và hợp đồng tương lai vĩnh cửu với lãi suất tài trợ — một công cụ đơn giản là không tồn tại trên các thị trường truyền thống.
2. Nền Tảng Toán Học: Tương Quan Là Một Cái Bẫy
Tại Sao Tương Quan Không Hiệu Quả
Hãy bắt đầu với sai lầm mà hầu như mọi quant mới học đều mắc phải: "BTC và ETH có tương quan với hệ số 0.85, vì vậy chúng ta có thể giao dịch cặp này." Không. Không thể được. À thực ra có thể — nhưng bạn sẽ thua tiền.
Tương quan đo lường mối quan hệ tuyến tính giữa lợi nhuận của hai tài sản. Hai tài sản có thể tương quan hoàn hảo, nhưng giá của chúng có thể phân kỳ mãi mãi. Ví dụ kinh điển: hai bước đi ngẫu nhiên với các gia số tương quan — chúng phân kỳ vô hạn mặc dù tương quan cao. Bạn sẽ mở vị thế mong chờ "hội tụ" sẽ không bao giờ đến.

Đồng Tích Hợp: Cách Tiếp Cận Đúng Đắn
Đồng tích hợp là thuộc tính của chuỗi giá, không phải lợi nhuận. Hai chuỗi không dừng X(t) và Y(t) được gọi là đồng tích hợp nếu tồn tại một tổ hợp tuyến tính:
S(t) = Y(t) - β · X(t)
là dừng — nghĩa là nó hồi phục về một giá trị trung bình. Hệ số β được gọi là tỷ lệ phòng ngừa, và S(t) là spread.
Trực giác: BTC và ETH có thể tăng vọt lên trăng hoặc lao xuống vực thẳm, nhưng nếu sự khác biệt của chúng (được chuẩn hóa đúng cách) dao động quanh một mức cố định — đó là đồng tích hợp. Và đó chính xác là những gì chúng ta cần để giao dịch.
Kiểm Định Engle-Granger (1987)
Một thủ tục hai bước mà Robert Engle và Clive Granger đã được trao giải Nobel Kinh tế năm 2003:
Bước 1. Hồi quy OLS: Y(t) = α + β · X(t) + ε(t). Chúng ta thu được tỷ lệ phòng ngừa β và phần dư ε(t).
Bước 2. Kiểm định ADF (Augmented Dickey-Fuller) trên phần dư ε(t). Giả thuyết null: ε(t) có gốc đơn vị (không dừng). Nếu p-value < 0.05, chúng ta bác bỏ H₀ — các chuỗi là đồng tích hợp.
Quan trọng: đối với kiểm định đồng tích hợp, bạn không thể sử dụng các giá trị tới hạn ADF tiêu chuẩn. Các giá trị tới hạn Engle-Granger được xác định qua mô phỏng Monte Carlo và tính đến sự phụ thuộc giữa các biến trong hồi quy OLS. Trong statsmodels, điều này được thực hiện đúng trong hàm coint().
Kiểm Định Johansen
Đối với hệ thống có nhiều hơn hai biến (ví dụ: BTC, ETH và SOL đồng thời), kiểm định Johansen được sử dụng. Nó tìm tất cả các mối quan hệ đồng tích hợp trong hệ thống và cho phép xây dựng danh mục đầu tư của nhiều tài sản. Kiểm định dựa trên mô hình VAR (véc tơ tự hồi quy) và sử dụng hai tiêu chí: thống kê dấu vết và thống kê giá trị riêng tối đa.
Quá Trình Ornstein-Uhlenbeck
Nếu spread được đồng tích hợp, động lực của nó có thể được mô hình hóa như một quá trình Ornstein-Uhlenbeck (OU):
dS(t) = θ(μ - S(t))dt + σ dW(t)
trong đó:
- θ — tốc độ hồi phục trung bình
- μ — mức trung bình dài hạn
- σ — biến động
- W(t) — quá trình Wiener (chuyển động Brown)
Từ các tham số quá trình OU, chu kỳ bán hủy của hồi phục trung bình được tính:
t½ = ln(2) / θ
Chu kỳ bán hủy là một chỉ số cực kỳ quan trọng. Nếu t½ = 5 ngày, spread hồi phục về mức trung bình trong khoảng 5 ngày. Nếu t½ = 200 ngày, bạn sẽ phải ngồi trong một vị thế nửa năm chờ hội tụ. Đối với các chiến lược crypto, chu kỳ bán hủy tối ưu là 1-30 ngày. Ngắn hơn — quá nhanh, hoa hồng ăn hết lợi nhuận. Dài hơn — quá chậm, rủi ro thay đổi cấu trúc.
Trong thực tế, θ được ước lượng qua hồi quy:
ΔS(t) = a + b · S(t-1) + ε(t)
trong đó θ = -b, và t½ = -ln(2) / b.
Chuẩn Hóa Z-Score
Để tạo tín hiệu giao dịch, spread được chuẩn hóa:
z(t) = (S(t) - μ̂) / σ̂
trong đó μ̂ và σ̂ là giá trị trung bình và độ lệch chuẩn lăn của spread. Z-score cho thấy spread đã lệch bao nhiêu độ lệch chuẩn so với mức trung bình. Ngưỡng vào lệnh thông thường: |z| > 2.0; ngưỡng thoát lệnh: |z| < 0.5.
3. Lựa Chọn Cặp trên Thị Trường Crypto
BTC-ETH: Cặp Kinh Điển (Đôi Khi) Hoạt Động
BTC và ETH là cặp rõ ràng nhất và thanh khoản nhất. Tương quan lợi nhuận liên tục trên 0.7. Nhưng đồng tích hợp lại là câu chuyện khác. Nó xuất hiện và biến mất:
- Trong thị trường đi ngang năm 2023, BTC/ETH đồng tích hợp đáng tin cậy (p-value < 0.01)
- Trong giai đoạn phân kỳ 2024-2025 (BTC tăng theo dòng vốn ETF, ETH tụt hậu), đồng tích hợp bị phá vỡ
- Đến đầu năm 2026, sau khi ra mắt ETH ETF và phục hồi tỷ lệ ETH/BTC, đồng tích hợp ổn định trở lại
Kết luận: đồng tích hợp phải được giám sát liên tục. Các tham số hồi quy được tính toán lại trên cửa sổ lăn, và chiến lược tự động tắt nếu p-value kiểm định ADF vượt quá ngưỡng.
Các Cặp Theo Lĩnh Vực
Thị trường crypto được phân đoạn thuận tiện theo lĩnh vực, và các cặp trong cùng lĩnh vực thường thể hiện đồng tích hợp ổn định:
| Lĩnh Vực | Ví Dụ Cặp | Đặc Điểm |
|---|---|---|
| Blockchain L1 | SOL/AVAX, NEAR/APT | Thanh khoản cao, chu kỳ bán hủy 3-10 ngày |
| Giao thức DeFi | AAVE/COMP, UNI/SUSHI | Thanh khoản trung bình, chu kỳ bán hủy 5-15 ngày |
| Giải pháp L2 | ARB/OP, MATIC/MANTA | Biến động spread cao |
| Memecoin | DOGE/SHIB | Khó đoán nhưng thú vị (không khuyến nghị) |
Các cặp tốt nhất cho stat arb có ba thuộc tính: (1) đồng tích hợp ổn định trên cửa sổ lịch sử >6 tháng, (2) thanh khoản đủ — khối lượng hàng ngày >$10M mỗi tài sản, (3) chu kỳ bán hủy hợp lý — từ 1 đến 30 ngày.
Spot vs Hợp Đồng Tương Lai Vĩnh Cửu (Cơ Sở)
Một loại "cặp" riêng biệt là cùng một tài sản trên thị trường spot và futures. Sự khác biệt giữa giá hợp đồng tương lai vĩnh cửu và giá spot (cơ sở) là dừng theo định nghĩa: cơ chế lãi suất tài trợ nén nó trở về không. Điều này làm cho giao dịch cơ sở trở thành một trong những hình thức stat arb đáng tin cậy nhất trong crypto.
4. Ba Cách Tiếp Cận Giao Dịch
A. Giao Dịch Cơ Sở: Spot-Futures và Carry Lãi Suất Tài Trợ
Hình thức stat arb "thuần túy" nhất trong crypto. Cơ chế:
- Mua tài sản trên spot (ví dụ: 1 BTC)
- Mở short trên hợp đồng tương lai vĩnh cửu (1 BTC)
- Nếu lãi suất tài trợ dương (long trả cho short) — bạn nhận tài trợ mỗi 8 giờ
Với lãi suất tài trợ trung bình 0.01% mỗi 8 giờ, đó là ~0.03% mỗi ngày hoặc ~11% hàng năm không có rủi ro định hướng. Trong thị trường bull, lãi suất tài trợ có thể tăng lên 0.05-0.1% mỗi 8 giờ — đó đã là 55-110% hàng năm.
Rủi ro: tài trợ âm (thị trường đảo chiều), thanh lý vị thế short trong khi giá tăng mạnh (cần bộ đệm ký quỹ), và phí sàn giao dịch.
Tính đến tháng 3 năm 2026, lãi suất tài trợ BTC trung bình đã ổn định ở mức ~0.015% mỗi 8 giờ — khoảng 50% cao hơn mức năm 2024.
B. Kinh Doanh Chênh Lệch Xuyên Sàn
Cùng một đồng coin, hai sàn giao dịch, giá khác nhau. Lý do — sự khác biệt về thanh khoản, thành phần nhà giao dịch và tốc độ cập nhật sổ lệnh.
Ví dụ: BTC trên Binance: 87,175. Spread: $25 (0.029%).
Chiến lược: mua trên Binance, bán trên Bybit. Vấn đề: vào thời điểm cả hai lệnh được thực thi, spread có thể đã biến mất. Giải pháp: duy trì số dư trên cả hai sàn và thực thi đồng thời.
Phí điển hình:
- Binance: ~0.075% taker (với giảm giá ~0.05%)
- Bybit: ~0.03% taker (VIP)
- Tổng cộng: ~0.08%
Điều này có nghĩa là spread phải vượt quá 0.08% để chiến lược có lợi nhuận. Năm 2026, các spread như vậy xuất hiện:
- Trên các cặp ít thanh khoản hơn (altcoin) — thường xuyên
- Trên các cặp chính (BTC, ETH) — chỉ trong những thời điểm biến động cao
- Giữa CEX và DEX — thường xuyên hơn, nhưng với rủi ro MEV và trượt giá
Không có co-location, độ trễ API là 10-100 ms. Với mạng tối ưu hóa — ~1 ms. Hầu hết nhà giao dịch bán lẻ hoạt động trong phạm vi 100-500 ms, đủ cho nhiều chiến lược kinh doanh chênh lệch nhưng không đủ để cạnh tranh với các tổ chức.
C. Giao Dịch Theo Cặp Với Đòn Bẩy
Giao dịch theo cặp cổ điển trên hai tài sản khác nhau sử dụng đòn bẩy. Đây là phức tạp nhất trong ba chiến lược — và có tiềm năng lợi nhuận nhất.
Cơ chế sử dụng cặp SOL/AVAX làm ví dụ:
- Tính toán tỷ lệ phòng ngừa β (ví dụ: β = 1.3)
- Khi z-score > +2: short SOL, long AVAX × β
- Khi z-score < -2: long SOL, short AVAX × β
- Thoát lệnh: |z-score| < 0.5 hoặc timeout (ví dụ: 30 ngày)
Với đòn bẩy 3x mỗi chiều và hồi phục spread trung bình 2σ → 3σ:
- Lợi nhuận mục tiêu mỗi giao dịch: ~3-6%
- Tần suất trung bình: 2-4 giao dịch mỗi tháng mỗi cặp
- Lợi nhuận hàng năm dự kiến: 30-60% (trước hoa hồng và trượt giá)
Rủi ro chính: tương quan có thể phá vỡ vào thời điểm tồi tệ nhất (thường trong sự cố thị trường). Chi tiết hơn trong phần 8.
5. Bộ Lọc Kalman cho Tỷ Lệ Phòng Ngừa Thích Ứng
Tại Sao Tỷ Lệ Phòng Ngừa Tĩnh Là Vấn Đề
Cách tiếp cận cổ điển: ước lượng β qua OLS trên cửa sổ lịch sử và cố định nó. Vấn đề: β thay đổi theo thời gian. Thị trường crypto đặc biệt không dừng — sự thay đổi xu hướng (DeFi summer → NFT hype → AI tokens) làm thay đổi các mối quan hệ cơ bản giữa các tài sản.
Sử dụng OLS lăn (hồi quy lăn) là một biện pháp nửa vời. Bạn phải chọn độ dài cửa sổ: quá ngắn — nhiễu; quá dài — độ trễ. Bộ lọc Kalman giải quyết vấn đề này một cách thanh lịch.

Mô Hình Không Gian Trạng Thái
Chúng ta biểu diễn mối quan hệ giữa Y(t) và X(t) như một mô hình tuyến tính với các hệ số thay đổi theo thời gian:
Phương trình quan sát:
Y(t) = α(t) + β(t) · X(t) + ε(t), ε(t) ~ N(0, R)
Phương trình trạng thái:
[α(t+1), β(t+1)]ᵀ = [α(t), β(t)]ᵀ + w(t), w(t) ~ N(0, Q)
Các tham số α(t) và β(t) được coi như một trạng thái ẩn dịch chuyển chậm (bước đi ngẫu nhiên). Bộ lọc Kalman ước lượng tối ưu trạng thái ẩn này từ các quan sát nhiễu.
- R (nhiễu quan sát) — phương sai của nhiễu quan sát. R càng lớn, bộ lọc phản ứng với dữ liệu mới càng chậm.
- Q (nhiễu trạng thái) — ma trận hiệp phương sai của nhiễu trạng thái. Q càng lớn, bộ lọc thích ứng càng nhanh.
Tỷ lệ Q/R xác định "độ mịn" của bộ lọc — tương tự như chọn độ dài cửa sổ trong OLS lăn, nhưng không cắt bỏ dữ liệu cứng nhắc.
Ưu Điểm So Với OLS Lăn
Các spread được tính bằng bộ lọc Kalman dừng hơn và hồi phục trung bình hơn so với spread từ hồi quy lăn. Bộ lọc Kalman sử dụng tất cả các quan sát quá khứ với trọng số giảm theo cấp số nhân, thay vì cắt bỏ dữ liệu ở độ dài cửa sổ cố định. Ngoài ra, bộ lọc Kalman không cần điều chỉnh tham số "độ dài cửa sổ" — thay vào đó, nó tự động hiệu chỉnh sự cân bằng giữa quán tính và khả năng thích ứng thông qua các ma trận Q và R.
Triển Khai Với filterpy
import numpy as np
from filterpy.kalman import KalmanFilter
def create_kalman_filter(
delta: float = 1e-4,
obs_noise: float = 1.0
) -> KalmanFilter:
"""
Creates a Kalman filter for adaptive hedge ratio estimation.
delta: state noise variance (Q = delta * I).
Larger delta → faster adaptation, more noise.
obs_noise: observation noise variance (R).
"""
kf = KalmanFilter(dim_x=2, dim_z=1)
kf.x = np.zeros((2, 1))
kf.F = np.eye(2)
kf.P = np.eye(2) * 1000
kf.Q = np.eye(2) * delta
kf.R = np.array([[obs_noise]])
return kf
def estimate_hedge_ratio(
prices_y: np.ndarray,
prices_x: np.ndarray,
delta: float = 1e-4,
obs_noise: float = 1.0
) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
"""
Estimates the adaptive hedge ratio using a Kalman filter.
Returns:
alphas: array of intercepts (α)
betas: array of hedge ratios (β)
spreads: array of spreads Y - α - β*X
"""
n = len(prices_y)
kf = create_kalman_filter(delta, obs_noise)
alphas = np.zeros(n)
betas = np.zeros(n)
spreads = np.zeros(n)
for t in range(n):
kf.H = np.array([[1.0, prices_x[t]]])
kf.predict()
kf.update(np.array([[prices_y[t]]]))
alphas[t] = kf.x[0, 0]
betas[t] = kf.x[1, 0]
spreads[t] = prices_y[t] - kf.x[0, 0] - kf.x[1, 0] * prices_x[t]
return alphas, betas, spreads
Tham số delta là chìa khóa. Đối với các cặp crypto có biến động cao (memecoin, altcoin vốn hóa nhỏ), hãy sử dụng delta = 1e-3. Đối với các cặp ổn định (BTC/ETH, SOL/AVAX) — delta = 1e-5.
6. Tín Hiệu Vào Lệnh và Thoát Lệnh
Ngưỡng Z-Score
Logic tín hiệu cơ bản:
def generate_signals(
spreads: np.ndarray,
lookback: int = 60,
entry_z: float = 2.0,
exit_z: float = 0.5,
stop_z: float = 4.0
) -> np.ndarray:
"""
Generates trading signals based on spread z-score.
Returns array: +1 (long spread), -1 (short spread), 0 (flat)
"""
signals = np.zeros(len(spreads))
position = 0
for t in range(lookback, len(spreads)):
window = spreads[t - lookback:t]
mu = np.mean(window)
sigma = np.std(window)
if sigma < 1e-10:
continue
z = (spreads[t] - mu) / sigma
if position == 0:
if z > entry_z:
position = -1 # Short spread (short Y, long X)
elif z < -entry_z:
position = 1 # Long spread (long Y, short X)
else:
if position == 1 and z > -exit_z:
position = 0
elif position == -1 and z < exit_z:
position = 0
elif abs(z) > stop_z:
position = 0
signals[t] = position
return signals
Bộ Lọc Động Lượng
Tín hiệu hồi phục trung bình thuần túy có thể được cải thiện với các bộ lọc:
-
Bộ lọc động lượng: đừng mở vị thế nếu spread tiếp tục phân kỳ. Chờ spread đảo chiều trước khi vào lệnh. Về mặt kỹ thuật: z-score đã vượt qua ngưỡng, nhưng sự thay đổi spread hiện tại đã hướng về mức trung bình.
-
Bộ lọc biến động: tăng ngưỡng vào lệnh trong giai đoạn biến động cao. Khi thị trường hoảng loạn, z-score có thể duy trì trên 3σ trong nhiều tuần.
-
Bộ lọc đồng tích hợp: trước mỗi giao dịch, xác minh rằng đồng tích hợp vẫn còn (kiểm định ADF lăn). Nếu p-value > 0.1 — tạm dừng giao dịch.
Thoát Lệnh Theo Thời Gian
Nếu một vị thế đã mở lâu hơn 2× chu kỳ bán hủy và spread chưa hồi phục — đóng nó cưỡng bức. Nếu spread không hồi phục trong thời gian 2× dự kiến, đồng tích hợp có khả năng đã phá vỡ và không còn gì để chờ đợi.
7. Backtesting: Làm Đúng Cách
Phân Tích Walk-Forward
Backtest tiêu chuẩn (train trên tất cả dữ liệu → test trên tất cả dữ liệu) là vô ích cho stat arb. Các tham số hồi quy được khớp quá mức với dữ liệu và kết quả sẽ quá lạc quan.
Cách tiếp cận walk-forward:
- Chia dữ liệu thành các giai đoạn: [train₁ → test₁] → [train₂ → test₂] → ...
- Trên mỗi giai đoạn train: ước lượng đồng tích hợp, tính tỷ lệ phòng ngừa, chọn ngưỡng z-score
- Trên giai đoạn test: giao dịch với tham số cố định
- Kết hợp tất cả giai đoạn test để đánh giá cuối cùng
Cấu hình điển hình cho crypto: train = 180 ngày, test = 30 ngày, bước = 30 ngày.

Mô Hình Chi Phí Giao Dịch
Đối với crypto, bạn cần tính đến:
| Thành Phần | Giá Trị Điển Hình | Ghi Chú |
|---|---|---|
| Phí maker | 0.02% | Lệnh giới hạn |
| Phí taker | 0.05-0.075% | Lệnh thị trường |
| Trượt giá | 0.01-0.1% | Phụ thuộc vào thanh khoản |
| Lãi suất tài trợ | ±0.01%/8h | Cho vị thế futures |
| Spread (bid-ask) | 0.01-0.05% | Trên các sàn lớn |
Vào và thoát một vị thế theo cặp liên quan đến 4 giao dịch (2 chiều × vào + thoát). Tổng chi phí: ~0.3-0.5% mỗi vòng quay. Điều này có nghĩa là lợi nhuận trung bình mỗi giao dịch phải vượt quá 0.5% để có kỳ vọng dương.
Mô Hình Trượt Giá
Mô hình tuyến tính: trượt giá = k × (order_size / ADV), trong đó ADV là khối lượng hàng ngày trung bình. Đối với crypto, k ≈ 0.1 cho top-10 coin và k ≈ 0.3-0.5 cho altcoin.
Mô hình thực tế hơn là tác động căn bậc hai: trượt giá = k × sqrt(order_size / ADV). Nó phản ánh tốt hơn cấu trúc vi mô thị trường thực tế.
Các Chỉ Số
def calculate_metrics(returns: np.ndarray, rf: float = 0.04) -> dict:
"""
Calculates key strategy metrics.
rf: risk-free rate (annual)
"""
daily_rf = rf / 365
excess = returns - daily_rf
ann_return = np.mean(returns) * 365
ann_vol = np.std(returns) * np.sqrt(365)
sharpe = (ann_return - rf) / ann_vol if ann_vol > 0 else 0
cumulative = np.cumprod(1 + returns)
running_max = np.maximum.accumulate(cumulative)
drawdowns = (cumulative - running_max) / running_max
max_dd = np.min(drawdowns)
calmar = ann_return / abs(max_dd) if max_dd != 0 else 0
win_rate = np.mean(returns > 0) if len(returns) > 0 else 0
gains = returns[returns > 0].sum()
losses = abs(returns[returns < 0].sum())
profit_factor = gains / losses if losses > 0 else float('inf')
return {
'annual_return': f'{ann_return:.1%}',
'annual_volatility': f'{ann_vol:.1%}',
'sharpe_ratio': f'{sharpe:.2f}',
'max_drawdown': f'{max_dd:.1%}',
'calmar_ratio': f'{calmar:.2f}',
'win_rate': f'{win_rate:.1%}',
'profit_factor': f'{profit_factor:.2f}',
}
Các chuẩn mực cho crypto stat arb:
- Sharpe > 1.5 — chiến lược tốt
- Drawdown tối đa < 15% — rủi ro chấp nhận được
- Calmar > 2.0 — tỷ lệ lợi nhuận/drawdown xuất sắc
- Profit factor > 1.5 — lợi thế bền vững
8. Các Vấn Đề Thực Tế
Trượt Giá và Thanh Khoản
Trong backtest, bạn vào lệnh ngay lập tức ở giá mid. Trong thực tế — không phải vậy. Trên altcoin với khối lượng hàng ngày 50K có thể dịch chuyển giá 0.2-0.5%. Đối với chiến lược theo cặp, đó là trượt giá gấp đôi (hai chiều) và có thể ăn hết toàn bộ lợi nhuận.
Giải pháp: sử dụng lệnh giới hạn (maker, không phải taker), chia nhỏ lệnh (TWAP/VWAP), và giới hạn chặt chẽ kích thước vị thế so với ADV (tối đa 1-2% khối lượng hàng ngày).
Rủi Ro Lãi Suất Tài Trợ
Với giao dịch cơ sở, bạn nhận lãi suất tài trợ, nhưng nó có thể chuyển sang âm. Trong thị trường bear tháng 12 năm 2022, lãi suất tài trợ BTC là -0.02% mỗi 8 giờ — nếu bạn đang giữ vị thế "long spot + short perp", bạn đang trả 100K vị thế.
Bảo vệ: giám sát lãi suất tài trợ theo thời gian thực và đóng vị thế khi lãi suất đảo chiều. Cách tiếp cận nâng cao hơn là kinh doanh chênh lệch lãi suất tài trợ giữa các sàn (long trên sàn có lãi suất tài trợ thấp, short trên sàn có lãi suất tài trợ cao).
Phá Vỡ Tương Quan Trong Khủng Hoảng
Tháng 3 năm 2020, tháng 5 năm 2021, tháng 11 năm 2022, tháng 8 năm 2024 — trong mọi sự cố crypto, các tương quan phá vỡ. Chính xác hơn, các tương quan tăng cường (mọi thứ cùng giảm), nhưng đồng tích hợp phá vỡ — spread có thể bay lên 10σ và không bao giờ quay lại.
Đây là gót chân Achilles của giao dịch theo cặp. Chiến lược kiếm tiền nhỏ đều đặn, sau đó mất một khoản lớn trong một ngày. Hồ sơ "nhặt xu trước bánh xe tải" kinh điển.
Bảo vệ:
- Stop-loss nghiêm ngặt: đóng vị thế khi z-score > 4σ
- Giới hạn đòn bẩy: tối đa 2-3x mỗi chiều
- Bộ lọc VIX/biến động: giảm kích thước vị thế khi biến động ngụ ý cao
- Đa dạng hóa: giao dịch 10-20 cặp đồng thời, không đặt tất cả vào một cặp
Yêu Cầu Vốn
Cho crypto stat arb nghiêm túc:
- Giao dịch cơ sở: từ $50K (trên một cặp, một sàn)
- Kinh doanh chênh lệch xuyên sàn: từ $100K (số dư trên hai sàn)
- Danh mục giao dịch theo cặp (10 cặp): từ $200K
- Cấp độ tổ chức: từ $1M
Với số tiền nhỏ hơn, hoa hồng và kích thước vị thế tối thiểu làm cho chiến lược không khả thi.
9. Triển Khai Python End-to-End
Lấy Dữ Liệu
import ccxt
import pandas as pd
import numpy as np
from datetime import datetime, timedelta
def fetch_ohlcv(
exchange_id: str,
symbol: str,
timeframe: str = '1h',
days: int = 365
) -> pd.DataFrame:
"""Fetch OHLCV data via ccxt."""
exchange = getattr(ccxt, exchange_id)({
'enableRateLimit': True,
})
since = int((datetime.now() - timedelta(days=days)).timestamp() * 1000)
all_candles = []
while True:
candles = exchange.fetch_ohlcv(
symbol, timeframe, since=since, limit=1000
)
if not candles:
break
all_candles.extend(candles)
since = candles[-1][0] + 1
if len(candles) < 1000:
break
df = pd.DataFrame(
all_candles,
columns=['timestamp', 'open', 'high', 'low', 'close', 'volume']
)
df['timestamp'] = pd.to_datetime(df['timestamp'], unit='ms')
df.set_index('timestamp', inplace=True)
return df
sol = fetch_ohlcv('binance', 'SOL/USDT', '1h', 365)
avax = fetch_ohlcv('binance', 'AVAX/USDT', '1h', 365)
prices = pd.DataFrame({
'SOL': sol['close'],
'AVAX': avax['close']
}).dropna()
Kiểm Định Đồng Tích Hợp
from statsmodels.tsa.stattools import coint, adfuller
from statsmodels.regression.linear_model import OLS
from statsmodels.tools import add_constant
def test_cointegration(y: np.ndarray, x: np.ndarray) -> dict:
"""
Full cointegration test with diagnostics.
"""
score, pvalue, crit_values = coint(y, x)
x_const = add_constant(x)
model = OLS(y, x_const).fit()
alpha, beta = model.params
spread = y - alpha - beta * x
adf_stat, adf_pvalue, _, _, adf_crit, _ = adfuller(spread, maxlag=20)
spread_lag = spread[:-1]
spread_diff = np.diff(spread)
spread_lag_const = add_constant(spread_lag)
hl_model = OLS(spread_diff, spread_lag_const).fit()
theta = -hl_model.params[1]
half_life = np.log(2) / theta if theta > 0 else np.inf
return {
'coint_pvalue': pvalue,
'cointegrated': pvalue < 0.05,
'hedge_ratio': beta,
'intercept': alpha,
'adf_statistic': adf_stat,
'adf_pvalue': adf_pvalue,
'half_life_hours': half_life,
'half_life_days': half_life / 24,
'spread_mean': np.mean(spread),
'spread_std': np.std(spread),
}
result = test_cointegration(
prices['SOL'].values,
prices['AVAX'].values
)
print(f"Cointegration: {result['cointegrated']} "
f"(p-value: {result['coint_pvalue']:.4f})")
print(f"Hedge ratio: {result['hedge_ratio']:.4f}")
print(f"Half-life: {result['half_life_days']:.1f} days")
Bộ Lọc Kalman + Backtester
from filterpy.kalman import KalmanFilter
class PairsBacktester:
"""
Walk-forward backtester for pairs trading
with Kalman filter.
"""
def __init__(
self,
prices_y: np.ndarray,
prices_x: np.ndarray,
kalman_delta: float = 1e-4,
obs_noise: float = 1.0,
entry_z: float = 2.0,
exit_z: float = 0.5,
stop_z: float = 4.0,
lookback: int = 60,
fee_rate: float = 0.001, # 0.1% round trip per leg
slippage_rate: float = 0.0005, # 0.05% slippage per leg
):
self.prices_y = prices_y
self.prices_x = prices_x
self.n = len(prices_y)
self.kalman_delta = kalman_delta
self.obs_noise = obs_noise
self.entry_z = entry_z
self.exit_z = exit_z
self.stop_z = stop_z
self.lookback = lookback
self.fee_rate = fee_rate
self.slippage_rate = slippage_rate
def run(self) -> pd.DataFrame:
"""Run the backtest. Returns a DataFrame with results."""
kf = KalmanFilter(dim_x=2, dim_z=1)
kf.x = np.zeros((2, 1))
kf.F = np.eye(2)
kf.P = np.eye(2) * 1000
kf.Q = np.eye(2) * self.kalman_delta
kf.R = np.array([[self.obs_noise]])
alphas = np.zeros(self.n)
betas = np.zeros(self.n)
spreads = np.zeros(self.n)
for t in range(self.n):
kf.H = np.array([[1.0, self.prices_x[t]]])
kf.predict()
kf.update(np.array([[self.prices_y[t]]]))
alphas[t] = kf.x[0, 0]
betas[t] = kf.x[1, 0]
spreads[t] = (
self.prices_y[t] - kf.x[0, 0]
- kf.x[1, 0] * self.prices_x[t]
)
positions = np.zeros(self.n)
z_scores = np.zeros(self.n)
position = 0
for t in range(self.lookback, self.n):
window = spreads[t - self.lookback:t]
mu = np.mean(window)
sigma = np.std(window)
if sigma < 1e-10:
continue
z = (spreads[t] - mu) / sigma
z_scores[t] = z
if position == 0:
if z > self.entry_z:
position = -1
elif z < -self.entry_z:
position = 1
else:
if position == 1 and z > -self.exit_z:
position = 0
elif position == -1 and z < self.exit_z:
position = 0
elif abs(z) > self.stop_z:
position = 0
positions[t] = position
spread_returns = np.diff(spreads) / np.abs(
spreads[:-1] + 1e-10
)
pnl = np.zeros(self.n)
for t in range(1, self.n):
if positions[t - 1] != 0:
raw_return = positions[t - 1] * spread_returns[t - 1]
pnl[t] = raw_return
if positions[t] != positions[t - 1]:
total_cost = 2 * (self.fee_rate + self.slippage_rate)
pnl[t] -= total_cost
return pd.DataFrame({
'price_y': self.prices_y,
'price_x': self.prices_x,
'alpha': alphas,
'beta': betas,
'spread': spreads,
'z_score': z_scores,
'position': positions,
'pnl': pnl,
'cumulative_pnl': np.cumsum(pnl),
})
bt = PairsBacktester(
prices_y=prices['SOL'].values,
prices_x=prices['AVAX'].values,
kalman_delta=1e-4,
entry_z=2.0,
exit_z=0.5,
stop_z=4.0,
lookback=60,
fee_rate=0.001,
slippage_rate=0.0005,
)
results = bt.run()
daily_pnl = results['pnl'].resample('D').sum() if hasattr(
results.index, 'freq'
) else results['pnl']
metrics = calculate_metrics(daily_pnl.values)
for k, v in metrics.items():
print(f'{k}: {v}')
Bộ Khung Giao Dịch Trực Tiếp
import ccxt
import asyncio
import logging
logger = logging.getLogger(__name__)
class LivePairsTrader:
"""
Minimal skeleton for live pairs trading.
For production: add retry logic, monitoring,
alerts, balance reconciliation.
"""
def __init__(
self,
exchange_id: str,
symbol_y: str,
symbol_x: str,
api_key: str,
secret: str,
position_size_usd: float = 1000.0,
entry_z: float = 2.0,
exit_z: float = 0.5,
):
self.exchange = getattr(ccxt, exchange_id)({
'apiKey': api_key,
'secret': secret,
'enableRateLimit': True,
})
self.symbol_y = symbol_y
self.symbol_x = symbol_x
self.position_size = position_size_usd
self.entry_z = entry_z
self.exit_z = exit_z
self.position = 0 # +1, -1, 0
self.kf = create_kalman_filter(delta=1e-4)
self.spread_history = []
async def update(self):
"""One update cycle."""
ticker_y = self.exchange.fetch_ticker(self.symbol_y)
ticker_x = self.exchange.fetch_ticker(self.symbol_x)
price_y = ticker_y['last']
price_x = ticker_x['last']
self.kf.H = np.array([[1.0, price_x]])
self.kf.predict()
self.kf.update(np.array([[price_y]]))
alpha = self.kf.x[0, 0]
beta = self.kf.x[1, 0]
spread = price_y - alpha - beta * price_x
self.spread_history.append(spread)
if len(self.spread_history) < 60:
logger.info(f"Warming up: {len(self.spread_history)}/60")
return
window = np.array(self.spread_history[-60:])
z = (spread - np.mean(window)) / np.std(window)
logger.info(
f"β={beta:.4f} spread={spread:.4f} z={z:.2f} "
f"pos={self.position}"
)
new_position = self.position
if self.position == 0:
if z > self.entry_z:
new_position = -1
elif z < -self.entry_z:
new_position = 1
else:
if self.position == 1 and z > -self.exit_z:
new_position = 0
elif self.position == -1 and z < self.exit_z:
new_position = 0
if new_position != self.position:
await self._execute_trade(
new_position, price_y, price_x, beta
)
self.position = new_position
async def _execute_trade(
self, target: int, price_y: float, price_x: float,
beta: float
):
"""Execute a pairs trade."""
if target == 0:
logger.info("Closing position")
elif target == 1:
size_y = self.position_size / price_y
size_x = (self.position_size * beta) / price_x
logger.info(
f"Long spread: buy {size_y:.4f} {self.symbol_y}, "
f"sell {size_x:.4f} {self.symbol_x}"
)
elif target == -1:
size_y = self.position_size / price_y
size_x = (self.position_size * beta) / price_x
logger.info(
f"Short spread: sell {size_y:.4f} {self.symbol_y}, "
f"buy {size_x:.4f} {self.symbol_x}"
)
async def run_loop(self, interval_seconds: int = 60):
"""Main loop."""
logger.info(
f"Starting live trading: "
f"{self.symbol_y}/{self.symbol_x}"
)
while True:
try:
await self.update()
except Exception as e:
logger.error(f"Error in update: {e}")
await asyncio.sleep(interval_seconds)
Thay Vì Kết Luận
Kinh doanh chênh lệch thống kê không phải là Chén Thánh. Đó là một nghề thủ công. Giữa "tôi biết đồng tích hợp là gì" và "tôi có một chiến lược hoạt động ổn định" là một hố sâu của các chi tiết kỹ thuật: xử lý dữ liệu đúng cách, backtesting walk-forward chính xác, mô hình trượt giá thực tế, giám sát thời gian thực.
Các thị trường tiền mã hóa vẫn cung cấp nhiều cơ hội hơn cho stat arb so với các thị trường truyền thống — thanh khoản phân mảnh, cơ sở hạ tầng thị trường còn non trẻ, và các công cụ độc đáo như hợp đồng tương lai vĩnh cửu với lãi suất tài trợ tạo ra những kém hiệu quả đã bị kinh doanh chênh lệch về không trên NYSE từ lâu.
Nhưng cửa sổ đang đóng lại. Các nhà đầu tư tổ chức đang gia nhập thị trường crypto, vốn kinh doanh chênh lệch đang tăng (theo ước tính, khối lượng vốn kinh doanh chênh lệch trên các sàn crypto tăng 215% vào năm 2025), và biên lợi nhuận đang bị nén lại. Nếu bạn định làm stat arb trong crypto — tốt nhất là bắt đầu ngay bây giờ.
Tất cả code trong bài viết này có sẵn như một điểm khởi đầu. Đừng chạy nó trong môi trường sản xuất mà không kiểm tra nghiêm túc. Và hãy nhớ: chiến lược duy nhất được đảm bảo hoạt động là quản lý rủi ro.
Các công trình học thuật chính:
- Engle, R.F. & Granger, C.W.J. (1987). "Co-Integration and Error Correction: Representation, Estimation, and Testing". Econometrica, 55(2), 251-276.
- Gatev, E., Goetzmann, W.N. & Rouwenhorst, K.G. (2006). "Pairs Trading: Performance of a Relative-Value Arbitrage Rule". The Review of Financial Studies, 19(3), 797-827.
- Vidyamurthy, G. (2004). Pairs Trading: Quantitative Methods and Analysis. Wiley.
- Avellaneda, M. & Lee, J.H. (2010). "Statistical Arbitrage in the US Equities Market". Quantitative Finance, 10(7), 761-782.
- Frontiers (2026). "Deep learning-based pairs trading: real-time forecasting of co-integrated cryptocurrency pairs". Frontiers in Applied Mathematics and Statistics.
Các thư viện hữu ích:
- statsmodels — đồng tích hợp, ADF, OLS
- filterpy — bộ lọc Kalman
- ccxt — API thống nhất cho 100+ sàn giao dịch
- arbitragelab — thư viện chuyên dụng cho giao dịch theo cặp (OU, Kalman, copulas)
Tác Giả
Trading-systems engineer
Trading-systems engineer building bots since 2017: cross-exchange arbitrage (connected up to 30 venues), cointegration-based pairs arbitrage across spot and futures, scalping, news and sentiment-driven strategies, trend algorithms, and portfolio management and balancing algorithms. Also builds sub-millisecond order execution, big-data warehouses, backtesting engines, AI agents, and trading interfaces (incl. open-source profitmaker.cc). Stack: JS/TS, Python, Rust/Zig/Go, DevOps, backend, frontend, architecture.