Polymarket Bot Tutorial · Rozdział 32 z 32

Prawdziwe błędy botów Polymarket i postmortemy: phantom fills, sticky-fail dedup, whipsaw lol-ctg-ccg, błąd flagi NegRisk, przedwczesny go-live - wraz z commitami i datami, które naprawiły każdy z nich.

Co obejmuje ten rozdział

Nasz własny dziennik produkcyjny błędów, które kosztowały prawdziwe pieniądze. Ważniejszy od szczegółów jest wzorzec — te same klasy błędów powracają w różnych botach, a lekarstwem zwykle nie jest lepsza strategia, tylko brakujący watchdog. Ten rozdział ma oszczędzić Ci czesne.

To jest rozdział 32 z naszej 32-częściowej serii o budowie Polymarket trading bota. Omawiamy temat dogłębnie w sekcjach poniżej. Treść dla każdej sekcji jest pisana i publikowana rozdział po rozdziale; odpowiedzi FAQ i referencje są już kompletne i odzwierciedlają doświadczenie produkcyjne z działania naszego własnego tradera.

  • Phantom fills (commity e68a087, 8bb7761)
  • Błąd flagi NegRisk (commit 06deaef)
  • Sticky-fail dedup (commit 4c0bef1)
  • Incydent whipsaw: lol-ctg-ccg
  • Przedwczesny go-live: wipe 2025
  • Przespanie-błędu: kill switch zadziałał
  • Wnioski, które da się uogólnić

Phantom fills (commity e68a087, 8bb7761)

Pierwszy duży incydent phantom fill w naszym traderze, maj 2025. Bot złożył 22 zakupy FOK, wszystkie zostały dopasowane na CLOB. Bot natychmiast spróbował wystawić 22 sprzedaże GTC. 8 z nich zostało odrzuconych z komunikatem "balance: 0 / sum of active orders: 0 / order amount: 10000000."

Przyczyna źródłowa: opóźnienie settlement (rozdział 12). CLOB dopasował transakcję w 100 ms, bot wystawił sell po 200 ms, ale transfer Polygon ERC-1155 trwał około 2 sekundy. CLOB odrzucił sprzedaż, ponieważ blockchain nadal pokazywał zerowy balance.

Naprawa: wstaw 5-sekundowe blokujące oczekiwanie między każdym udanym buy a kolejnym GTC na tym samym tokenie. Commity e68a087 i 8bb7761. Od tego czasu zero incydentów phantom fill.

Wniosek: czas API i czas chain to różne osie czasu. Kod, który zakłada, że są synchroniczne, trafi dokładnie w ten tryb awarii.

Błąd flagi NegRisk (commit 06deaef)

Wydarzenie NegRisk z wieloma outcome'ami i 8 kandydatami miało chwilowy arb na poziomie 1.8c (suma YES asks = 0.982). Nasz arber odpalił wszystkie 8 zakupów FOK. 6 z nich zostało zrealizowanych; 2 rozliczyły się do niewłaściwego exchange contract.

Przyczyna źródłowa: bot wywoływał createAndPostOrder bez ustawienia negRisk: true w obiekcie flags. Dwa z marketów miały inną historyczną datę utworzenia i wymagały tej flagi; sześć jej nie potrzebowało, bo ich underlying contract i tak routował przez NegRisk domyślnie.

Naprawa: odczytuj market.negRisk z Gamma dla każdego marketu i przekazuj dalej do każdego wywołania order. Commit 06deaef. Powtórzyliśmy arb z ustawioną flagą; pozostałe 2 rozliczyły się poprawnie.

Wniosek: nigdy nie ustawiaj domyślnej wartości dla właściwości marketu. Zawsze odczytuj ją jawnie ze źródła prawdy.

Sticky-fail dedup (commit 4c0bef1)

Bot ponowił nieudanego buy 5 razy w 12 sekund. Pierwsza próba faktycznie się powiodła (timeout sieci sprawił, że bot nie zobaczył odpowiedzi); kolejne 4 retry utworzyły 4 dodatkowe pozycje. Razem: 5 pozycji na tym samym market, kiedy chcieliśmy 1.

Przyczyna źródłowa: brak idempotentnego client-order-id. Logika retry w bocie brzmiała: "jeśli się nie udało, spróbuj ponownie z nowym saltem". CLOB nie miał żadnego sposobu, by rozpoznać te retry jako duplikaty.

Naprawa: generuj deterministyczny UUID dla każdego zamierzonego orderu przed pierwszą próbą. Wszystkie retry używają tego samego client-order-id, co pozwala CLOB-owi na dedup. Commit 4c0bef1.

Wniosek: retry bez idempotence to duplikaty. Każdy order potrzebuje stabilnego identyfikatora po stronie klienta.

Incydent whipsaw: lol-ctg-ccg

Mecz esportowy (CTG vs CCG) sprawił, że bot wszedł w buy po 0.45, gdy imbalance przeszedł na dodatni. W ciągu 30 sekund imbalance przeszedł na ujemny i nasz GTC sell po 0.50 został trafiony przez cudze zlecenie. PnL: +5c × 10 shares = +$0.50.

10 minut później imbalance tego samego market ponownie przeszedł na dodatni. Bot wszedł jeszcze raz po 0.42. Tym razem imbalance już się nie odbił; mid zjechał do 0.18, a pozycja została dowieziona do rozliczenia przy 0.

Przyczyna źródłowa: strategia traktowała imbalance jako sygnał kierunkowy, ale nie śledziła tego, że imbalance skakał — oba sygnały były szumem, nie informacją. Bot został whipsawowany przez dwa fałszywe sygnały na tym samym market w ciągu 20 minut.

Naprawa: cooldown per market — po fillu brak nowych wejść na tym samym market przez 30 minut. Pozwoliło to na wiele wejść na różnych marketach, ale nie jedno po drugim na tym samym.

Wniosek: sygnał, który się odbija, nie jest sygnałem. Filtruj trwałość przed działaniem.

Przedwczesny go-live: wipe 2025

Nowa strategia market making przeszła 12 paper trades. Twórca nie poczekał na 30, uznał, że "wygląda dobrze", wdrożył live z kapitałem $500. W ciągu 18 godzin wallet spadł do $200.

Przyczyna źródłowa: 12 trades to za mała próbka, by odróżnić 60% WR od 35% WR. Strategia miała w rzeczywistości 35% WR; 12-trade paper window po prostu trafiło na niereprezentatywną serię.

30-trade gate istnieje nie bez powodu. Zmienność w próbce 12 transakcji sprawia, że jest ona nieodróżnialna od "strategia nie działa".

Wniosek: dyscyplina wygrywa z przekonaniem. 30-trade gate nie podlega negocjacjom.

Przespanie-błędu: kill switch zadziałał

Bot miał off-by-one w filtrze pory dnia — miał pauzować o 02:00 UTC, ale faktycznie pauzował o 03:00 UTC. W godzinie 02:00-03:00, gdy nie był wstrzymany, Polygon RPC mocno rate-limitował nasze requesty; ścieżka odczytu w bocie zwracała nieaktualne dane.

Bot dalej handlował na starych cenach. PnL w tej godzinie: -$3.20 na 22 trades. Kill switch dziennej straty zadziałał przy -5%, zatrzymał bota, wysłał alert na Telegram o 03:08 UTC. Twórca obudził się do zatrzymanego bota o 09:00, a całkowite szkody ograniczyły się do progu kill switcha.

Wniosek: błąd był realny, ale kill switch zadziałał. -$3.20 zamiast -$50.00. Kontrole ryzyka nie zapobiegają błędom; ograniczają koszt błędów, których nie przewidziałeś.

Wnioski, które da się uogólnić

We wszystkich postmortemach powtarzają się cztery wzorce.

  1. Czas API ≠ czas chain. Opóźnienie settlement, opóźnienie RPC, opóźnienie WebSocket — wszystkie wprowadzają luki, które kod bota musi jawnie obsłużyć.
  2. Retry potrzebują idempotence. Retry bez client-order-id to ryzyko duplicate-order. Zawsze.
  3. Odczytuj każdą właściwość marketu jawnie. Flaga NegRisk, tick size, expiration. Nigdy nie ustawiaj wartości domyślnej; zawsze czytaj ze źródła prawdy.
  4. Kill switch to podłoga, nie funkcja. Kontrole ryzyka ograniczają straty przy błędach. Strategie nie zapobiegają błędom; zakładają, że bot działa poprawnie. Bot nie zawsze będzie działał poprawnie.

Każdy rozdział w tej serii ma gdzieś w sobie jeden z tych wzorców. To podstawowe zasady produkcyjnego bota. Pomiń je, a znajdziesz je ponownie we własnych postmortemach.

Najczęściej zadawane pytania

Jaki jest najdroższy błąd bota Polymarket?
Wejście na live zanim paper-trading osiągnie gate 30 trades. Zrobiliśmy to. Błąd to nie tylko utrata pieniędzy - to także utrata szansy na naukę strategii w kontrolowanym środowisku. Boty, które idą live zbyt wcześnie, albo zostają zniszczone i porzucone, albo miesiącami odrabiają straty, zanim wrócą do paper-trading.
Czym jest błąd phantom fill?
Sytuacja, w której bot wierzy, że order został zrealizowany, ale exchange zapisał go jako jeszcze niezrealizowany. Objawy: pozycja pojawia się w stanie bota, ale nie on-chain, co prowadzi do podwójnych zleceń przy retry. Naprawione w naszym traderze przez trzy commity (e68a087, 8bb7761, 06deaef): używaj FOK dla buy, odpytywania statusu aż do match, nigdy nie ufaj status=delayed jako filled.
Czym był incydent whipsaw lol-ctg-ccg?
Market esportowy na cienkim order book, gdzie nasz trader uruchomił stop-loss na -$2.55 przy 0.14, a potem patrzył, jak cena wraca do 0.325 w ciągu 2 minut. Skonfigurowaliśmy stop-loss na -4 punktów procentowych, co jest zbyt ciasne dla cienkich esportowych booków. Naprawa: poszerzyliśmy SL do -8 pp dla marketów o niskiej płynności, a ciaśniejszy SL zostawiliśmy tylko dla grubych booków (NBA, piłka nożna o wysokiej płynności). Zobacz memory/trader-sl-wider.md.
Jak objawił się błąd flagi NegRisk?
Bot składał ordery bez ustawienia neg_risk=true na marketach wielooutcome. Ordery były odrzucane z mylącymi komunikatami, co prowadziło do kilkusekundowych opóźnień przed retry, a te do pominiętych filli. Naprawa w commit 06deaef: zawsze ustawiaj neg_risk zgodnie z metadata marketu, nigdy nie zakładaj.
Na czym polegał incydent sleep-through-bug?
Wallet utknął z zablokowanym orderem o 4 rano. Właściciel kazał botowi się zatrzymać; dotknął pliku data/halt_autobuy. Bot wykrył ten plik przed następną próbą złożenia orderu i odmówił jego wystawienia. Właściciel obudził się do czystego stanu zamiast gorszego. Potwierdziło to wzorzec halt-sentinel; teraz domyślnie dostarczamy go w każdym bocie.
Jaka jest pojedyncza najbardziej uniwersalna lekcja z tych postmortemów?
Nigdy nie ufaj happy path. Każdy błąd, który wypuściliśmy, wynikał z założenia, że request się powiódł, fill był prawdziwy albo cena się nie ruszy. Pisz defensywnie: zakładaj, że ordery mogą się nie udać, reconciliation może się rozjechać, a jeden market zaraz zrobi coś dziwnego. Podatek od paranoi jest mały; koszt jego pominięcia to postmortem, który napiszesz później.