Polymarket Bot Tutorial · Chương 29 trong 32

Xây dựng một paper trading engine cho Polymarket trước khi chạy live: mô phỏng lệnh theo giá thực, theo dõi P&L, áp dụng cửa 30 giao dịch (>=55% win rate, +PnL) trước khi dùng vốn live, và khung code.

Chương này bao gồm những gì

Paper trading là bước không thể bỏ qua giữa ý tưởng chiến lược và triển khai live. Chương này là paper engine đơn giản đã chặn mọi live bot mà chúng tôi từng triển khai - dưới 200 dòng Python, theo dõi mọi giao dịch trong một diary JSONL, áp dụng cùng fee/slippage như luồng live.

  • Vì sao phải paper trước khi live (luôn luôn)
  • Cửa 30 giao dịch (được xác minh +55% WR + positive PnL)
  • Xây dựng một paper engine đơn giản
  • Theo dõi paper diary song song với live diary
  • Khi paper lệch so với live (và vì sao)
  • Chuyển sang live: khoản nạp nhỏ đầu tiên
  • Code: paper engine tối giản

Vì sao phải paper trước khi live (luôn luôn)

Cửa 30 giao dịch paper là kỷ luật duy nhất phân tách 7.6% trader Polymarket có lợi nhuận với 84.1% người thua lỗ. Phần lớn builder bỏ qua bước này và phải trả học phí. Lý do thật sự khiến nó hiệu quả: paper trading cho thấy win rate thực của chiến lược trên đủ số mẫu để phân biệt tín hiệu với may rủi.

Bỏ qua paper tốn nhiều hơn số tiền tiết kiệm được. Một chiến lược trông có vẻ có lãi trong backtest nhưng thực ra chỉ là tung đồng xu sẽ đốt $200-500 vốn live trước khi tạo ra đủ 30 mẫu dữ liệu live. Paper-trade cùng 30 giao dịch đó tốn $0.

Paper engine không cần quá tinh vi. Nó cần trung thực - cùng fee, cùng slippage, cùng độ trễ fill như luồng live. Càng đơn giản càng tốt, vì bất cứ thứ gì mang tính tùy chọn đều sẽ bị cắt bớt và bot sẽ lên live sớm hơn đáng lẽ.

Cửa 30 giao dịch (được xác minh +55% WR + positive PnL)

Cửa này là dạng nhị phân: 30 paper trades đã đóng, tiêu chí thành công được viết sẵn từ trước (thường là WR ≥ 55% với một chiến lược positive-EV), hoặc không triển khai live.

30 là cỡ mẫu tối thiểu để khoảng tin cậy 95% của win rate thực đủ hẹp nhằm phân biệt tín hiệu với nhiễu. Dưới 30, một tỷ lệ quan sát 60% có thể tương ứng với tỷ lệ thực từ 45-75%. Từ 30 trở lên, khoảng này thu hẹp còn khoảng 50-70% - vẫn rộng, nhưng đủ để loại trừ chuyện "chiến lược chỉ là tung đồng xu."

Các tiêu chí thành công phải được đặt RA TRƯỚC khi paper run bắt đầu. Đặt sau đó sẽ tạo ra sự hợp lý hóa hậu nghiệm (bạn sẽ tìm ra cách diễn giải bất kỳ 30 giao dịch nào là "đủ tốt").

Xây dựng một paper engine đơn giản

Paper engine về cơ bản là code giao dịch live, chỉ thay hàm đặt lệnh bằng một fill mô phỏng. Phần mô phỏng:

  • Đọc live order book: cùng lời gọi như bot live sẽ thực hiện.
  • Mô phỏng fill: nếu mua theo FOK với giá ≥ best ask, fill lệnh ở mức volume-weighted average của các ask đã bị consume; ghi fill vào paper diary.
  • Áp dụng fee: trừ cùng mức fee mà luồng live sẽ trả.
  • Theo dõi inventory: duy trì một paper-balance và paper-positions dictionary song song.

Cả engine chỉ gói trong 100-200 dòng Python. Kỷ luật quan trọng: mọi giả định mà luồng live dùng (fill rate, latency, fee) đều phải được tái hiện trong paper, kể cả khi hơi xấu hơn thực tế - paper nên là sàn, không phải trần.

Theo dõi paper diary song song với live diary

Paper trading run tạo ra một diary JSONL không khác gì về cấu trúc so với live diary mà bot sẽ ghi sau này. Cùng các field: timestamp, action, market_slug, side, size, price, expected_fill_price, simulated_pnl_at_exit.

Có hai lý do để dùng cùng định dạng. Thứ nhất, các công cụ phân tích đọc live trades (P&L reports, win-rate calculators) sẽ chạy trên paper mà không cần chỉnh sửa. Thứ hai, việc so sánh paper với live sau này sẽ phát hiện những lệch pha cho thấy bug.

Mẹo production: để paper engine ghi vào per_trade_paper.jsonl trong cùng thư mục với live per_trade.jsonl. Một lệnh duy nhất sẽ so sánh cả hai: diff -y <(jq -r .market_slug per_trade.jsonl) <(jq -r .market_slug per_trade_paper.jsonl).

Khi paper lệch so với live (và vì sao)

Sự lệch giữa paper và live là điều không tránh khỏi. Ba nguyên nhân phổ biến.

  • Slippage: paper fill theo snapshot của ask; live sẽ đi qua book và có thể fill xấu hơn 1-2c ở các thị trường mỏng. Giải pháp: mô phỏng slippage trong paper bằng cách thêm một penalty cho mỗi giao dịch bằng một nửa spread.
  • Fill latency: paper fill ngay lập tức; live mất 200-500ms, trong lúc đó giá có thể di chuyển. Giải pháp: mô phỏng bằng cách chờ rồi đọc lại book trước khi "fill" trong paper.
  • Adverse selection: paper giả định bạn lấy được best ask; live phải cạnh tranh với các bot khác vốn có thể đã lifting ask đó. Giải pháp: khó mô phỏng hơn; hãy thẳng thắn với chính mình rằng paper đang đánh giá quá cao.

Khi paper nói +5%/tháng còn live chạy ở -2%/tháng, chênh lệch thường là một trong ba thứ này. Hãy audit từng mục thay vì mặc định rằng bản thân chiến lược đã sai.

Chuyển sang live: khoản nạp nhỏ đầu tiên

Paper vượt qua 30 giao dịch. Kế hoạch triển khai live:

  1. Nạp $25-50 làm vốn smoke-test. Hãy coi đó là học phí; nếu mất nó, bài học vẫn đáng giá.
  2. Chạy bot ở chế độ live trong 5-10 giao dịch với vị thế ở size tối thiểu (5 shares).
  3. Xác minh mỗi fill khớp với kỳ vọng paper trong phạm vi 2c. Điều tra mọi khoảng lệch lớn hơn trước khi tiếp tục.
  4. Nếu 5-10 giao dịch live khớp với paper, nạp $200-500 và chạy vị thế kích thước bình thường.
  5. Nếu không khớp, dừng lại, debug, sửa lỗi, rồi bắt đầu lại từ bước 1.

Khoảng lệch live-paper phổ biến nhất ở lần triển khai đầu tiên là thiếu fee hoặc ước tính slippage sai. Sửa những thứ đó khá đơn giản; kỷ luật nằm ở việc phát hiện khoảng lệch trước khi tăng vốn.

Code: paper engine tối giản

Tham khảo: paper engine đơn giản đọc live book + mô phỏng fill FOK.

import json, time
PAPER_BAL = 10_000.0   # USD starting
positions = {}         # token_id -> shares

def paper_fok_buy(token_id, max_price, size):
    book = fetch_book(token_id)
    # Walk asks, fill what we can within max_price
    filled = 0; cost = 0
    for level in book.asks:
        px = float(level["price"])
        if px > max_price: break
        avail = float(level["size"])
        take = min(avail, size - filled)
        filled += take
        cost += take * px
        if filled >= size: break
    if filled < size:
        return {"status":"rejected","filled":0}  # FOK semantics

    global PAPER_BAL
    PAPER_BAL -= cost
    positions[token_id] = positions.get(token_id, 0) + filled

    log_paper({"ts": int(time.time()), "action":"buy",
               "token": token_id, "size": filled, "price": cost/filled})
    return {"status":"matched","filled":filled,"cost":cost}

Bổ sung cho production: paper sell function (mirror của buy), paper GTC simulation (post lên book ở một mức giá, mô phỏng fill khi mid chạm giá đó), reconciliation giữa paper diary và "would-have-been" live diary.

Các câu hỏi thường gặp

Cửa 30 giao dịch là gì?
Đây là quy tắc gating nội bộ của chúng tôi để chuyển từ paper sang live: ít nhất 30 paper trades đã đóng, win rate >= 55%, và net PnL dương sau khi trừ slippage. Thiếu bất kỳ điều kiện nào trong số này thì bạn vẫn phải ở paper. Chúng tôi tạo ra quy tắc này sau nhiều lần go-live quá sớm khiến tài khoản bị xóa sạch trong năm 2025.
Vì sao là 30 giao dịch mà không phải 100?
Vì statistical power. Với 30 giao dịch, win rate 55% có xác suất khoảng 70% là edge thật (không phải nhiễu). Với 100 giao dịch, độ tin cậy đó tăng lên hơn 90%. Chúng tôi chọn 30 làm mức tối thiểu vì thời gian paper dài hơn thường dẫn đến overfitting - trader chỉnh chiến lược quá lâu thay vì kiểm tra nó.
Tôi có thể bỏ qua paper trading nếu tôi rất tự tin không?
Chính lúc tự tin là lúc bạn không nên bỏ qua. Những bot chạy tốt nhất trên Polymarket là của những người từng sai trước đó. Cửa 30 giao dịch tồn tại để bắt những chiến lược trông có vẻ đúng nhưng thực ra không phải. Phần lớn chiến lược của chính chúng tôi ban đầu đều fail paper - đó chính là giá trị của nó.
Kết quả paper có khớp với kết quả live không?
Thường là có với chiến lược chậm (politics, weather), không với chiến lược nhanh (crypto 5 phút, sports microstructure). Khoảng lệch nằm ở chỗ "paper trading không trả slippage" - fill thực tế sẽ xấu hơn giá bạn nhìn thấy. Chúng tôi thường giảm kết quả paper 30-50% trước khi tin rằng nó đủ tốt cho live, nhất là với chiến lược nhanh.
Làm sao để triển khai paper engine bằng Python?
Subscribe vào CLOB WebSocket thật cho các market bạn giao dịch. Khi chiến lược quyết định "đặt lệnh", hãy log nó vào một file JSONL với mức giá fill có thể xảy ra (bid hiện tại cho lệnh mua, ask hiện tại cho lệnh bán). Theo dõi vị thế theo cách virtual. Mark-to-market theo giá live. Cả engine chỉ khoảng 200 dòng Python.
Tôi nên paper trade bao lâu trước khi chạy live?
Cho đến khi đạt cửa 30 giao dịch, hoặc 2-4 tuần tùy thời gian nào dài hơn. Nếu bạn chạm cửa quá nhanh, bạn đang overfitting; hãy chậm lại và xác minh win rate của bạn bền vững trước các thay đổi tham số nhỏ. Nếu sau nhiều tháng vẫn không chạm được cửa, chiến lược đó có lẽ không có edge và bạn nên loại bỏ nó.