Polymarket Bot Tutorial · Chapter 29 of 32

실서비스에 배포하기 전에 Polymarket paper trading engine을 구축하세요: real price에 대해 order를 시뮬레이션하고, P&L을 추적하며, 실제 live capital 투입 전에 30-trade gate(>=55% win rate, +PnL)를 강제하고, code skeleton을 작성합니다.

이 장에서 다루는 내용

Paper trading은 strategy idea와 live deployment 사이에서 반드시 거쳐야 하는 단계입니다. 이 장은 우리가 배포한 모든 live bot에 대해 gate 역할을 해온 간단한 paper engine입니다. 200 lines가 되지 않는 Python으로 구현되며, 모든 trade를 JSONL diary에 기록하고, live path와 동일한 fees/slippage를 적용합니다.

  • 왜 live 전에 paper를 하는가(항상)
  • 30-trade gate(검증된 +55% WR + positive PnL)
  • 간단한 paper engine 구축하기
  • live diary와 함께 paper diary 추적하기
  • paper가 live와 달라지는 경우(그리고 그 이유)
  • live로 졸업하기: 작은 첫 deposit
  • Code: minimal paper engine

왜 live 전에 paper를 해야 하는가(항상)

30-trade paper gate는 수익을 내는 Polymarket trader 7.6%와 손실을 보는 84.1%를 가르는 단 하나의 규율입니다. 대부분의 builder는 이를 건너뛰고 수업료를 냅니다. 이 방식이 효과적인 솔직한 이유는: paper trading이 충분한 sample에서 strategy의 진짜 win rate를 드러내어 signal과 luck을 구분해 주기 때문입니다.

paper를 생략하면 절약하는 것보다 더 많은 비용이 듭니다. backtest에서는 수익성 있어 보이지만 실제로는 coin flip인 strategy는 live capital $200-500을 소모한 뒤에야 30-sample size의 live data를 만들어 냅니다. 같은 30 trades를 paper-trading하는 데는 $0이 듭니다.

paper engine은 복잡할 필요가 없습니다. 정직해야 합니다. 즉, live path와 같은 fees, 같은 slippage, 같은 fill latency가 있어야 합니다. 더 단순할수록 좋습니다. 왜냐하면 선택 사항은 무엇이든 잘려나가고 bot이 예상보다 일찍 live로 배포되기 때문입니다.

30-trade gate(검증된 +55% WR + positive PnL)

gate는 이진적입니다: 30개의 closed paper trades, 사전에 정의된 성공 기준(일반적으로 positive-EV strategy에서 WR ≥ 55%), 아니면 live deployment 없음.

30은 true win rate에 대한 95% confidence interval이 signal과 noise를 구분할 만큼 충분히 좁아지는 최소 sample size입니다. 30 미만에서는 관측된 60% rate가 실제로는 45-75%의 true rate에 해당할 수 있습니다. 30 이상에서는 interval이 대략 50-70%로 좁아집니다. 여전히 넓지만, "이 strategy는 coin flip이다"를 배제하기에는 충분합니다.

성공 기준은 paper run이 시작되기 전에 반드시 설정해야 합니다. 나중에 설정하면 사후적 합리화(post-hoc rationalization)가 발생합니다(어떤 30 trades든 "충분히 좋다"고 해석할 방법을 찾게 됩니다).

간단한 paper engine 구축하기

paper engine은 본질적으로 order-placement function을 simulated fill로 바꾼 live trading code입니다. 시뮬레이션은 다음과 같습니다.

  • Live order book 읽기: live bot이 호출할 것과 동일한 call을 사용합니다.
  • Fill 시뮬레이션: FOK로 buy할 때 price가 best ask 이상이면, 소비된 asks의 volume-weighted average로 order를 fill하고, paper diary에 fill을 기록합니다.
  • Fees 적용: live path가 지불할 동일한 fees를 차감합니다.
  • Inventory 추적: parallel paper-balance와 paper-positions dictionary를 유지합니다.

전체 engine은 100-200 lines의 Python에 들어갑니다. 핵심 규율은 live path가 가정하는 모든 것(fill rate, latency, fee)을 paper에서도 재현해야 한다는 점입니다. 현실보다 약간 더 나쁘게 재현해도 괜찮습니다. paper는 ceiling이 아니라 floor여야 하기 때문입니다.

live diary와 함께 paper diary 추적하기

paper trading run은 bot이 나중에 작성할 live diary와 구조상 구별되지 않는 JSONL diary를 생성합니다. 동일한 fields: timestamp, action, market_slug, side, size, price, expected_fill_price, simulated_pnl_at_exit.

같은 format을 써야 하는 이유는 두 가지입니다. 첫째, live trades를 읽는 analysis tools(PnL reports, win-rate calculators)가 수정 없이 paper에도 작동합니다. 둘째, 나중에 paper와 live를 비교하면 bugs를 나타내는 divergences를 잡아낼 수 있습니다.

Production tip: paper engine이 live per_trade.jsonl과 같은 directory에 per_trade_paper.jsonl을 쓰게 하세요. 한 번의 command로 둘 다 비교할 수 있습니다: diff -y <(jq -r .market_slug per_trade.jsonl) <(jq -r .market_slug per_trade_paper.jsonl).

paper가 live와 달라지는 경우(그리고 그 이유)

paper와 live 사이의 divergence는 피할 수 없습니다. 흔한 경우는 세 가지입니다.

  • Slippage: paper는 ask snapshot에서 fill되지만, live는 book을 따라가며 얇은 market에서는 1-2c 더 나쁘게 fill될 수 있습니다. 해결책: trade당 spread의 절반에 해당하는 penalty를 추가하여 paper에서 slippage를 시뮬레이션합니다.
  • Fill latency: paper는 즉시 fill되지만, live는 200-500ms가 걸리고 그 사이 price가 움직일 수 있습니다. 해결책: paper에서 "fill"하기 전에 잠시 기다린 뒤 book을 다시 읽도록 시뮬레이션합니다.
  • Adverse selection: paper는 최상의 ask를 얻는다고 가정하지만, live에서는 이미 그 ask를 들어 올린 다른 bot과 경쟁합니다. 해결책: 더 시뮬레이션하기 어렵습니다. paper가 과대평가한다는 점을 스스로에게 솔직히 인정해야 합니다.

paper에서는 +5%/month인데 live에서는 -2%/month라면, 그 차이는 대개 위 셋 중 하나입니다. strategy 자체가 틀렸다고 가정하기보다 하나씩 점검하세요.

live로 졸업하기: 작은 첫 deposit

Paper에서 30 trades를 통과했습니다. live deployment 계획은 다음과 같습니다.

  1. smoke-test capital로 $25-50를 deposit합니다. tuition이라고 생각하세요. 잃더라도 그 교훈의 가치는 충분합니다.
  2. minimum size(5 shares) position으로 bot을 live mode에서 5-10 trades 실행합니다.
  3. 각 fill이 paper expectation과 2c 이내로 일치하는지 확인합니다. 더 큰 gap은 계속하기 전에 조사합니다.
  4. 5-10 live trades가 paper와 일치하면 $200-500를 deposit하고 normal-size position을 실행합니다.
  5. 일치하지 않으면 중단하고, debug하고, 수정한 뒤 step 1부터 다시 시작합니다.

첫 deployment에서 가장 흔한 live-paper gap은 빠진 fee 또는 slippage misestimate입니다. 수정은 간단합니다. 중요한 것은 capital을 확장하기 전에 gap을 잡아내는 discipline입니다.

Code: minimal paper engine

Reference: live book을 읽고 FOK fill을 시뮬레이션하는 simple paper engine.

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}

Production additions: paper sell function(buy의 mirror), paper GTC simulation(book 위에 price로 post한 뒤 mid가 price에 도달하면 fill을 시뮬레이션), paper diary와 "would-have-been" live diary 간 reconciliation.

자주 묻는 질문

30-trade gate가 무엇인가요?
paper에서 live로 넘어가기 위한 내부 gating rule입니다: 최소 30개의 closed paper trades, win rate >= 55%, 그리고 slippage를 반영한 net PnL이 positive여야 합니다. 이 중 하나라도 실패하면 paper에 머물러야 합니다. 이 규칙은 2025년에 너무 이른 go-live 시도가 여러 계좌를 날려버린 뒤 만들었습니다.
왜 100 trades가 아니라 30 trades인가요?
Statistical power 때문입니다. 30 trades에서는 55% win rate가 진짜 edge(잡음이 아님)일 확률이 대략 70%입니다. 100 trades에서는 그 신뢰도가 90% 이상으로 올라갑니다. 우리는 30을 최소 기준으로 선택했습니다. 더 긴 paper 기간은 종종 overfitting을 유발하기 때문입니다. trader들이 테스트하는 대신 strategy를 너무 오래 조정하게 됩니다.
확신이 있으면 paper trading을 건너뛰어도 되나요?
바로 그때가 건너뛰면 안 되는 순간입니다. Polymarket에서 가장 잘하는 bot은 이미 틀려본 적이 있는 사람들이 운영합니다. 30-trade gate는 맞아 보이지만 실제로는 아닌 strategies를 걸러내기 위해 존재합니다. 우리 own strategies 중 다수도 처음에는 paper에서 실패했습니다. 그게 가치입니다.
paper 결과와 live 결과가 일치하나요?
대체로 느리게 움직이는 strategy(정치, 날씨)에서는 yes, 빠른 strategy(5-min crypto, sports microstructure)에서는 no입니다. gap의 핵심은 "paper trading does not pay slippage"입니다. 실제 fill은 보았던 price보다 더 나쁩니다. 특히 빠른 strategy에서는 live를 믿기 전에 paper return을 30-50% 할인합니다.
Python에서 paper engine은 어떻게 구현하나요?
거래하는 market에 대해 실제 CLOB WebSocket에 subscribe합니다. strategy가 "order를 넣는다"고 결정하면, 그 주문을 JSONL file에 would-be fill price와 함께 기록합니다(buy는 current bid, sell은 current ask). position은 가상으로 추적합니다. live price를 기준으로 mark-to-market합니다. 전체 engine은 Python 약 200 lines입니다.
live로 가기 전에 얼마나 오래 paper trade해야 하나요?
30-trade gate를 충족할 때까지, 또는 2-4주 중 더 긴 기간까지입니다. gate에 너무 빨리 도달한다면 overfitting 중이라는 뜻입니다. 속도를 늦추고 작은 parameter change에도 win rate가 견고한지 확인하세요. 몇 달이 지나도 gate에 도달하지 못한다면 strategy에 edge가 없을 가능성이 높으므로 중단해야 합니다.