מדריך בוט Polymarket · פרק 32 מתוך 32
טעויות אמיתיות של בוט Polymarket וניתוחי כשל שלאחר מעשה: phantom fills, sticky-fail dedup, whipsaw של lol-ctg-ccg, באג דגל NegRisk, עלייה מוקדמת ל-live - עם ה-commits והתאריכים שתיקנו כל אחד מהם.
מה מכסה הפרק הזה
יומן הייצור שלנו עצמו של באגים שגבו כסף אמיתי. הדפוס חשוב יותר מהפרטים - אותן משפחות של באגים חוזרות בבוטים, והתרופה היא בדרך כלל watchdog חסר, לא אסטרטגיה טובה יותר. הפרק הזה נועד לחסוך לכם את שכר הלימוד.
- Phantom fills (commits e68a087, 8bb7761)
- באג דגל NegRisk (commit 06deaef)
- Sticky-fail dedup (commit 4c0bef1)
- אירוע Whipsaw: lol-ctg-ccg
- עלייה מוקדמת ל-live: wipe ב-2025
- Sleep-through-bug: ה-kill switch עבד
- לקחים שמוכללים
Phantom fills (commits e68a087, 8bb7761)
אירוע ה-phantom-fill הגדול הראשון אצל הטריידר שלנו, מאי 2025. הבוט ביצע 22 קניות FOK, וכולן הותאמו ב-CLOB. מיד לאחר מכן הבוט ניסה לפרסם 22 מכירות GTC. 8 מהן נדחו עם "balance: 0 / sum of active orders: 0 / order amount: 10000000."
סיבת השורש: השהיית settlement (פרק 12). ה-CLOB התאים בתוך 100ms, הבוט פרסם את המכירה אחרי 200ms, אבל העברת ה-ERC-1155 ב-Polygon לקחה כ-2 שניות. ה-CLOB דחה את המכירה כי ה-chain עדיין הראתה יתרה אפס.
תיקון: להכניס המתנה חוסמת של 5 שניות בין כל קנייה מוצלחת לבין כל follow-up מסוג GTC על אותו טוקן. commits e68a087 ו-8bb7761. מאז לא היו אירועי phantom-fill.
לקח: זמן של API וזמן של chain הם צירי זמן שונים. קוד שמניח שהם סינכרוניים ייתקל בדיוק במצב הכשל הזה.
באג דגל NegRisk (commit 06deaef)
אירוע NegRisk רב-תוצאתי עם 8 מועמדים כלל ארב רגעי של 1.8c (סכום מחירי ה-YES ask = 0.982). ה-arber שלנו הפעיל 8 קניות FOK. 6 מהן מולאו; 2 נסלקו לתוך חוזה ה-exchange הלא נכון.
סיבת השורש: הבוט קרא ל-createAndPostOrder בלי להגדיר negRisk: true באובייקט הדגלים. לשניים מהשווקים היה תאריך יצירה היסטורי שונה והם דרשו את הדגל; שישה לא נזקקו לו כי החוזה הבסיסי שלהם כבר נותב דרך NegRisk כברירת מחדל.
תיקון: לקרוא את market.negRisk מ-Gamma עבור כל שוק, ולהעביר אותו לכל קריאת order. commit 06deaef. הרצנו שוב את ה-arb כשהדגל מוגדר; 2 הנותרים נסלקו כראוי.
לקח: לעולם אל תניחו ברירת מחדל על מאפיין של שוק. קראו אותו במפורש ממקור האמת בכל פעם.
Sticky-fail dedup (commit 4c0bef1)
הבוט ניסה שוב קנייה שנכשלה 5 פעמים בתוך 12 שניות. הניסיון הראשון למעשה הצליח (timeout ברשת גרם לבוט לא לראות את התשובה); 4 הניסיונות החוזרים הבאים יצרו 4 פוזיציות נוספות. סה"כ: 5 פוזיציות על אותו שוק כשביקשנו 1.
סיבת השורש: לא היה client-order-id ייחודי ואידמפוטנטי. הלוגיקה של ה-retry בבוט הייתה "אם זה נכשל, נסה שוב עם salt חדש." ל-CLOB לא הייתה דרך לזהות את ה-retries כ-duplicates.
תיקון: לייצר UUID דטרמיניסטי לכל order מתוכנן לפני הניסיון הראשון. כל ה-retries משתמשים באותו client-order-id, מה שמאפשר ל-CLOB לבצע dedup. commit 4c0bef1.
לקח: retries בלי idempotence הם duplicates. כל order צריך מזהה יציב בצד הלקוח.
אירוע Whipsaw: lol-ctg-ccg
משחק esports (CTG מול CCG) גרם לבוט להיכנס לקנייה ב-0.45 כשה-imbalance התהפך לחיובי. בתוך 30 שניות ה-imbalance התהפך לשלילי, ופקודת המכירה GTC שלנו ב-0.50 ננשכה על ידי order של מישהו אחר. PnL: +5c × 10 shares = +$0.50.
10 דקות לאחר מכן, ה-imbalance של אותו שוק התהפך שוב לחיובי. הבוט נכנס שוב ב-0.42. הפעם ה-imbalance לא התאושש; ה-mid drifted ל-0.18 והפוזיציה רצה עד לסיום ב-0.
סיבת השורש: האסטרטגיה התייחסה ל-imbalance כאות כיווני, אבל לא עקבה אחרי זה שה-imbalance קפץ הלוך ושוב - שני האותות היו רעש, לא מידע. הבוט נחבט הלוך-ושוב בין שני אותות כושלים באותו שוק בתוך 20 דקות.
תיקון: cooldown לכל שוק - אחרי fill, אין כניסות חדשות לאותו שוק במשך 30 דקות. זה איפשר כמה כניסות בשווקים שונים, אבל לא רצף של back-to-back על אותו שוק.
לקח: אות שקופץ הלוך ושוב הוא לא אות. סננו לפי persistence לפני פעולה.
עלייה מוקדמת ל-live: wipe ב-2025
אסטרטגיית market-making חדשה עברה 12 paper trades. הבונה לא חיכה ל-30, החליט ש"נראה טוב", ופרס live עם הון של $500. בתוך 18 שעות ה-wallet ירדה ל-$200.
סיבת השורש: 12 trades אינם מדגם מספיק כדי להבחין בין WR של 60% ל-WR של 35%. בפועל, לאסטרטגיה היה WR של 35%; חלון ה-paper של 12 trades פשוט נפל על רצף לא מייצג.
שער ה-30 trades קיים מסיבה טובה. ה-variance במדגם של 12 trades הופך אותו לבלתי ניתן להבחנה מ"האסטרטגיה לא עובדת".
לקח: משמעת מנצחת ביטחון עצמי. שער ה-30 trades אינו נתון למו"מ.
Sleep-through-bug: ה-kill switch עבד
לבוט היה off-by-one במסנן של שעת היום שלו - הוא נועד להשהות ב-02:00 UTC, אבל בפועל השהה ב-03:00 UTC. במהלך השעה 02:00-03:00 שבה לא הייתה השהיה, Polygon RPC הגביל את הבקשות שלנו בצורה כבדה; מסלול הקריאה של הבוט החזיר נתונים מיושנים.
הבוט המשיך לסחור על מחירים מיושנים. PnL לשעה: -$3.20 לאורך 22 trades. ה-kill switch של הפסד יומי הופעל ב--5%, עצר את הבוט, ושלח התראת Telegram ב-03:08 UTC. הבונה התעורר לבוט עצור ב-09:00, והנזק הכולל הוגבל לסף ה-kill.
לקח: הבאג היה אמיתי אבל ה-kill switch עבד. -$3.20 במקום -$50.00. בקרות הסיכון לא מונעות באגים; הן מגבילות את העלות של באגים שלא ראיתם מגיעים.
לקחים שמוכללים
בכל ניתוחי הכשל, ארבעה דפוסים חוזרים.
- API time ≠ chain time. השהיית settlement, השהיית RPC, השהיית WebSocket - כולן יוצרות פערים שקוד הבוט חייב לטפל בהם במפורש.
- Retries צריכים idempotence. retry בלי client-order-id הוא סיכון של duplicate orders. תמיד.
- קראו כל מאפיין של שוק במפורש. דגל NegRisk, גודל tick, תפוגה. לעולם לא ברירת מחדל; תמיד לקרוא ממקור האמת.
- ה-kill switch הוא הרצפה, לא פיצ'ר. בקרות סיכון מגבילות הפסדים על באגים. אסטרטגיות לא מונעות באגים; הן מניחות שהבוט עובד נכון. הבוט לא תמיד יעבוד נכון.
לכל פרק בסדרה הזו יש אחד מהדפוסים האלה מוטמע איפשהו. אלה עקרונות נושאי-עומס של בוט production. דלגו עליהם ותמצאו אותם שוב בניתוחי הכשל שלכם.










