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.
- 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ć.
- Retry potrzebują idempotence. Retry bez client-order-id to ryzyko duplicate-order. Zawsze.
- 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.
- 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.











