Polymarket Bot Tutorial · Глава 32 из 32
Реальные ошибки Polymarket bot и postmortem: phantom fills, sticky-fail dedup, lol-ctg-ccg whipsaw, баг флага NegRisk, premature go-live - с commits и датами, которые исправили каждую из них.
Что покрывает эта глава
Наш собственный production diary багов, которые стоили реальных денег. Важнее не детали, а pattern - одни и те же классы багов повторяются в bot'ах, а лечится это обычно отсутствующим watchdog, а не лучшей strategy. Эта глава призвана сэкономить вам tuition.
- Phantom fills (commits e68a087, 8bb7761)
- Баг флага NegRisk (commit 06deaef)
- Sticky-fail dedup (commit 4c0bef1)
- Whipsaw incident: lol-ctg-ccg
- Premature go-live: wipe 2025 года
- Sleep-through-bug: kill switch сработал
- Уроки, которые обобщаются
Phantom fills (commits e68a087, 8bb7761)
Первый крупный phantom-fill incident в нашем trader - май 2025 года. Bot выставил 22 FOK buy, все были matched на CLOB. Сразу после этого bot попытался разместить 22 GTC sell. 8 из них были rejected с сообщением "balance: 0 / sum of active orders: 0 / order amount: 10000000."
Root cause: settlement lag (chapter 12). CLOB matched за 100ms, bot отправил sell через 200ms, но transfer Polygon ERC-1155 занял примерно 2 секунды. CLOB отклонил sell, потому что chain по-прежнему показывал нулевой balance.
Fix: вставить blocking wait на 5 секунд между любым успешным buy и любым последующим GTC на том же token. Commits e68a087 и 8bb7761. С тех пор - zero phantom-fill incidents.
Lesson: API time и chain time - это разные timelines. Code, который предполагает, что они синхронны, столкнется именно с этим failure mode.
Баг флага NegRisk (commit 06deaef)
В NegRisk multi-outcome event с 8 candidates был кратковременный arb в 1.8c (sum of YES asks = 0.982). Наш arber отправил все 8 FOK buy. 6 из них filled; 2 settled в неправильный exchange contract.
Root cause: bot вызывал createAndPostOrder, не устанавливая negRisk: true в object flags. У двух рынков была другая historical creation date, и им требовался этот flag; шести он не был нужен, потому что их underlying contract уже маршрутизировался через NegRisk по умолчанию.
Fix: читать market.negRisk из Gamma для каждого market и передавать дальше во все order call. Commit 06deaef. Мы повторно прогнали arb с установленным flag; оставшиеся 2 settled корректно.
Lesson: никогда не ставьте property рынка по умолчанию. Каждый раз читайте его явно из source of truth.
Sticky-fail dedup (commit 4c0bef1)
Bot повторил неудачный buy 5 раз за 12 секунд. Первая попытка на самом деле succeeded (network timeout не дал bot'у увидеть response); следующие 4 retries создали еще 4 additional positions. Итого: 5 positions на одном и том же market, когда нам была нужна 1.
Root cause: отсутствовал idempotent client-order-id. Retry logic у bot'а была такой: "если не получилось, попробуй снова с новым salt." У CLOB не было способа распознать retries как duplicates.
Fix: генерировать deterministic UUID для каждого intended order до первой попытки. Все retries используют один и тот же client-order-id, что позволяет CLOB делать dedup. Commit 4c0bef1.
Lesson: retries без idempotence - это duplicates. У каждого order должен быть стабильный client-side identifier.
Whipsaw incident: lol-ctg-ccg
В esports match (CTG vs CCG) bot вошел в buy по 0.45, когда imbalance стал positive. В течение 30 секунд imbalance стал negative, и наш GTC sell по 0.50 был исполнен чьим-то чужим order. PnL: +5c × 10 shares = +$0.50.
Через 10 минут imbalance на том же market снова стал positive. Bot снова вошел по 0.42. На этот раз imbalance так и не восстановился; mid drifted до 0.18, и position дотянулась до resolution на 0.
Root cause: strategy трактовала imbalance как directional signal, но не отслеживала, что imbalance bouncing - оба signal были noise, а не information. Bot был whipsawed между двумя ложными signal на одном и том же market в течение 20 минут.
Fix: cooldown per market - после fill новые entries на том же market запрещены в течение 30 минут. Это позволяло multiple entries на разных markets, но не back-to-back на одном и том же.
Lesson: signal, который bounce'ится, - это не signal. Перед действием фильтруйте по persistence.
Premature go-live: wipe 2025 года
Новая market-making strategy прошла 12 paper trades. Builder не дождался 30, решил, что "все выглядит хорошо", и выкатил live с $500 капитала. Через 18 часов wallet был уже на $200.
Root cause: 12 trades - недостаточная выборка, чтобы отличить 60% WR от 35% WR. На самом деле strategy давала 35% WR; в 12-trade paper window просто попалась нерепрезентативная streak.
30-trade gate существует не просто так. Variance на sample из 12 trades делает его неотличимым от "strategy не работает".
Lesson: discipline beats conviction. 30-trade gate не обсуждается.
Sleep-through-bug: kill switch сработал
У bot'а был off-by-one в time-of-day filter - он должен был ставить pause в 02:00 UTC, а на деле ставил его в 03:00 UTC. В течение часа 02:00-03:00 без pause Polygon RPC сильно rate-limit'ил наши requests; read path bot'а возвращал stale data.
Bot продолжал торговать по stale prices. PnL за час: -$3.20 по 22 trades. Daily-loss kill switch сработал на -5%, остановил bot и отправил Telegram alert в 03:08 UTC. Builder проснулся и увидел halted bot в 09:00; total damage ограничился kill threshold.
Lesson: баг был real, но kill switch сработал. -$3.20 вместо -$50.00. Risk controls не предотвращают баги; они ограничивают cost тех багов, которых вы не увидели заранее.
Уроки, которые обобщаются
Во всех postmortem повторяются четыре pattern.
- API time ≠ chain time. Settlement lag, RPC lag, WebSocket lag - все это создает gaps, которые bot code должен явно обрабатывать.
- Retries need idempotence. Retry без client-order-id - это риск duplicate order. Всегда.
- Read every market property explicitly. NegRisk flag, tick size, expiration. Никогда не default'ите; всегда читайте из source of truth.
- Kill switch - это floor, а не feature. Risk controls ограничивают losses на bugs. Strategy не предотвращают bugs; они предполагают, что bot работает корректно. Bot не всегда будет работать корректно.
В каждой главе этой серии где-то встроен один из этих pattern. Это несущие principles production bot. Пропустите их - и вы снова найдете их в собственных postmortem.





