# Risks & Pitfalls — What Kills Polymarket Bots

A blunt list of the failure modes that consistently cost real money. Surface the relevant ones in the README of every generated bot.

## Money pitfalls

### The 2% fee trap

Polymarket charges **0–2% on profits** on most markets (zero on some). Many builders forget it in their edge calc. It applies to net profit, not notional, but at small spreads (1–2%) it can flip you from profitable to negative.

Always compute edge as: `(payout - cost) * (1 - fee_rate) > expected_slippage + gas + LATENCY_RISK_BUFFER`. The buffer is non-negotiable for arb strategies — markets move while you're submitting.

### Polygon gas — usually fine, sometimes not

Default Polygon gas is ~$0.007 per tx, which is negligible. But:

- During congestion (rare but real), priority fees spike to $0.10+
- Approval transactions (one-time per allowance) are bigger — budget $0.05 each
- The order-placement itself doesn't cost gas (it's off-chain matching, on-chain settlement by the operator). You only pay gas for splits/merges/redemptions.

Set `MIN_PRIORITY_FEE_GWEI=30` and `MIN_MAX_FEE_GWEI=60` in your config as floors. Don't let the bot hold up settlement by underbidding.

### Capital lock-up surprises

- Markets can take **hours to days** to resolve after the event. UMA's optimistic oracle has a challenge period. End-game sweep relies on this; copy-trading suffers from it.
- Disputed resolutions can lock funds for **a week or more**.
- Black-swan resolutions: UMA voters override the obvious answer. **No refunds.** Polymarket has historically declined to refund users on governance attacks.

Mitigation: never put more than ~10% of bankroll in one market; whitelist categories where oracle history is clean.

## Execution pitfalls

### Slippage on thin books

Polymarket's CLOB is **not** an AMM — slippage is a function of order book depth, not a curve. On deep books, $100 USDC orders execute near the quoted price. On thin books, you can take 5–10 cents of slippage on a $100 order, killing every strategy.

Hard rule: never take more than ~5% of the displayed depth in one order. If you need more size, slice across time.

### FOK vs market

A naked market order on Polymarket can give you a terrible price. Always prefer:

- **FOK** for arbitrage and copy trading — fill at your price or don't fill at all
- **GTC** for end-game sweep, market making, AI bot — sit on the book and wait
- **FAK** when partial fills are OK and you'd rather get something than nothing

The SDK accepts a `price_tolerance_bps` field on market orders that caps slippage — set it; don't trust the default.

### Non-atomic arbitrage

Polymarket arb is non-atomic — you submit two orders, one can fill, the other can fail. If you're not handling this, you'll regularly end up holding directional risk on a "guaranteed" trade.

Always:
1. Submit both legs in parallel (same event-loop tick).
2. On any partial fill, **immediately** market-sell the filled leg.
3. Log every flatten event — they're learning data for tightening your filters.

### Paper-trading-vs-live divergence

A trader noted: paper trading showed **$20/min gains**, live runs hit slippage + minimum order constraints, **net $130 loss across 5 sessions**.

Reasons paper trading lies:
- Paper assumes you fill at midpoint; live, you fill at the offer.
- Paper ignores Polymarket's 5-share minimum on most tokens (5 shares × $0.95 = $4.75 minimum spend).
- Paper ignores latency between detection and submission.
- Paper assumes both legs fill on arb; in reality only one does ~5% of the time.

Always paper-trade with the **actual order book** as the fill price (i.e. simulate hitting the offer, not the mid), not synthetic prices.

## Security pitfalls

### Private key handling

- **Never log the private key.** Not even truncated. Logs end up in screenshots, copilot caches, Notion pages.
- **Never commit `.env`.** First file in `.gitignore`.
- Use a dedicated wallet for the bot, separate from any personal wallet. Limit it to bot capital only. If keys leak, you lose only what's there.
- For production, consider a remote signer (e.g. Web3Auth, Fireblocks) so the bot never sees the raw key. For most retail use this is overkill.

### The Feb-2026 nonce-cancellation attack

A real attack discovered in Feb 2026 that specifically hurts market makers and arb bots:

The attacker places a normal limit order via the API. Off-chain matching succeeds and matches the attacker against a market maker's resting order. The attacker then drains the funder address on-chain, causing the settlement transaction to revert. The reverted tx **wipes the matched orders from the book** — including the maker's order, which is now removed without the maker getting filled.

Cost per attack: < $0.10 in gas. Documented profit on one wallet: **$16,427 over 7 markets**.

Polymarket has not patched this. The community-built `Nonce Guard` open-source tool monitors order cancellations on Polygon, builds a blacklist of attacker addresses, and provides alerts.

**For a market-maker bot:** integrate Nonce Guard or your own equivalent — watch the on-chain `OrderCancelled` events for unusual patterns and pause quoting in affected markets temporarily.

**For an arb bot:** less affected; the bot's orders are FOK so there's nothing for the attacker to wipe. But maker bots co-located on your IP can be affected — keep them isolated.

### Replay / front-run risk

Polymarket settles via a centralized operator (off-chain matching), so MEV-style front-running is limited compared to AMMs. But:

- API rate limits can be hit by an adversary spamming your wallet's API key — rotate keys if you suspect it
- Watch for mempool monitoring: any Polygon tx you submit is visible. Don't assume your "secret strategy" is hidden.

## Operational pitfalls

### Rate limiting

Polymarket fronts everything with Cloudflare. Hit the limit → HTTP 429 → temporary throttling, sometimes IP-level.

- Use `tenacity` for exponential backoff on every REST call.
- Prefer WebSocket for any continuous data flow.
- Cache aggressively — leaderboards, market metadata, tags all change slowly.
- Run from a stable IP — residential connections sometimes get bucketed with abuse traffic and get worse limits.

### Logging that isn't logging

Print statements + tail-the-terminal is not logging. Use structured JSONL:

```python
import json, time
def log(event_type: str, **kwargs):
    line = json.dumps({"ts": time.time(), "type": event_type, **kwargs})
    with open("logs/events.jsonl", "a") as f:
        f.write(line + "\n")
```

Why JSONL: you can `jq`, you can `pandas.read_json(lines=True)`, you can build a dashboard from it later without reprocessing.

Log: every signal seen (with reason for skip), every order attempted (with reason for failure), every fill, every error. It's the only way to debug a bot that's been running for two days.

### No kill switch

Every bot must have:

1. A **per-trade hard cap** (`MAX_TRADE_SIZE_USD`).
2. A **daily loss kill switch** (`MAX_DAILY_LOSS_USD`) — bot exits cleanly, cancels open orders, doesn't restart until manually flipped.
3. A **manual stop** (Ctrl+C handled with `signal.SIGINT` to cancel open orders before exit).
4. A **`DRY_RUN=true`** default — code defaults to true; only the user can flip it.

The default state of every starter template in this skill is "won't lose money even if it ships broken".

### Running on resolving markets

Markets within ~T-15min of resolution are dangerous for every strategy:
- Copy trading: the trade you mirror is an exit; you enter, can't sell, ride to 0 or 1 randomly.
- Market making: spreads widen, you get adversely selected.
- Arb: the gap you're seeing is end-game sweep, not arb — your fill is at $0.99 not $0.50.

Hard filter: skip any market where `endDate - now < 4h` for copy-trading, `< 30min` for market making, `< 15min` for arb.

## Strategy-specific gotchas

### Copy trading

- **Don't copy the leaderboard #1.** The top-of-rolling-window wallet is usually variance, not skill. Look at 30d+ track record and Sharpe.
- **Don't copy entries on terminal markets.** Filter by time-to-resolution.
- **Skip exit-only signals.** If the target sells but you don't hold the position, do not buy on their sell.
- **Watch for cascading copy.** If wallet A copies wallet B and you copy A, you're 2 hops behind the signal.

### Arbitrage

- Already covered: 2% fee, gas, slippage, non-atomic risk.
- **Tick size dynamics.** When prices reach 0.96 or 0.04, tick size changes — `WAIT_FOR_FILL` logic has to account for it or your limit will be invalid.
- **Cross-market arb on different oracles is basis risk, not arb.** Always confirm both venues resolve from the same source.

### Market making

- Already covered: nonce attack, adverse selection, inventory blowup.
- **Reward bands fill up.** Quoting inside the reward band means competing against other makers for the same band. Your share of rewards drops as more enter.
- **Cancellation gas.** Lots of re-quoting → many cancels. Wider thresholds save real money.

### AI / LLM

- **Hallucinations.** Always require web_search in tool use. Always require ≥2 sources cited.
- **Resolution criteria.** Markets resolve on exact wording. Pass the full description, not just the title.
- **API spend > profit.** Cap API spend per market and per day in code, not in a Stripe alert.
- **Recency lag.** If the model trained-on or fetched-from data is older than the market move, you're trading on stale signal. Always check the last_trade_time vs your model evaluation timestamp.

### 5-min crypto

- **Wrong strike source.** Polymarket uses Chainlink, not Binance. Use RTDS WS subscription.
- **5-share minimum.** $5 minimum spend. Don't waste micros.
- **The last 5 seconds.** ~15-20% of windows are decided in the final 10 seconds. Your edge is small if you don't have low-latency oracle access.

## "I made $X/day" — read carefully

Source videos and articles routinely cite results like:

- "$13 profit after 3 hours" (real, modest)
- "$313 → $414,000 in one month" (real but cherry-picked, almost certainly survivorship bias)
- "$2k → $75k in a single day" (real, reported, almost impossible to reproduce)
- "$2.2M in two months" (real, top-tier HFT operation, not retail)

These are the **wins**. The losses don't get videos. Treat published numbers as the upper tail, not the median. Build with discipline and small position sizes; let actual results, not other people's social proof, dictate scaling.

Source: the IMDEA paper found total documented arbitrage was **$40M extracted across all of Polymarket in 12 months**, by all bots combined. The biggest single trader was **$2.0M from 4,049 trades**, $496 average. That's the realistic high water mark.

## TL;DR — what actually kills bots

In rough order of frequency, based on the source material:

1. **Funder/signature_type mismatch** — 0 trades placed, hours wasted.
2. **No allowances on EOA wallet** — orders fail silently.
3. **Slippage on thin books** — strategy backtests great, lives terribly.
4. **Forgetting the 2% fee** — looked profitable, wasn't.
5. **Non-atomic arb without flatten path** — accumulates directional risk.
6. **Copying terminal markets** — capital locked, adverse fills.
7. **No kill switch** — one bad day, account zeroed.
8. **Paper-vs-live divergence** — confidence misplaced.
9. **No JSONL logging** — can't debug what went wrong.
10. **Hardcoded private key** — leaked to GitHub, drained.
