Polymarket Bot Tutorial · Kapitel 32 von 32
Echte Polymarket-Bot-Fehler und Postmortems: phantom fills, sticky-fail dedup, lol-ctg-ccg whipsaw, NegRisk-Flag-Bug, verfrühter Go-Live - mit den Commits und Daten, die jeweils alles behoben haben.
Was dieses Kapitel abdeckt
Unser eigenes Produktions-Tagebuch mit Bugs, die echtes Geld gekostet haben. Das Muster ist wichtiger als die Details - dieselben Bug-Klassen tauchen in Bots immer wieder auf, und die Lösung ist meist ein fehlender Watchdog, nicht eine bessere Strategie. Dieses Kapitel soll dir die Lehrgelder ersparen.
- Phantom fills (Commits e68a087, 8bb7761)
- NegRisk-Flag-Bug (Commit 06deaef)
- Sticky-fail dedup (Commit 4c0bef1)
- Whipsaw-Vorfall: lol-ctg-ccg
- Verfrühter Go-Live: 2025 wipe
- Sleep-through-bug: kill switch funktionierte
- Lehren, die sich verallgemeinern lassen
Phantom fills (Commits e68a087, 8bb7761)
Der erste große Phantom-Fill-Vorfall bei unserem Trader, Mai 2025. Der Bot platzierte 22 FOK-Buys, alle wurden am CLOB gematcht. Der Bot versuchte sofort, 22 GTC-Sells zu posten. 8 davon wurden mit "balance: 0 / sum of active orders: 0 / order amount: 10000000." abgelehnt.
Ursache: Settlement-Lag (Kapitel 12). Der CLOB matchte in 100 ms, der Bot postete den Sell in 200 ms, aber der Polygon-ERC-1155-Transfer dauerte rund 2 Sekunden. Der CLOB lehnte den Sell ab, weil die Chain noch einen Kontostand von null anzeigte.
Fix: zwischen jedem erfolgreichen Buy und einem anschließenden GTC-Follow-up auf demselben Token eine blockierende Wartezeit von 5 Sekunden einfügen. Commits e68a087 und 8bb7761. Seitdem keine Phantom-Fill-Vorfälle mehr.
Lektion: API-Zeit und Chain-Zeit sind unterschiedliche Zeitachsen. Code, der annimmt, sie seien synchron, läuft genau in diesen Fehlerfall.
NegRisk-Flag-Bug (Commit 06deaef)
Ein NegRisk-Multi-Outcome-Event mit 8 Kandidaten hatte einen momentanen Arb von 1,8c (Summe der YES-asks = 0,982). Unser Arber feuerte alle 8 FOK-Buys ab. 6 davon wurden ausgeführt; 2 wurden im falschen Exchange-Contract abgewickelt.
Ursache: Der Bot rief createAndPostOrder auf, ohne negRisk: true im Flags-Objekt zu setzen. Zwei der Märkte hatten ein anderes historisches Erstellungsdatum und benötigten das Flag; sechs brauchten es nicht, weil ihr zugrunde liegender Contract standardmäßig bereits über NegRisk geroutet wurde.
Fix: market.negRisk für jeden Markt aus Gamma lesen und an jeden Order-Call durchreichen. Commit 06deaef. Wir führten den Arb mit gesetztem Flag erneut aus; die verbleibenden 2 wurden korrekt abgewickelt.
Lektion: Niemals eine Markt-Eigenschaft als Default annehmen. Lies sie jedes Mal explizit aus der Source of Truth.
Sticky-fail dedup (Commit 4c0bef1)
Der Bot wiederholte einen fehlgeschlagenen Buy 5 Mal in 12 Sekunden. Der erste Versuch war tatsächlich erfolgreich (ein Network Timeout führte dazu, dass der Bot die Antwort nicht sah); die nächsten 4 Retries erzeugten 4 zusätzliche Positionen. Insgesamt: 5 Positionen im selben Markt, obwohl wir 1 wollten.
Ursache: kein idempotenter client-order-id. Die Retry-Logik des Bots lautete: "Wenn es fehlgeschlagen ist, nochmal mit einem neuen Salt versuchen." Der CLOB hatte keine Möglichkeit, die Retries als Duplikate zu erkennen.
Fix: Vor dem ersten Versuch eine deterministische UUID pro beabsichtigtem Order generieren. Alle Retries verwenden dieselbe client-order-id, sodass der CLOB dedupen kann. Commit 4c0bef1.
Lektion: Retries ohne Idempotenz sind Duplikate. Jeder Order braucht eine stabile clientseitige Kennung.
Whipsaw-Vorfall: lol-ctg-ccg
Ein esports-Match (CTG gegen CCG) veranlasste den Bot, bei 0,45 einen Buy einzugehen, als der imbalance positiv wurde. Innerhalb von 30 Sekunden drehte der imbalance negativ, und unser GTC-Sell bei 0,50 wurde von jemandes Order ausgeführt. PnL: +5c × 10 Shares = +$0,50.
10 Minuten später drehte der imbalance desselben Marktes erneut positiv. Der Bot stieg wieder bei 0,42 ein. Diesmal erholte sich der imbalance nie; der mid driftete auf 0,18 und die Position lief bis zur Resolution auf 0 aus.
Ursache: Die Strategie behandelte imbalance als Richtungssignal, verfolgte aber nicht, dass der imbalance hin und her sprang - beide Signale waren Lärm, keine Information. Der Bot wurde innerhalb von 20 Minuten bei zwei fehlerhaften Signalen auf demselben Markt hin und her geschleudert.
Fix: Cooldown pro Markt - nach einem Fill gibt es 30 Minuten lang keine neuen Entries auf demselben Markt. Mehrere Entries über verschiedene Märkte hinweg waren erlaubt, aber nicht direkt hintereinander auf demselben.
Lektion: Ein Signal, das hin und her springt, ist kein Signal. Vor dem Handeln auf Persistenz filtern.
Verfrühter Go-Live: 2025 wipe
Eine neue Market-Making-Strategie bestand 12 Paper Trades. Der Builder wartete nicht auf 30, entschied "sieht gut aus", deployte live mit $500 Kapital. Innerhalb von 18 Stunden stand das Wallet bei $200.
Ursache: 12 Trades sind keine ausreichende Stichprobe, um 60% WR von 35% WR zu unterscheiden. Die Strategie lag tatsächlich bei 35% WR; das 12-Trade-Paper-Fenster hatte zufällig eine unrepräsentative Serie.
Das 30-Trade-Gate existiert nicht ohne Grund. Die Varianz einer 12-Trade-Stichprobe macht sie ununterscheidbar von "die Strategie funktioniert nicht".
Lektion: Disziplin schlägt Überzeugung. Das 30-Trade-Gate ist nicht verhandelbar.
Sleep-through-bug: kill switch funktionierte
Der Bot hatte einen Off-by-One-Fehler in seinem Time-of-Day-Filter - er sollte um 02:00 UTC pausieren, pausierte aber tatsächlich erst um 03:00 UTC. Während der unpausierten Stunde von 02:00 bis 03:00 UTC wurde Polygon RPC für unsere Requests stark rate-limited; der Read-Path des Bots lieferte veraltete Daten zurück.
Der Bot handelte weiter auf Basis veralteter Preise. PnL in dieser Stunde: -$3,20 über 22 Trades. Der Daily-Loss-Kill-Switch löste bei -5% aus, stoppte den Bot und sendete um 03:08 UTC eine Telegram-Alert. Der Builder wachte um 09:00 Uhr mit einem gestoppten Bot auf; der Gesamtschaden blieb auf das Kill-Threshold begrenzt.
Lektion: Der Bug war real, aber der kill switch funktionierte. -$3,20 statt -$50,00. Risk Controls verhindern keine Bugs; sie begrenzen die Kosten von Bugs, die du nicht kommen sahst.
Lehren, die sich verallgemeinern lassen
Über alle Postmortems hinweg wiederholen sich vier Muster.
- API-Zeit ≠ Chain-Zeit. Settlement-Lag, RPC-Lag, WebSocket-Lag - all das erzeugt Lücken, die Bot-Code explizit behandeln muss.
- Retries brauchen Idempotenz. Ein Retry ohne client-order-id ist immer ein Duplicate-Order-Risiko.
- Jede Markt-Eigenschaft explizit lesen. NegRisk-Flag, Tick Size, Expiration. Nie defaulten; immer aus der Source of Truth lesen.
- Der kill switch ist der Boden, kein Feature. Risk Controls begrenzen Verluste durch Bugs. Strategien verhindern keine Bugs; sie setzen voraus, dass der Bot korrekt funktioniert. Der Bot wird nicht immer korrekt funktionieren.
Jedes Kapitel in dieser Serie enthält irgendwo eines dieser Muster. Sie sind die tragenden Prinzipien eines Production-Bots. Überspring sie, und du findest sie in deinen eigenen Postmortems wieder.





