Chúng tôi khởi động lại một chiến lược paper trading khi giao dịch đang diễn ra và nhận được chuỗi lệnh khác với chuỗi lệnh trước khi khởi động lại. Cùng dữ liệu thị trường, cùng phiên bản chiến lược, cùng số dư tài khoản. Phép tính tín hiệu vẫn khớp. Nhưng chiến lược không còn nhớ đúng vị thế và các lệnh đang chờ của mình.
Chúng tôi lần theo sai lệch trong một ngày chạy lại dữ liệu. Bài học rút ra rất đơn giản: với phần mềm của bạn, khởi động lại cũng là một sự kiện thị trường. Nếu không kiểm thử cách chiến lược dựng lại trạng thái, một lần kiểm thử ngược suôn sẻ có thể che giấu việc hệ thống paper trading quên mất những gì nó đang nắm giữ.
09:10 — Chúng tôi chọn một vị thế đơn giản
Chiến lược thử nghiệm giao dịch một hợp đồng vĩnh cửu có thanh khoản cao: vào vị thế mua khi đường trung bình động ngắn hạn cắt lên trên đường dài hạn, rồi thoát vị thế khi hai đường cắt theo chiều ngược lại. Chúng tôi bắt đầu chạy lại với một vị thế nhỏ đã mở và một lệnh giới hạn reduce-only đang chờ để giảm bớt vị thế. Như vậy, quá trình khôi phục phải dựng lại 2 thông tin: chúng tôi đang nắm giữ gì và đã yêu cầu sàn thực hiện những gì.
Tại thời điểm lưu trạng thái, tài khoản có 0.04 hợp đồng. Lệnh đang mở có khối lượng 0.01. Tiến trình chiến lược đã lưu cả hai giá trị trong bộ nhớ, nhưng quy trình khởi động chỉ truy vấn vị thế. Nó cho rằng lệnh đã lưu không còn tồn tại.
09:25 — Lệnh trùng đầu tiên xuất hiện
Sau khi khởi động lại, chiến lược thấy vị thế mua hiện có, chạy logic tín hiệu rồi gửi thêm một lệnh giảm 0.01. Lúc này, sàn paper trading có 2 lệnh đang hoạt động. Từng lệnh riêng lẻ đều hợp lý. Nhưng nếu cả hai cùng khớp, chúng có thể bán ra gấp đôi khối lượng dự định.
Ban đầu, chúng tôi nghi ngờ vòng lặp tín hiệu. Nhưng lỗi không nằm ở vòng lặp, mà ở ảnh chụp trạng thái chưa đầy đủ. Chiến lược hỏi: “Tôi đang có vị thế nào?” nhưng không hỏi: “Những lệnh nào vẫn còn hiệu lực?”
| Trạng thái sau khi khởi động lại | Tiến trình cho rằng | Tài khoản thực tế có |
|---|---|---|
| Vị thế | Mua 0.04 | Mua 0.04 |
| Lệnh giảm vị thế đang mở | Không có | 2 lệnh, mỗi lệnh 0.01 |
| Mức phơi nhiễm dự kiến sau khi 1 lệnh khớp | Mua 0.03 | Có thể giảm còn mua 0.02 |
10:00 — Chúng tôi sửa quy trình khôi phục, rồi phát hiện vấn đề về thời điểm
Chúng tôi thay đổi quy trình khởi động để dựng lại trạng thái từ các vị thế và lệnh đang mở của tài khoản trước khi cho phép đưa ra quyết định mới. Lỗi lệnh trùng đã được xử lý. Sau đó, chúng tôi tạo một lần mất kết nối phức tạp hơn: một lệnh khớp khi chiến lược đang ngoại tuyến, còn thông báo khớp lệnh chỉ đến sau khi kết nối lại.
Ảnh chụp tài khoản đã phản ánh lệnh khớp. Sau đó, thông báo đến trễ lại làm giảm vị thế cục bộ thêm lần nữa. Trong vài giây, chiến lược cho rằng mình nắm giữ 0.02 hợp đồng, trong khi tài khoản có 0.03. Lần tái cân bằng tiếp theo dựa trên một khoản thiếu hụt không có thật.
Chúng tôi bổ sung các quy tắc đối soát: lấy ảnh chụp tài khoản làm trạng thái ban đầu, dùng mã định danh sự kiện để bỏ qua những lệnh khớp đã được phản ánh trong ảnh chụp, và không gửi lệnh cho đến khi hoàn tất đồng bộ ban đầu. Thông báo có thể đến trễ hoặc bị gửi 2 lần. Quy trình khôi phục phải xử lý được cả hai trường hợp.
13:40 — Lần chạy lại phát hiện một sai lệch khó nhận ra
Chúng tôi chạy lại cùng một diễn biến giá cho lần chạy ban đầu và lần chạy sau khi khởi động lại. Nếu chỉ so sánh P&L cuối cùng, chúng tôi đã bỏ sót vấn đề: hai phiên bản kết thúc với cùng vị thế sau khi thị trường đảo chiều. So sánh các sự kiện lệnh mới làm lộ ra sai lệch.
Chúng tôi ghi lại từng quyết định cùng trạng thái mà nó sử dụng: vị thế, lệnh đang mở, mã định danh của lệnh khớp được xử lý gần nhất, giá trị tín hiệu và phiên bản chiến lược. Nhờ vậy, sự kiện đầu tiên bị lệch có lời giải thích rõ ràng. Một lần chạy thấy có lệnh đang chờ; lần kia thấy danh sách trống. Sau đó, một lần chạy áp dụng cùng một lệnh khớp 2 lần.
Số dư cuối kỳ giống nhau không chứng minh hành vi giống nhau. Hãy so sánh chuỗi quyết định và lệnh, nhất là quanh các thời điểm khôi phục.
16:20 — Lần tới chúng tôi sẽ làm khác đi
Chúng tôi đã mất quá nhiều thời gian chạy lại dữ liệu giá trước khi kiểm tra các chuyển đổi trạng thái của tài khoản. Lần tới, chúng tôi sẽ đưa các trường hợp lỗi vào trước và giữ diễn biến thị trường gần như đi ngang. Nhờ vậy, lỗi phần mềm sẽ dễ nhận ra, không bị một biến động mạnh làm nhiễu quá trình chẩn đoán.
- Khởi động lại khi có vị thế và một lệnh đã khớp một phần.
- Ngắt kết nối sau khi gửi lệnh, rồi kết nối lại trước khi thông báo khớp lệnh đến.
- Gửi cùng một sự kiện khớp lệnh 2 lần và xác nhận trạng thái chỉ thay đổi 1 lần.
- Chặn lệnh mới cho đến khi vị thế và lệnh đang mở đều đã được đối soát.
- So sánh nhật ký quyết định và lệnh giữa lần chạy liên tục với lần chạy sau khi khởi động lại.
Chúng tôi cũng rút ra rằng nên lưu ảnh chụp trạng thái khôi phục cùng phiên bản chiến lược và nhật ký sự kiện. Nhờ vậy, có thể tái hiện lỗi trong vài phút, thay vì phải trông chờ ai đó nhớ chính xác trình tự kết nối lại.
Một chiến lược paper trading chỉ hoạt động đúng khi tiến trình còn sống thì chưa vượt qua buổi diễn tập đầy đủ. Hãy khởi động lại giữa lúc đang có vị thế, để các lệnh khớp đến trễ, rồi kiểm tra mọi lệnh nó gửi sau đó. Mục tiêu không phải chứng minh rằng chiến lược không bao giờ lỗi. Mục tiêu là làm cho quá trình khôi phục có thể quan sát được, trước khi tài khoản paper trading bất ngờ dạy bạn bài học tương tự.
← Tất cả bài viết


