Tutorial de Bot da Polymarket · Capítulo 29 de 32
Crie um engine de paper trading da Polymarket antes de ir ao ar: simule ordens contra preços reais, acompanhe P&L, imponha a gate de 30 trades (>=55% de win rate, +PnL) antes de qualquer capital ao vivo, e a estrutura de código.
O que este capítulo cobre
Paper trading é a etapa inegociável entre a ideia da estratégia e o deploy ao vivo. Este capítulo é o simple paper engine que bloqueou todos os bots ao vivo que lançamos - com menos de 200 linhas de Python, registra cada trade em um diário JSONL, aplica as mesmas fees/slippage do caminho ao vivo.
- Por que paper antes do live (sempre)
- A gate de 30 trades (>=55% WR verificado + PnL positivo)
- Construindo um simple paper engine
- Rastreando o diário de paper junto com o diário live
- Quando paper diverge do live (e por quê)
- Passando para o live: primeiro depósito pequeno
- Código: minimal paper engine
Por que paper antes do live (sempre)
A gate de 30 trades em paper é a única disciplina que separa os 7,6% de traders lucrativos da Polymarket dos 84,1% que perdem. A maioria dos builders pula essa etapa e paga tuition. O motivo honesto de isso funcionar: paper trading revela a true win rate da estratégia em amostras suficientes para distinguir sinal de sorte.
Pular o paper custa mais do que economiza. Uma estratégia que parece lucrativa em backtest, mas na verdade é um coin flip, vai queimar US$ 200-500 de capital ao vivo antes de produzir um conjunto de 30 amostras de dados ao vivo. Fazer paper-trading dessas mesmas 30 trades custa US$ 0.
O paper engine não precisa ser sofisticado. Ele precisa ser honesto - mesmas fees, mesmo slippage, mesma fill latency que o caminho ao vivo. Quanto mais simples, melhor, porque qualquer coisa opcional é cortada e o bot vai ao ar antes da hora.
A gate de 30 trades (>=55% WR verificado + PnL positivo)
A gate é binária: 30 paper trades fechadas, critérios de sucesso definidos com antecedência (normalmente WR ≥ 55% em uma estratégia com EV positivo), ou sem deploy ao vivo.
30 é o tamanho mínimo de amostra em que o intervalo de confiança de 95% sobre a true win rate fica estreito o suficiente para distinguir sinal de ruído. Abaixo de 30, uma taxa observada de 60% pode corresponder a uma taxa real de 45-75%. Com 30+, o intervalo encolhe para ~50-70% - ainda amplo, mas suficiente para descartar "a estratégia é um coin flip."
Os critérios de sucesso precisam ser definidos ANTES de começar a rodada de paper. Defini-los depois produz racionalização pós-fato (você vai encontrar um jeito de interpretar qualquer conjunto de 30 trades como "bom o suficiente").
Construindo um simple paper engine
O paper engine é essencialmente o código de trading ao vivo com a função de placement de ordem trocada por um fill simulado. A simulação:
- Ler o live order book: mesma chamada que o bot ao vivo faria.
- Simular fill: se comprar em FOK com preço ≥ best ask, preencha a ordem na média ponderada por volume dos asks consumidos; registre o fill no paper diary.
- Aplicar fees: subtraia as mesmas fees que o caminho ao vivo pagaria.
- Rastrear inventory: mantenha um paper-balance paralelo e um dicionário de paper-positions.
O engine inteiro cabe em 100-200 linhas de Python. A disciplina-chave: toda suposição que o caminho ao vivo faz (fill rate, latency, fee) precisa ser reproduzida em paper, mesmo que ligeiramente pior do que a realidade - paper deve ser o piso, não o teto.
Rastreando o diário de paper junto com o diário live
A rodada de paper trading produz um diário JSONL indistinguível em estrutura do diário live que o bot escreverá depois. Os mesmos campos: timestamp, action, market_slug, side, size, price, expected_fill_price, simulated_pnl_at_exit.
Dois motivos para usar o mesmo formato. Primeiro, as ferramentas de análise que leem trades ao vivo (relatórios de PnL, calculadoras de win rate) funcionam em paper sem modificação. Segundo, comparar paper com live depois pega divergências que indicam bugs.
Dica de produção: faça o paper engine escrever em per_trade_paper.jsonl no mesmo diretório do per_trade.jsonl ao vivo. Um único comando compara ambos: diff -y <(jq -r .market_slug per_trade.jsonl) <(jq -r .market_slug per_trade_paper.jsonl).
Quando paper diverge do live (e por quê)
Divergências entre paper e live são inevitáveis. Três comuns.
- Slippage: paper preenche no snapshot do ask; no live, a ordem percorre o book e pode preencher 1-2c pior em mercados finos. Solução: simule slippage em paper adicionando uma penalidade por trade igual à metade do spread.
- Fill latency: paper preenche instantaneamente; o live leva 200-500ms, durante os quais o preço pode mudar. Solução: simular esperando e relendo o book antes de "preencher" em paper.
- Adverse selection: paper assume que você recebe o melhor ask; no live, você compete com outros bots que talvez já tenham levantado aquele ask. Solução: mais difícil de simular; reconheça honestamente para si mesmo que paper superestima.
Quando paper diz +5%/mês e o live roda em -2%/mês, a diferença geralmente é uma dessas. Audite uma por uma em vez de presumir que a estratégia em si estava errada.
Passando para o live: primeiro depósito pequeno
Paper passa em 30 trades. Plano de deploy ao vivo:
- Deposite US$ 25-50 como capital de smoke-test. Trate como tuition; se perder, a lição valeu a pena.
- Rode o bot em modo live por 5-10 trades com posições no tamanho mínimo (5 shares).
- Verifique se cada fill corresponde às expectativas do paper dentro de 2c. Investigue qualquer diferença maior antes de continuar.
- Se 5-10 trades ao vivo baterem com o paper, deposite US$ 200-500 e rode posições de tamanho normal.
- Se não baterem, pare, faça debug, corrija e recomece do passo 1.
A diferença mais comum entre live e paper no primeiro deploy é uma fee ausente ou uma estimativa de slippage errada. Corrigir isso é direto; a disciplina é detectar a diferença antes de escalar capital.
Código: minimal paper engine
Referência: simple paper engine que lê o book ao vivo + simula 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}
Adições de produção: função de venda paper (espelho da compra), simulação paper GTC (postar no book a preço, simular fill quando o mid alcançar o preço), reconciliação entre o paper diary e o diário live "que teria sido".





