آموزش Polymarket Bot · فصل 32 از 32
اشتباهات واقعی Polymarket bot و postmortemها: phantom fills، sticky-fail dedup، whipsaw در lol-ctg-ccg، باگ NegRisk flag، go-live زودهنگام - همراه با commitها و تاریخهایی که هرکدام را برطرف کردند.
این فصل چه چیزهایی را پوشش میدهد
دفترچه تولیدی خودمان از bugهایی که پول واقعی از بین بردند. الگوها از جزئیات مهمترند - همان دستههای bug در botهای مختلف تکرار میشوند، و درمان معمولاً یک watchdogِ جاافتاده است، نه یک strategy بهتر. هدف این فصل این است که شهریه شما را پس بگیرد.
- Phantom fills (commitهای e68a087، 8bb7761)
- NegRisk flag bug (commit 06deaef)
- Sticky-fail dedup (commit 4c0bef1)
- Whipsaw incident: lol-ctg-ccg
- Premature go-live: 2025 wipe
- Sleep-through-bug: kill switch کار کرد
- درسهایی که قابل تعمیماند
Phantom fills (commitهای e68a087، 8bb7761)
اولین incident بزرگ phantom-fill در trader ما، مه 2025. Bot 22 خرید FOK ثبت کرد که همگی در CLOB matched شدند. Bot بلافاصله تلاش کرد 22 فروش GTC ثبت کند. 8 مورد با پیام "balance: 0 / sum of active orders: 0 / order amount: 10000000." رد شدند.
علت اصلی: settlement lag (فصل 12). CLOB در 100ms match شد، bot فروش را در 200ms ثبت کرد، اما انتقال Polygon ERC-1155 حدود 2 ثانیه طول کشید. CLOB فروش را رد کرد چون chain هنوز balance صفر را نشان میداد.
رفع: بین هر خرید موفق و هر follow-up از نوع GTC روی همان token، یک انتظار blocking پنجثانیهای اضافه کنید. commitهای e68a087 و 8bb7761. از آن زمان هیچ incident phantom-fill نداشتهایم.
درس: زمان API و زمان chain دو timeline متفاوت هستند. کدی که فرض کند همزماناند، دقیقاً با همین failure mode روبهرو میشود.
NegRisk flag bug (commit 06deaef)
یک event چند-نتیجهای NegRisk با 8 candidate یک arb موقت 1.8c داشت (جمع YES askها = 0.982). arber ما هر 8 خرید FOK را اجرا کرد. 6 تای آنها filled شدند؛ 2 تای دیگر در exchange contract اشتباهی settled شدند.
علت اصلی: bot تابع createAndPostOrder را بدون تنظیم negRisk: true در object فلگها صدا میزد. دو تا از بازارها تاریخ ایجاد تاریخی متفاوتی داشتند و به این flag نیاز داشتند؛ شش مورد دیگر نیازی نداشتند چون contract زیرین آنها از قبل بهصورت پیشفرض از طریق NegRisk routing میشد.
رفع: در Gamma برای هر market، market.negRisk را بخوانید و به هر order call منتقل کنید. commit 06deaef. ما arb را با flag فعال دوباره اجرا کردیم؛ 2 مورد باقیمانده درست settled شدند.
درس: هیچ property مربوط به market را بهصورت پیشفرض فرض نکنید. هر بار آن را صریحاً از source of truth بخوانید.
Sticky-fail dedup (commit 4c0bef1)
Bot یک خرید ناموفق را در 12 ثانیه 5 بار retry کرد. تلاش اول در واقع موفق شده بود (timeout شبکه باعث شده بود bot پاسخ را نبیند)؛ 4 retry بعدی 4 position اضافی ایجاد کردند. مجموع: 5 position در همان market وقتی فقط 1 تا میخواستیم.
علت اصلی: نبود client-order-id اتمیک. منطق retry در bot این بود: "اگر fail شد، با یک salt جدید دوباره امتحان کن." CLOB هیچ راهی برای تشخیص retryها بهعنوان duplicate نداشت.
رفع: قبل از اولین تلاش، یک UUID deterministic برای هر order موردنظر تولید کنید. همه retryها از همان client-order-id استفاده میکنند و به CLOB اجازه میدهند duplicateها را dedup کند. commit 4c0bef1.
درس: retry بدون idempotence یعنی duplicate. هر order به یک شناسه client-side پایدار نیاز دارد.
Whipsaw incident: lol-ctg-ccg
یک match در esports (CTG در برابر CCG) باعث شد bot وقتی imbalance مثبت شد، در 0.45 وارد buy شود. ظرف 30 ثانیه، imbalance منفی شد و فروش GTC ما در 0.50 توسط order شخص دیگری hit شد. PnL: +5c × 10 shares = +$0.50.
10 دقیقه بعد، imbalance همان market دوباره مثبت شد. Bot دوباره در 0.42 وارد شد. این بار imbalance هیچوقت برنگشت؛ mid تا 0.18 drift کرد و position تا resolution در 0 ماند.
علت اصلی: strategy از imbalance بهعنوان signal جهتدار استفاده میکرد، اما دنبال نمیکرد که imbalance دارد bounce میزند - هر دو signal فقط noise بودند، نه information. Bot در عرض 20 دقیقه روی دو signal ناموفق در همان market whipsaw شد.
رفع: cooldown برای هر market - بعد از هر fill، تا 30 دقیقه هیچ entry جدیدی روی همان market نباشد. این کار اجازه میداد روی marketهای مختلف چندین entry انجام شود، اما نه پشتسرهم روی همان یکی.
درس: سیگنالی که bounce میزند، signal نیست. قبل از اقدام، persistence را فیلتر کنید.
Premature go-live: 2025 wipe
یک strategy جدید market-making از 12 paper trade عبور کرد. سازنده منتظر 30 مورد نماند، گفت "خوب به نظر میرسد"، و با $500 سرمایه بهصورت live deploy کرد. ظرف 18 ساعت wallet به $200 رسید.
علت اصلی: 12 trade برای تشخیص WR 60% از WR 35% sample کافی نیست. آن strategy در واقع WR 35% داشت؛ بازه 12-trade در paper اتفاقاً یک streak غیرنماینده داشت.
دروازه 30-trade بیدلیل وجود ندارد. variance روی یک sample 12-trade آن را از "این strategy کار نمیکند" غیرقابلتشخیص میکند.
درس: انضباط از conviction مهمتر است. دروازه 30-trade قابل مذاکره نیست.
Sleep-through-bug: kill switch کار کرد
Bot در time-of-day filter خود یک off-by-one داشت - قرار بود ساعت 02:00 UTC pause کند، اما در واقع در 03:00 UTC pause میکرد. در ساعت 02:00 تا 03:00 که pause نشده بود، Polygon RPC درخواستهای ما را بهشدت rate-limit میکرد؛ مسیر read در bot داده stale برمیگرداند.
Bot با قیمتهای stale به معامله ادامه داد. PnL آن ساعت: -$3.20 در 22 trade. kill switch مربوط به daily-loss در -5% فعال شد، bot را متوقف کرد و در 03:08 UTC یک هشدار Telegram فرستاد. سازنده ساعت 09:00 با یک bot متوقفشده از خواب بیدار شد؛ خسارت کل فقط تا آستانه kill محدود شد.
درس: bug واقعی بود، اما kill switch کار کرد. بهجای -$50.00 فقط -$3.20. کنترلهای risk مانع bug نمیشوند؛ هزینه bugهایی را که ندیدهاید محدود میکنند.
درسهایی که قابل تعمیماند
در همه postmortemها، چهار الگو تکرار میشوند.
- زمان API ≠ زمان chain. settlement lag، RPC lag، WebSocket lag - همه شکافهایی ایجاد میکنند که کد bot باید صریحاً با آنها برخورد کند.
- retryها به idempotence نیاز دارند. retry بدون client-order-id یعنی ریسک duplicate order. همیشه.
- همه propertyهای market را صریح بخوانید. NegRisk flag، tick size، expiration. هرگز default نکنید؛ همیشه از source of truth بخوانید.
- kill switch کفِ خسارت است، نه یک feature. کنترلهای risk خسارت bugها را محدود میکنند. strategyها bug را از بین نمیبرند؛ فرض میکنند bot درست کار میکند. bot همیشه درست کار نخواهد کرد.
هر فصل در این مجموعه جایی یکی از این الگوها را در خود دارد. اینها اصول load-bearing یک bot production هستند. از آنها عبور کنید و دوباره در postmortemهای خودتان پیدایشان میکنید.





