Polymarket Bot Tutorial · الفصل 32 من 32

أخطاء حقيقية في Polymarket bot وتقارير ما بعد الحادث: phantom fills، وsticky-fail dedup، وlol-ctg-ccg whipsaw، وخطأ NegRisk flag، والإطلاق المبكر - مع commits والتواريخ التي أصلحت كل مشكلة.

ما الذي يغطيه هذا الفصل

يومياتنا الإنتاجية الخاصة بالأخطاء التي كلّفتنا أموالًا حقيقية. النمط أهم من التفاصيل - فالفئات نفسها من الأخطاء تتكرر عبر bots، والعلاج غالبًا يكون watchdog مفقودًا، لا استراتيجية أفضل. الهدف من هذا الفصل أن يوفر عليك الرسوم الدراسية.

  • Phantom fills (commits e68a087, 8bb7761)
  • NegRisk flag bug (commit 06deaef)
  • Sticky-fail dedup (commit 4c0bef1)
  • حادثة whipsaw: lol-ctg-ccg
  • الإطلاق المبكر: wipe 2025
  • الاستغراق في النوم فوق bug: kill switch عمل
  • دروس قابلة للتعميم

Phantom fills (commits e68a087, 8bb7761)

أول حادثة كبيرة لـ phantom fills في trader الخاص بنا، في مايو 2025. وضع bot عدد 22 من أوامر الشراء من نوع FOK، وتمت مطابقتها كلها على CLOB. بعد ذلك مباشرة حاول bot إرسال 22 أمر بيع من نوع GTC. تم رفض 8 منها برسالة "balance: 0 / sum of active orders: 0 / order amount: 10000000."

السبب الجذري: تأخر التسوية (الفصل 12). تمّت مطابقة CLOB خلال 100ms، وأرسل bot أمر البيع بعد 200ms، لكن نقل Polygon ERC-1155 استغرق نحو ثانيتين. رفض CLOB أمر البيع لأن السلسلة ما زالت تُظهر رصيدًا صفريًا.

الإصلاح: إدخال انتظار حاجز لمدة 5 ثوانٍ بين أي شراء ناجح وأي متابعة من نوع GTC على الرمز نفسه. commits e68a087 و8bb7761. لا توجد أي حوادث phantom fills منذ ذلك الحين.

الدرس: وقت API ووقت السلسلة ليسا نفس الجدول الزمني. الكود الذي يفترض أنهما متزامنان سيصطدم بهذا نمط الفشل تحديدًا.

NegRisk flag bug (commit 06deaef)

كان هناك حدث متعدد النتائج من نوع NegRisk يضم 8 مرشحين، وظهرت فيه فرصة arb لحظية بمقدار 1.8c (مجموع أسعار YES ask = 0.982). أطلق arber لدينا جميع أوامر الشراء الثمانية من نوع FOK. تم تنفيذ 6 منها؛ بينما استقرّ 2 في عقد exchange خاطئ.

السبب الجذري: كان bot يستدعي createAndPostOrder دون تعيين negRisk: true داخل كائن flags. كان لدى اثنين من الأسواق تاريخ إنشاء مختلف ويتطلبان هذا flag؛ أما الأسواق الستة الأخرى فلم تكن بحاجة إليه لأن العقد الأساسي لديها كان يمر عبر NegRisk افتراضيًا أصلًا.

الإصلاح: قراءة market.negRisk من Gamma لكل market، وتمريره إلى كل استدعاء أوامر. commit 06deaef. أعدنا تشغيل arb مع تعيين flag؛ وتمت تسوية السوقين المتبقيين بشكل صحيح.

الدرس: لا تجعل خاصية السوق افتراضية أبدًا. اقرأها صراحةً من مصدر الحقيقة في كل مرة.

Sticky-fail dedup (commit 4c0bef1)

أعاد bot محاولة شراء فاشلة 5 مرات خلال 12 ثانية. المحاولة الأولى نجحت فعليًا (انقطاع الشبكة المؤقت جعل bot لا يرى الاستجابة)؛ أما المحاولات الأربع التالية فقد أنشأت 4 مراكز إضافية. المحصلة: 5 مراكز في السوق نفسه بينما كنا نريد مركزًا واحدًا.

السبب الجذري: عدم وجود client-order-id قابل للتكرار. كانت منطقية إعادة المحاولة لدى bot تقول: "إذا فشلت، جرّب مرة أخرى مع salt جديد." لم يكن لدى CLOB أي طريقة للتعرف على المحاولات المتكررة باعتبارها نسخًا مكررة.

الإصلاح: توليد UUID حتمي لكل order مقصود قبل المحاولة الأولى. جميع محاولات الإعادة تستخدم client-order-id نفسه، ما يسمح لـ CLOB بإجراء dedup. commit 4c0bef1.

الدرس: إعادة المحاولة دون idempotence تعني أوامر مكررة. كل order يحتاج إلى معرّف ثابت على جانب العميل.

حادثة whipsaw: lol-ctg-ccg

مباراة esports (CTG vs CCG) دفعت bot إلى الدخول في شراء عند 0.45 عندما أصبح imbalance إيجابيًا. خلال 30 ثانية، انقلب imbalance إلى سلبي، وتم ضرب أمر البيع GTC عند 0.50 بواسطة order من طرف آخر. PnL: +5c × 10 shares = +$0.50.

بعد 10 دقائق، انقلب imbalance في السوق نفسه إلى إيجابي مرة أخرى. دخل bot مرة أخرى عند 0.42. هذه المرة لم يتعافَ imbalance أبدًا؛ وانجرف mid إلى 0.18، ثم سارت الصفقة إلى التسوية النهائية عند 0.

السبب الجذري: كانت الاستراتيجية تتعامل مع imbalance كإشارة اتجاهية لكنها لم تتابع أن imbalance كان يتأرجح - كانت الإشارتان كلتاهما ضجيجًا، لا معلومات. تعرّض bot لـ whipsaw عبر إشارتين فاشلتين في السوق نفسه خلال 20 دقيقة.

الإصلاح: cooldown لكل market - بعد أي fill، لا توجد أي دخولات جديدة في السوق نفسه لمدة 30 دقيقة. سُمح بعدة دخولات عبر أسواق مختلفة، لكن ليس بشكل متتالٍ في السوق نفسه.

الدرس: الإشارة التي تتأرجح ليست إشارة. صفِّها من أجل الاستمرارية قبل التصرف.

الإطلاق المبكر: wipe 2025

استراتيجية market-making جديدة اجتازت 12 صفقة ورقية. لم ينتظر builder حتى 30، وقرر أن "الوضع يبدو جيدًا"، ثم نشرها مباشرة برأس مال قدره $500. خلال 18 ساعة أصبح المحفظة عند $200.

السبب الجذري: 12 صفقة ليست عينة كافية للتمييز بين WR بنسبة 60% وWR بنسبة 35%. كانت الاستراتيجية في الواقع عند WR بنسبة 35%؛ وكانت نافذة الصفقات الورقية الـ 12 مجرد سلسلة غير ممثلة للواقع.

بوابة الـ 30 صفقة موجودة لسبب. التباين في عينة من 12 صفقة يجعلها غير قابلة للتمييز عن "الاستراتيجية لا تعمل".

الدرس: الانضباط يتفوق على الاقتناع. بوابة الـ 30 صفقة ليست قابلة للتفاوض.

Sleep-through-bug: kill switch عمل

كان لدى bot خطأ off-by-one في فلتر وقت اليوم - كان من المفترض أن يتوقف عند 02:00 UTC، لكنه كان يتوقف فعليًا عند 03:00 UTC. خلال الساعة غير المتوقفة 02:00-03:00، كان Polygon RPC يفرض rate-limiting شديدًا على طلباتنا؛ وكانت مسار القراءة لدى bot يعيد بيانات قديمة.

واصل bot التداول بناءً على أسعار قديمة. PnL خلال الساعة: -$3.20 عبر 22 صفقة. تم تشغيل kill switch الخاص بالخسارة اليومية عند -5%، وأوقف bot، وأرسل تنبيه Telegram عند 03:08 UTC. استيقظ builder على bot متوقف عند 09:00، وكان إجمالي الضرر محدودًا بحدّ الـ kill.

الدرس: كان bug حقيقيًا لكن kill switch عمل. -$3.20 بدلًا من -$50.00. ضوابط المخاطر لا تمنع الأخطاء؛ إنها تحدّ من تكلفة الأخطاء التي لم تتوقعها.

دروس قابلة للتعميم

عبر جميع تقارير ما بعد الحوادث، تتكرر أربعة أنماط.

  1. API time ≠ chain time. تأخر التسوية، وتأخر RPC، وتأخر WebSocket - كلها تُدخل فجوات يجب على كود bot التعامل معها صراحةً.
  2. الإعادة تحتاج إلى idempotence. أي retry بدون client-order-id هو خطر أوامر مكررة. دائمًا.
  3. اقرأ كل خاصية للسوق بشكل صريح. NegRisk flag، وحجم tick، والانتهاء. لا تجعل شيئًا افتراضيًا أبدًا؛ اقرأ دائمًا من مصدر الحقيقة.
  4. kill switch هو الحد الأدنى، وليس ميزة. ضوابط المخاطر تحدّ من الخسائر الناتجة عن الأخطاء. الاستراتيجيات لا تمنع الأخطاء؛ إنها تفترض أن bot يعمل بشكل صحيح. ولن يعمل bot بشكل صحيح دائمًا.

كل فصل في هذه السلسلة يحتوي في مكان ما على واحد من هذه الأنماط. إنها المبادئ الحاملة لbot إنتاجي. تجاهلها وستجدها مرة أخرى في تقارير ما بعد الحوادث الخاصة بك.

الأسئلة الشائعة

ما هو أغلى خطأ يمكن أن يرتكبه Polymarket bot؟
الإطلاق المبكر قبل أن تصل paper-trading إلى بوابة الـ 30 صفقة. لقد فعلنا ذلك. الخطأ ليس مجرد خسارة المال - بل خسارة فرصة التعلّم من الاستراتيجية في بيئة مضبوطة. bots التي تُطلق مبكرًا جدًا إما تتعرض للإبادة ويُتخلّى عنها، أو تهدر أشهرًا في التعافي قبل العودة إلى paper-trading.
ما هو bug phantom fill؟
عندما يعتقد bot أن order تم تنفيذه بينما سجّلته المنصة على أنه لم يُنفذ بعد. الأعراض: يظهر المركز في حالة bots الخاصة بك لكن ليس على السلسلة، ما يؤدي إلى أوامر مزدوجة عند إعادة المحاولة. تم إصلاحه في trader الخاص بنا عبر ثلاثة commits (e68a087، 8bb7761، 06deaef): استخدم FOK للشراء، واستعلم عن الحالة حتى تتم المطابقة، ولا تثق أبدًا في status=delayed باعتباره تنفيذًا.
ما هي حادثة whipsaw lol-ctg-ccg؟
سوق esports على دفتر أوامر رقيق حيث أطلق trader لدينا stop-loss بقيمة -$2.55 عند 0.14، ثم شاهد السعر يتعافى إلى 0.325 خلال دقيقتين. كنا قد ضبطنا stop-loss عند -4 نقاط مئوية، وهو ضيق جدًا لدفاتر esports الرقيقة. الإصلاح: توسيع SL إلى -8pp للأسواق منخفضة السيولة، مع الإبقاء على SL أضيق فقط للدفاتر السميكة (NBA، كرة القدم عالية السيولة). راجع memory/trader-sl-wider.md.
كيف ظهر خطأ NegRisk flag؟
وضع bot أوامر دون تعيين neg_risk=true في الأسواق متعددة النتائج. تم رفض الأوامر برسائل خطأ مربكة، ما أدى إلى تأخيرات لعدة ثوانٍ قبل إعادة المحاولة، وبالتالي فوات fills. تم الإصلاح في commit 06deaef: عيّن دائمًا neg_risk وفق metadata الخاصة بالسوق، ولا تفترض أبدًا.
ما هي حادثة sleep-through-bug؟
علق المحفظة بسبب order عالق عند الساعة 4 صباحًا. وجّه المالك bot إلى التوقف؛ ولامس ملف data/halt_autobuy. اكتشف bot الملف قبل محاولة التداول التالية ورفض تنفيذ أوامر جديدة. استيقظ المالك على حالة نظيفة بدلًا من حالة أسوأ. تم التحقق من نمط halt-sentinel؛ ونحن نُضمّنه الآن افتراضيًا في كل bot.
ما هو الدرس الأكثر قابلية للتعميم من هذه التقارير؟
لا تثق أبدًا بالمسار السلس. كل bug شحنّاه جاء من افتراض أن الطلب نجح، أو أن fill حقيقي، أو أن السعر لن يتحرك. اكتب الكود دفاعيًا: افترض أن الأوامر تفشل، وافترض أن reconcilations تتباعد، وافترض أن سوقًا واحدًا على وشك أن يفعل شيئًا غريبًا. ضريبة الشك صغيرة؛ أما تكلفة تجاهلها فهي تقرير ما بعد الحادث الذي ستكتبه لاحقًا.