Một nhà phát triển chia sẻ cách xây dựng robot giao dịch Polymarket, bằng cách bắt các biến động giá trong thị trường BTC 15 phút, biến 1.000 USD thành 1.869 USD trong vài ngày, lợi nhuận đạt 86% qua backtest. Bài viết giới thiệu chi tiết logic xây dựng robot, phương pháp backtest và những giới hạn của nó.
(Phần tiền đề: Thị trường dự đoán hàng đầu Polymarket thông báo tự xây dựng L2, chiến thắng của Polygon có còn không?)
(Bổ sung nền: Làm thế nào để kiếm lợi nhuận 40% hàng năm qua arbitrage trên Polymarket?)
Mục lục bài viết
Logic xây dựng robot
Backtest
Giới hạn của backtest
Cơ sở hạ tầng
Vài tuần trước, tôi quyết định xây dựng robot Polymarket của riêng mình. Phiên bản đầy đủ mất vài tuần để hoàn thành.
Tôi sẵn lòng bỏ công sức này vì thực sự tồn tại các lỗ hổng hiệu quả trên Polymarket, dù đã có một số robot lợi dụng những điểm yếu này để kiếm lời, nhưng vẫn còn rất nhiều cơ hội chưa được khai thác, thị trường này còn nhiều hơn số lượng robot hiện có.
Logic xây dựng robot
Logic của robot dựa trên một chiến lược tôi từng thực hiện thủ công, để nâng cao hiệu quả, tôi đã tự động hóa nó. Robot hoạt động trên thị trường “BTC 15 phút UP/DOWN” (tăng/giảm trong 15 phút).
Robot chạy một chương trình giám sát thời gian thực, có khả năng tự động chuyển sang vòng quay BTC 15 phút hiện tại, truyền dữ liệu qua WebSocket để lấy giá mua/bán tốt nhất (best bid/ask), hiển thị trên một giao diện terminal cố định, và cho phép kiểm soát toàn diện qua lệnh văn bản.
Trong chế độ thủ công, bạn có thể đặt lệnh trực tiếp.
buy up / buy down : mua với số USD nhất định.
buyshares up / buyshares down : mua chính xác số lượng cổ phiếu, sử dụng lệnh LIMIT (giới hạn) + GTC (hủy trước khi hết hạn) thân thiện với giao diện người dùng, giao dịch theo giá bán tốt nhất (best ask).
Chế độ tự động chạy một vòng lặp hai bước (two-leg).
Bước đầu tiên, nó chỉ quan sát biến động giá trong vòng windowMin phút sau khi bắt đầu mỗi vòng. Nếu một trong hai phía giảm quá nhanh (trong khoảng 3 giây, giảm ít nhất movePct), nó sẽ kích hoạt “Phần 1 (Leg 1)”, mua vào phía giảm mạnh đó.
Sau khi hoàn thành Leg 1, robot sẽ không mua cùng một phía nữa. Nó sẽ chờ “Phần 2 (Leg 2, tức đối trọng)”, và chỉ kích hoạt khi thoả mãn điều kiện: leg1EntryPrice + oppositeAsk <= sumTarget.
Khi điều kiện này thoả mãn, nó sẽ mua vào phía đối diện. Sau khi hoàn thành Leg 2, vòng lặp kết thúc, robot trở lại trạng thái quan sát, chờ tín hiệu giảm giá tiếp theo với cùng tham số.
Nếu trong quá trình vòng lặp, vòng quay thay đổi, robot sẽ bỏ qua vòng đó và bắt đầu lại với cùng thiết lập trong vòng tiếp theo.
Tham số của chế độ tự động như sau: auto on [sum=0.95] [move=0.15] [windowMin=2]
windowMin: thời gian tối đa để thực hiện Phần 1 kể từ khi bắt đầu vòng.
Backtest
Logic của robot rất đơn giản: chờ đợt bán tháo mạnh, mua vào phía giảm giá, rồi chờ giá ổn định và thực hiện đối trọng bằng cách mua vào phía còn lại, đồng thời đảm bảo: priceUP + priceDOWN < 1.
Tuy nhiên, logic này cần được kiểm thử. Nó có thực sự hiệu quả trong dài hạn không? Quan trọng hơn, robot có nhiều tham số (số cổ phiếu, tổng số, phần trăm di chuyển, thời gian cửa sổ, v.v.) và tập hợp tham số nào tối ưu nhất để tối đa hoá lợi nhuận?
Ý tưởng đầu tiên của tôi là để robot chạy thực tế một tuần và quan sát kết quả. Vấn đề là điều này quá mất thời gian, và chỉ có thể kiểm thử một bộ tham số, trong khi tôi cần thử nhiều bộ.
Ý tưởng thứ hai là sử dụng dữ liệu lịch sử trực tuyến từ API CLOB của Polymarket để backtest. Không may, đối với thị trường BTC 15 phút UP/DOWN, endpoint dữ liệu lịch sử luôn trả về tập dữ liệu trống. Không có dữ liệu giá biến động (ticks), nên không thể kiểm tra xem có thể phát hiện “giảm giá trong khoảng 3 giây” hay không, không thể kích hoạt Leg 1, dù tham số thế nào đi nữa, cũng sẽ ra 0 vòng và 0% ROI.
Sau khi điều tra kỹ hơn, tôi phát hiện các người dùng khác cũng gặp vấn đề tương tự khi lấy dữ liệu lịch sử của một số thị trường. Tôi thử các thị trường khác có dữ liệu lịch sử thực sự và kết luận: đối với thị trường này, dữ liệu lịch sử không được lưu trữ.
Do giới hạn này, cách duy nhất để backtest chiến lược này một cách đáng tin cậy là khi robot chạy, ghi lại các snapshot giá bán tốt nhất (best-ask) theo thời gian thực để tạo ra bộ dữ liệu lịch sử của riêng tôi.
Các bộ ghi chép sẽ chụp ảnh nhanh (snapshot) ghi vào đĩa, gồm các nội dung:
Thời gian (timestamp)
ID vòng quay (round slug)
Thời gian còn lại (remaining seconds)
Token UP/DOWN
Giá bán tốt nhất (best ask)
Sau đó, “recorded backtest” sẽ phát lại các snapshot này, áp dụng logic tự động giống hệt, đảm bảo có dữ liệu tần suất cao để phát hiện giảm giá và điều chỉnh đối trọng.
Trong 4 ngày, tôi đã thu thập được 6 GB dữ liệu. Tôi có thể ghi nhiều hơn, nhưng nghĩ rằng như vậy đủ để thử các bộ tham số khác nhau.
Tôi bắt đầu thử các tham số này:
Số dư ban đầu: $1,000
Mỗi lần giao dịch 20 cổ phiếu
sumTarget = 0.95
Ngưỡng giảm giá = 15%
windowMin = 2 phút
Tôi cũng áp dụng phí cố định 0.5% và chênh lệch giá 2% để giữ trong kịch bản bảo thủ.
Kết quả backtest cho ra ROI là 86%, trong vài ngày, $1,000 biến thành $1,869.
Sau đó, tôi thử bộ tham số khá liều lĩnh hơn:
Số dư ban đầu: $1,000
Mỗi lần giao dịch 20 cổ phiếu
sumTarget = 0.6
Ngưỡng giảm giá = 1%
windowMin = 15 phút
Kết quả: sau 2 ngày, lợi nhuận đầu tư là -50%.
Điều này rõ ràng cho thấy việc chọn tham số là yếu tố quan trọng nhất. Nó có thể giúp bạn kiếm nhiều tiền hoặc gây ra thua lỗ lớn.
Giới hạn của backtest
Ngay cả khi đã tính phí và chênh lệch giá, backtest vẫn có những giới hạn của nó.
Thứ nhất, chỉ sử dụng dữ liệu vài ngày, có thể chưa đủ để có cái nhìn toàn diện về thị trường.
Nó dựa vào các snapshot giá bán tốt nhất đã ghi lại; trong thực tế, các lệnh có thể được khớp một phần hoặc khớp ở mức giá khác. Thêm vào đó, độ sâu sổ lệnh và khối lượng có thể khớp có thể không được mô phỏng.
Không bắt được các biến động nhỏ dưới giây (dữ liệu lấy mẫu mỗi giây). Mặc dù timestamp có độ chính xác 1 giây, nhưng trong mỗi giây có thể xảy ra nhiều chuyện.
Trong backtest, slippage được giả định cố định, không mô phỏng độ trễ biến đổi (ví dụ 200–1500 ms) hoặc peak mạng.
Mỗi phần giao dịch được xem như thực thi “ngay lập tức” (không có xếp hàng lệnh, không đặt lệnh chờ).
Phí được tính đồng nhất, trong thực tế, phí có thể phụ thuộc vào: thị trường / token, người đặt lệnh và người ăn lệnh, cấp độ phí hoặc điều kiện.
Để giữ tính thận trọng, tôi áp dụng quy tắc: nếu Leg 2 không thực hiện trước khi thị trường đóng, Leg 1 sẽ coi như toàn bộ mất (total loss).
Điều này rất bảo thủ, nhưng không phải lúc nào cũng phù hợp với thực tế:
Đôi khi Leg 1 có thể đóng sớm hơn,
Đôi khi cuối cùng nó trong vùng ITM và thắng,
Đôi khi thua lỗ có thể là một phần chứ không phải toàn bộ.
Dù thua lỗ có thể bị đánh giá quá cao, nhưng điều này cung cấp một kịch bản “tồi tệ nhất” hữu ích.
Quan trọng nhất, backtest không thể mô phỏng tác động của các lệnh lớn của bạn lên sổ lệnh hoặc hành vi thu hút các trader khác săn lùng bạn. Trong thực tế, lệnh của bạn có thể:
Gây nhiễu sổ lệnh,
Thu hút hoặc đẩy lùi các trader khác,
Dẫn đến slippage phi tuyến.
Backtest giả định bạn là một người chỉ lấy thanh khoản (price taker), không ảnh hưởng gì.
Cuối cùng, nó không mô phỏng giới hạn tần suất (rate limits), lỗi API, lệnh bị từ chối, tạm dừng, timeout, kết nối lại, hoặc robot bỏ lỡ tín hiệu do bận rộn.
Backtest rất hữu ích để xác định phạm vi tham số tốt, nhưng không đảm bảo 100%, vì một số hiệu ứng thực tế không thể mô phỏng.
Cơ sở hạ tầng
Tôi dự định chạy robot này trên Raspberry Pi để tránh tiêu tốn tài nguyên của máy chủ chính, và duy trì hoạt động 24/7 liên tục.
Tuy nhiên, vẫn còn nhiều cải tiến có thể thực hiện:
Chuyển sang Rust thay vì JavaScript để có hiệu năng và thời gian xử lý vượt trội.
Chạy node RPC Polygon riêng để giảm độ trễ.
Triển khai trên VPS gần máy chủ của Polymarket cũng sẽ giảm đáng kể độ trễ.
Chắc chắn còn nhiều tối ưu khác tôi chưa khám phá. Hiện tại, tôi đang học Rust vì nó đang trở thành ngôn ngữ không thể thiếu trong phát triển Web3.
Trang này có thể chứa nội dung của bên thứ ba, được cung cấp chỉ nhằm mục đích thông tin (không phải là tuyên bố/bảo đảm) và không được coi là sự chứng thực cho quan điểm của Gate hoặc là lời khuyên về tài chính hoặc chuyên môn. Xem Tuyên bố từ chối trách nhiệm để biết chi tiết.
Tôi đã tạo một bot để "kiếm tiền thụ động" trên Polymarket: Đây là logic xây dựng của tôi
Một nhà phát triển chia sẻ cách xây dựng robot giao dịch Polymarket, bằng cách bắt các biến động giá trong thị trường BTC 15 phút, biến 1.000 USD thành 1.869 USD trong vài ngày, lợi nhuận đạt 86% qua backtest. Bài viết giới thiệu chi tiết logic xây dựng robot, phương pháp backtest và những giới hạn của nó.
(Phần tiền đề: Thị trường dự đoán hàng đầu Polymarket thông báo tự xây dựng L2, chiến thắng của Polygon có còn không?)
(Bổ sung nền: Làm thế nào để kiếm lợi nhuận 40% hàng năm qua arbitrage trên Polymarket?)
Mục lục bài viết
Vài tuần trước, tôi quyết định xây dựng robot Polymarket của riêng mình. Phiên bản đầy đủ mất vài tuần để hoàn thành.
Tôi sẵn lòng bỏ công sức này vì thực sự tồn tại các lỗ hổng hiệu quả trên Polymarket, dù đã có một số robot lợi dụng những điểm yếu này để kiếm lời, nhưng vẫn còn rất nhiều cơ hội chưa được khai thác, thị trường này còn nhiều hơn số lượng robot hiện có.
Logic xây dựng robot
Logic của robot dựa trên một chiến lược tôi từng thực hiện thủ công, để nâng cao hiệu quả, tôi đã tự động hóa nó. Robot hoạt động trên thị trường “BTC 15 phút UP/DOWN” (tăng/giảm trong 15 phút).
Robot chạy một chương trình giám sát thời gian thực, có khả năng tự động chuyển sang vòng quay BTC 15 phút hiện tại, truyền dữ liệu qua WebSocket để lấy giá mua/bán tốt nhất (best bid/ask), hiển thị trên một giao diện terminal cố định, và cho phép kiểm soát toàn diện qua lệnh văn bản.
Trong chế độ thủ công, bạn có thể đặt lệnh trực tiếp.
buy up / buy down : mua với số USD nhất định.
buyshares up / buyshares down : mua chính xác số lượng cổ phiếu, sử dụng lệnh LIMIT (giới hạn) + GTC (hủy trước khi hết hạn) thân thiện với giao diện người dùng, giao dịch theo giá bán tốt nhất (best ask).
Chế độ tự động chạy một vòng lặp hai bước (two-leg).
Bước đầu tiên, nó chỉ quan sát biến động giá trong vòng windowMin phút sau khi bắt đầu mỗi vòng. Nếu một trong hai phía giảm quá nhanh (trong khoảng 3 giây, giảm ít nhất movePct), nó sẽ kích hoạt “Phần 1 (Leg 1)”, mua vào phía giảm mạnh đó.
Sau khi hoàn thành Leg 1, robot sẽ không mua cùng một phía nữa. Nó sẽ chờ “Phần 2 (Leg 2, tức đối trọng)”, và chỉ kích hoạt khi thoả mãn điều kiện: leg1EntryPrice + oppositeAsk <= sumTarget.
Khi điều kiện này thoả mãn, nó sẽ mua vào phía đối diện. Sau khi hoàn thành Leg 2, vòng lặp kết thúc, robot trở lại trạng thái quan sát, chờ tín hiệu giảm giá tiếp theo với cùng tham số.
Nếu trong quá trình vòng lặp, vòng quay thay đổi, robot sẽ bỏ qua vòng đó và bắt đầu lại với cùng thiết lập trong vòng tiếp theo.
Tham số của chế độ tự động như sau: auto on [sum=0.95] [move=0.15] [windowMin=2]
Backtest
Logic của robot rất đơn giản: chờ đợt bán tháo mạnh, mua vào phía giảm giá, rồi chờ giá ổn định và thực hiện đối trọng bằng cách mua vào phía còn lại, đồng thời đảm bảo: priceUP + priceDOWN < 1.
Tuy nhiên, logic này cần được kiểm thử. Nó có thực sự hiệu quả trong dài hạn không? Quan trọng hơn, robot có nhiều tham số (số cổ phiếu, tổng số, phần trăm di chuyển, thời gian cửa sổ, v.v.) và tập hợp tham số nào tối ưu nhất để tối đa hoá lợi nhuận?
Ý tưởng đầu tiên của tôi là để robot chạy thực tế một tuần và quan sát kết quả. Vấn đề là điều này quá mất thời gian, và chỉ có thể kiểm thử một bộ tham số, trong khi tôi cần thử nhiều bộ.
Ý tưởng thứ hai là sử dụng dữ liệu lịch sử trực tuyến từ API CLOB của Polymarket để backtest. Không may, đối với thị trường BTC 15 phút UP/DOWN, endpoint dữ liệu lịch sử luôn trả về tập dữ liệu trống. Không có dữ liệu giá biến động (ticks), nên không thể kiểm tra xem có thể phát hiện “giảm giá trong khoảng 3 giây” hay không, không thể kích hoạt Leg 1, dù tham số thế nào đi nữa, cũng sẽ ra 0 vòng và 0% ROI.
Sau khi điều tra kỹ hơn, tôi phát hiện các người dùng khác cũng gặp vấn đề tương tự khi lấy dữ liệu lịch sử của một số thị trường. Tôi thử các thị trường khác có dữ liệu lịch sử thực sự và kết luận: đối với thị trường này, dữ liệu lịch sử không được lưu trữ.
Do giới hạn này, cách duy nhất để backtest chiến lược này một cách đáng tin cậy là khi robot chạy, ghi lại các snapshot giá bán tốt nhất (best-ask) theo thời gian thực để tạo ra bộ dữ liệu lịch sử của riêng tôi.
Các bộ ghi chép sẽ chụp ảnh nhanh (snapshot) ghi vào đĩa, gồm các nội dung:
Sau đó, “recorded backtest” sẽ phát lại các snapshot này, áp dụng logic tự động giống hệt, đảm bảo có dữ liệu tần suất cao để phát hiện giảm giá và điều chỉnh đối trọng.
Trong 4 ngày, tôi đã thu thập được 6 GB dữ liệu. Tôi có thể ghi nhiều hơn, nhưng nghĩ rằng như vậy đủ để thử các bộ tham số khác nhau.
Tôi bắt đầu thử các tham số này:
Tôi cũng áp dụng phí cố định 0.5% và chênh lệch giá 2% để giữ trong kịch bản bảo thủ.
Kết quả backtest cho ra ROI là 86%, trong vài ngày, $1,000 biến thành $1,869.
Sau đó, tôi thử bộ tham số khá liều lĩnh hơn:
Kết quả: sau 2 ngày, lợi nhuận đầu tư là -50%.
Điều này rõ ràng cho thấy việc chọn tham số là yếu tố quan trọng nhất. Nó có thể giúp bạn kiếm nhiều tiền hoặc gây ra thua lỗ lớn.
Giới hạn của backtest
Ngay cả khi đã tính phí và chênh lệch giá, backtest vẫn có những giới hạn của nó.
Để giữ tính thận trọng, tôi áp dụng quy tắc: nếu Leg 2 không thực hiện trước khi thị trường đóng, Leg 1 sẽ coi như toàn bộ mất (total loss).
Điều này rất bảo thủ, nhưng không phải lúc nào cũng phù hợp với thực tế:
Dù thua lỗ có thể bị đánh giá quá cao, nhưng điều này cung cấp một kịch bản “tồi tệ nhất” hữu ích.
Quan trọng nhất, backtest không thể mô phỏng tác động của các lệnh lớn của bạn lên sổ lệnh hoặc hành vi thu hút các trader khác săn lùng bạn. Trong thực tế, lệnh của bạn có thể:
Backtest giả định bạn là một người chỉ lấy thanh khoản (price taker), không ảnh hưởng gì.
Cuối cùng, nó không mô phỏng giới hạn tần suất (rate limits), lỗi API, lệnh bị từ chối, tạm dừng, timeout, kết nối lại, hoặc robot bỏ lỡ tín hiệu do bận rộn.
Backtest rất hữu ích để xác định phạm vi tham số tốt, nhưng không đảm bảo 100%, vì một số hiệu ứng thực tế không thể mô phỏng.
Cơ sở hạ tầng
Tôi dự định chạy robot này trên Raspberry Pi để tránh tiêu tốn tài nguyên của máy chủ chính, và duy trì hoạt động 24/7 liên tục.
Tuy nhiên, vẫn còn nhiều cải tiến có thể thực hiện:
Chắc chắn còn nhiều tối ưu khác tôi chưa khám phá. Hiện tại, tôi đang học Rust vì nó đang trở thành ngôn ngữ không thể thiếu trong phát triển Web3.
#####