27 tháng Tám, 2026 · kỹ thuật dữ liệu

Vũ trụ mã giao dịch của bạn là cỗ máy thời gian: thiên lệch sống sót trong backtest hợp đồng vĩnh cửu crypto

Vũ trụ mã giao dịch của bạn là cỗ máy thời gian: thiên lệch sống sót trong backtest hợp đồng vĩnh cửu crypto

Tối Chủ nhật bạn gửi tôi notebook, và từ đó tôi vẫn mở nó trên màn hình phụ. Động lượng chéo mặt cắt, top 150 hợp đồng vĩnh cửu Binance USDⓈ-M theo khối lượng giao dịch tính bằng USD trong 30 ngày, tái cân bằng hàng tuần, mua decile trên cùng và bán khống decile dưới cùng, từ 2021-01 đến 2026-06. Sharpe 2.31, mức sụt giảm tối đa 14.2%, cùng mô hình chi phí mà tôi thấy khá sát thực tế: vào lệnh taker, thoát lệnh taker, cộng dồn funding theo mỗi khoảng thời gian, và một thành phần trượt giá tăng theo quy mô lệnh của bạn so với sổ lệnh. Bạn đã làm đúng những phần khó. Rồi bạn hỏi vì sao 6 tuần paper trading lại đi ngang, và liệu lợi thế có suy giảm không.

Nó không suy giảm. Nó chưa từng có trong backtest. Hãy xem ô 4:

info = requests.get(BASE + "/fapi/v1/exchangeInfo").json()
symbols = [s["symbol"] for s in info["symbols"]
           if s["status"] == "TRADING" and s["quoteAsset"] == "USDT"]

Bạn gọi endpoint đó vào tháng 6 năm 2026 rồi dùng kết quả để xác định những gì có thể giao dịch vào tháng 3 năm 2021. Theo cách xây dựng danh sách ấy, mọi mã đều còn tồn tại đến tháng 6 năm 2026. Đó chính là toàn bộ lỗi, và tác động của nó còn lớn hơn tổng tác động của phần còn lại trong mô hình chi phí của bạn.

2.31 → 0.74Sharpe, danh sách mã còn tồn tại so với dữ liệu tại thời điểm
~1/5vũ trụ giao dịch năm 2021 của bạn giờ không còn tồn tại
380 KBảnh chụp exchangeInfo hàng ngày, nén gzip

Endpoint không cho bạn biết điều gì

exchangeInfo không có dữ liệu lịch sử. Không có tham số asOf, không có kho lưu trữ, không có nhật ký thay đổi. Nó chỉ là ảnh chụp tại thời điểm hiện tại, và Binance chưa bao giờ hứa cung cấp gì hơn thế. Khi một hợp đồng bị hủy niêm yết, mục của nó bị xóa khỏi phản hồi và theo API, mã giao dịch đó không còn tồn tại. Kể từ năm 2019, Binance đã niêm yết hơn 600 hợp đồng vĩnh cửu USDⓈ-M và cho ngừng giao dịch hơn 100 hợp đồng. BTS, COCOS, TOMO, RAY, FTT, SC, cùng hàng loạt hợp đồng altcoin mùa tăng giá năm 2021 chỉ có một quý huy hoàng rồi thanh khoản cạn dần cho đến khi sàn gỡ chúng xuống.

Hãy tự hỏi bộ lọc động lượng 30 ngày thích những mã nào. Không phải BTC. Nó thích những thứ vừa tăng gấp 3 nhờ cú bơm giá khi niêm yết và một vòng xoáy Twitter. Nhóm đó chồng lấn rất nhiều với nhóm cuối cùng bị hủy niêm yết, còn bộ lọc vũ trụ giao dịch của bạn đã loại bỏ phần giao nhau ấy.

Tôi chạy lại chiến lược của bạn trên kho lưu trữ ảnh chụp của chúng tôi, dùng vũ trụ giao dịch tại thời điểm đó, cùng tín hiệu và cùng chi phí, và Sharpe ra 0.74 với mức sụt giảm 31%. Khoảng 2/5 chênh lệch đến từ những mã bị hủy niêm yết mà bạn không thể nắm giữ. Thêm 1/4 đến từ vấn đề ngược lại, tinh vi hơn, và tôi đoán bạn sẽ còn ít thích nó hơn.

Đầu kia của cỗ máy thời gian: những mã chưa tồn tại

Đặc trưng của bạn là lợi suất 90 ngày. Cửa sổ cuốn của bạn có min_periods=20, vì bạn đặt nó một lần để phần khởi động không làm hỏng quý đầu tiên của mẫu dữ liệu, rồi không xem lại nữa. Vì vậy, một hợp đồng mới niêm yết 21 ngày sẽ được tính điểm động lượng từ 3 tuần biến động giá sau niêm yết; nó thường lọt vào decile trên cùng khi đợt niêm yết diễn ra thuận lợi, rồi được đưa vào danh mục của bạn.

Có thể nói phần đó vẫn hợp lý. Nhưng điều sau thì không: nhà cung cấp dữ liệu đã bổ sung ngược một số mã bằng dữ liệu giao ngay hoặc chỉ số từ trước khi hợp đồng vĩnh cửu tồn tại, nên một số hợp đồng có lịch sử kline trước cả onboardDate của chính chúng. Bạn có thể kiểm tra việc này trong khoảng 1 phút. Ghép bảng giá với ngày niêm yết rồi đếm số dòng nằm trước ngày đó. Trong dữ liệu của bạn có 41 mã có nến trước khi niêm yết, và một mã trong số đó, vào mùa hè năm 2023, đóng góp mức tăng 6% chỉ trong một tuần vào đường cong vốn nhờ vị thế trong một hợp đồng phải 9 ngày sau mới ra đời.

Chuỗi mã giao dịch không phải mã định danh. Đó là nhãn mà sàn cho thuê, đôi khi cho thuê 2 lần.

Điều này dẫn đến chuyện đổi tên. MATICUSDT đổi thành POLUSDT. FTMUSDT đổi thành SUSDT theo tỷ lệ chuyển đổi 1:1. Sự kiện LUNA hồi tháng 5 năm 2022 để lại LUNCUSDT, rồi sau đó là một LUNAUSDT hoàn toàn mới có chung gốc mã với một thứ đã mất gần như tất cả giá trị. Nếu bộ nạp dữ liệu của bạn dùng chuỗi mã làm khóa rồi nối mọi tệp tìm được, thì ít nhất một chuỗi dữ liệu trong đó có điểm gián đoạn không phải do biến động giá, và đặc trưng động lượng sẽ đọc điểm gián đoạn ấy thành tín hiệu mạnh nhất trong nhóm.

Mọi thứ khác cũng thay đổi theo thời gian

Một khi chấp nhận rằng danh sách mã giao dịch thay đổi theo thời gian, lập luận tương tự cũng áp dụng cho mọi trường khác trong phản hồi đó. Bạn đang dùng giá trị của hôm nay cho tất cả chúng.

TrườngThay đổi ra saoĐiều gì bị ảnh hưởng
statusTRADING → SETTLING → biến mấtThiên lệch sống sót; thoát lệnh ảo ở mức giá đóng cửa chưa từng được giao dịch
onboardDateHàng tuần có mã mới niêm yết; mã đã chết thì không còn thông tinGiao dịch hợp đồng trước khi chúng tồn tại
tickSize / stepSizeĐiều chỉnh theo biến động mức giáLàm tròn lệnh, và mức giá giới hạn lẽ ra đã bị từ chối
minNotionalTăng dần theo thời gian trên các sổ lệnh mỏngNhững vị thế nhỏ mà bộ định tuyến lệnh thực tế sẽ từ chối
fundingIntervalHours8h suốt nhiều năm, rồi chuyển thành 4h hoặc 1h với nhiều mãTính carry sai lệch 2–3×, đúng ở những altcoin mà bộ lọc của bạn nắm giữ
bậc đòn bẩyCác bậc và ký quỹ duy trì được điều chỉnh lạiMô hình hóa thanh lý và năng lực ký quỹ

Trong trường hợp của bạn, funding gây đau đầu nhất. Vòng lặp cộng dồn của bạn giả định có 3 lần thanh toán mỗi ngày trong toàn bộ lịch sử. Một phần đáng kể danh mục altcoin đã chuyển sang funding mỗi 4 giờ, và các vị thế bán khống ở những mã có funding cao đóng góp phần lớn PnL mô phỏng của bạn. Sai lệch ở đây không phải do làm tròn. Sai lệch theo cấp số nhân.

Bản thân việc hủy niêm yết là một sự kiện, nhưng bạn chưa mô hình hóa nó

Trong lần chạy lại với dữ liệu tại thời điểm đó, tôi đã cho chiến lược của bạn một phương án thoát lệnh khá ưu ái. Quy trình hủy niêm yết thực tế diễn ra theo kịch bản: thông báo, thường trước 7 đến 14 ngày, rồi đến khoảng thời gian chỉ được giảm vị thế, sau đó tất toán bắt buộc theo giá mark. Thông báo là thông tin công khai và bạn có thể hành động, nên mô phỏng trung thực sẽ thoát lệnh ở giá đóng cửa ngày thông báo. Nhưng với một đợt hủy niêm yết altcoin điển hình, giá đóng cửa đó đã thấp hơn 10-20% so với tuần trước, sổ lệnh lại mỏng, và thành phần trượt giá của bạn phải biết mình đang hoạt động trong một chế độ khác. Nếu bộ máy khớp lệnh trả cho bạn giá tất toán mà không tính tác động thị trường, thì bạn đã âm thầm biến tài sản sắp chết thành thứ có thể giao dịch ở giá trị hợp lý.

Nếu bạn chưa từng lưu trữ exchangeInfo thì vẫn chưa hết cách. Bản dump công khai tại data.binance.vision/data/futures/um/monthly/klines/ vẫn giữ các thư mục của những mã đã bị hủy niêm yết rất lâu sau khi API quên mất chúng. Liệt kê thư mục, rồi lấy tệp hàng tháng đầu tiên và cuối cùng của mỗi mã để có khoảng thời gian niêm yết và hủy niêm yết khả dụng mà không cần nhà cung cấp nào. Đây là dữ liệu tái dựng, không phải hồ sơ chính thức, và sẽ không khôi phục được tick size hay khoảng thời gian funding. Nhưng nó sẽ cho biết mã nào tồn tại vào lúc nào, đáp ứng 80% nhu cầu của bạn cho thứ Hai.

Những gì tôi muốn bạn xây dựng trước khi đụng lại vào tín hiệu

  1. Một cron hằng ngày lấy exchangeInfo từ mọi sàn bạn nghiên cứu rồi ghi vào object storage theo ngày. Tệp nén gzip chưa đến nửa megabyte. Lưu 10 năm chỉ tốn một khoản làm tròn trong S3, đổi lại là năng lực nghiên cứu mà sau này bạn không thể mua được.
  2. Một danh mục tài sản được tạo từ các ảnh chụp đó: mỗi hàng ứng với (sàn, mã, valid_from, valid_to) cùng toàn bộ trường dữ liệu. So sánh các ảnh chụp liên tiếp để tạo danh mục, và coi mọi thay đổi trường là một hàng mới.
  3. Một hàm tạo vũ trụ giao dịch bắt buộc nhận tham số thời gian. universe(ts), tuyệt đối không dùng universe(). Đặt mặc định sao cho không ai, kể cả bạn trong tương lai lúc 1am, có thể vô tình dùng danh sách mã còn tồn tại.
  4. Một kiểm tra khẳng định trước ngày niêm yết trong bộ nạp dữ liệu: không được có nến nào trước onboard_ts trừ đi 1 ngày. Dừng lần chạy, đừng chỉ đưa ra cảnh báo.
  5. Một mã định danh công cụ nội bộ ổn định, tồn tại qua các lần đổi tên, còn mã giao dịch chỉ là thuộc tính. Ánh xạ POL và MATIC vào cùng một mã định danh, đồng thời đánh dấu các lần đổi mệnh giá để logic kiểm tra tính liên tục có thể từ chối nối dữ liệu qua các lần đó.

Làm những việc đó rồi chạy lại. Tôi đoán kết quả sẽ gần mức 0.74 của tôi, và câu hỏi thú vị khi đó là liệu Sharpe 0.74 có tính cả những mã đã chết có còn gì đáng để paper trading không. Có thể là có. Động lượng chéo mặt cắt trên hợp đồng vĩnh cửu không phải vô nghĩa, và một phần lợi nhuận còn lại sau khi sửa vũ trụ giao dịch thực sự đến từ carry ở phía bán khống. Bạn cũng sẽ thấy kết quả paper trading bắt đầu khớp với backtest, vì paper trading vốn luôn chạy trên vũ trụ giao dịch tại thời điểm đó. Nó chưa bao giờ có lựa chọn khác.

P.S. Đây không phải chuyện riêng của crypto, chỉ là ở đây nó lộ rõ hơn. Các nhà nghiên cứu cổ phiếu đã xử lý lợi suất hủy niêm yết và mã giao dịch được tái sử dụng từ khi CRSP bắt đầu cung cấp dữ liệu, còn thị trường dự đoán thì đẩy vấn đề đến cực hạn: mọi hợp đồng đều hết hạn theo thiết kế, nên vũ trụ giao dịch chỉ gồm những lần niêm yết và kết thúc. Nếu sau này bạn chuyển bộ lọc này sang Kalshi, hãy xây dựng danh mục tài sản trước. Ở đó không có loại lịch sử nào khác.

thiên lệch sống sótdữ liệu tại thời điểmhợp đồng tương lai cryptobacktestkỹ thuật dữ liệu
← Tất cả bài viết