پولی مارکیٹ بوٹ ٹیوٹوریل · باب 32 از 32
حقیقی پولی مارکیٹ بوٹ کی غلطیاں اور پوسٹ مارٹم: phantom فِلز، sticky-fail dedup، lol-ctg-ccg whipsaw، NegRisk flag bug، قبل از وقت go-live - ان commits اور dates کے ساتھ جنہوں نے ہر مسئلہ ٹھیک کیا۔
اس باب میں کیا شامل ہے
ہماری اپنی bugs کی پروڈکشن ڈائری جو حقیقی پیسہ لے گئے۔ اصل چیز مخصوص تفصیلات سے زیادہ پیٹرن ہے - bug کی وہی classes بوٹس میں بار بار آتی ہیں، اور علاج عموماً کوئی missing watchdog ہوتا ہے، نہ کہ بہتر حکمتِ عملی۔ اس باب کا مقصد آپ کی tuition بچانا ہے۔
- Phantom فِلز (commits e68a087, 8bb7761)
- NegRisk flag bug (commit)
- Sticky-fail dedup (commit)
- Whipsaw incident: lol-ctg-ccg
- قبل از وقت go-live: 2025 wipe
- Sleep-through-bug: kill switch نے کام کیا
- وہ اسباق جو عام لاگو ہوتے ہیں
Phantom فِلز (commits e68a087, 8bb7761)
May 2025 میں ہمارے تاجر پر پہلا بڑا phantom-فِل incident۔ بوٹ نے 22 FOK buys place کیے، اور سب CLOB پر matched ہو گئے۔ بوٹ نے فوراً 22 GTC sells post کرنے کی کوشش کی۔ ان میں سے 8 نے "balance: 0 / sum of active orders: 0 / order amount: 10000000." کے ساتھ reject کر دیا۔
Root cause: تصفیہ lag (chapter 12). CLOB نے 100ms میں میچ کیا، بوٹ نے 200ms میں sell post کر دی، لیکن Polygon ERC-1155 transfer میں تقریباً 2 سیکنڈز لگے۔ CLOB نے sell reject کر دی کیونکہ chain پر ابھی تک zero balance دکھ رہا تھا۔
Fix: کسی بھی successful buy اور اسی token پر آنے والی GTC follow-up کے درمیان 5-second blocking wait insert کریں۔ Commits e68a087 اور 8bb7761۔ تب سے zero phantom-فِل incidents۔
Lesson: API time اور chain time دو مختلف timelines ہیں۔ جو code یہ assume کرے کہ یہ synchronous ہیں، وہ اسی exact failure mode سے ٹکرائے گا۔
NegRisk flag bug (commit)
8 candidates والے NegRisk multi-نتیجہ ایونٹ میں 1.8c کا momentary arb تھا (sum of YES asks = 0.982)۔ ہمارے arber نے 8 کے 8 FOK buys fire کیے۔ 6 فِل ہو گئے؛ 2 غلط ایکسچینج contract میں طے ہو گئے۔
Root cause: بوٹ createAndPostOrder call کر رہا تھا مگر flags object میں negRisk: true set نہیں کر رہا تھا۔ دو مارکیٹس کی historical creation date مختلف تھی اور انہیں flag کی ضرورت تھی؛ باقی چھ کو اس کی ضرورت نہیں تھی کیونکہ ان کا underlying contract پہلے ہی default طور پر NegRisk کے ذریعے route ہو رہا تھا۔
Fix: ہر مارکیٹ کے لیے Gamma سے market.negRisk پڑھیں، اور ہر order call میں pass کریں۔ Commit۔ ہم نے flag set کر کے arb دوبارہ run کیا؛ باقی 2 درست طریقے سے طے ہو گئے۔
Lesson: کبھی بھی مارکیٹ property کو default نہ کریں۔ ہر بار ذریعہ of truth سے explicitly پڑھیں۔
Sticky-fail dedup (commit)
Bot نے 12 سیکنڈز میں failed buy کو 5 بار retry کیا۔ پہلی attempt دراصل succeed ہو گئی تھی (network ٹائم آؤٹ کی وجہ سے بوٹ جواب نہ دیکھ سکا)؛ اگلے 4 retries نے 4 اضافی پوزیشنز create کر دیں۔ Total: اسی مارکیٹ پر 5 پوزیشنز جب ہمیں 1 چاہیے تھی۔
Root cause: idempotent client-order-id موجود نہیں تھا۔ بوٹ کی retry logic یہ تھی: "اگر fail ہوا، تو نئے salt کے ساتھ دوبارہ کوشش کرو۔" CLOB کے پاس retries کو duplicates سمجھنے کا کوئی طریقہ نہیں تھا۔
Fix: پہلی attempt سے پہلے intended order کے لیے deterministic UUID generate کریں۔ تمام retries same client-order-id استعمال کریں، تاکہ CLOB dedup کر سکے۔ Commit۔
Lesson: idempotence کے بغیر retries، duplicates ہیں۔ ہر order کے لیے ایک stable client-side identifier چاہیے۔
Whipsaw incident: lol-ctg-ccg
ایک esports میچ (CTG vs CCG) میں imbalance positive ہوتے ہی بوٹ نے 0.45 پر buy enter کی۔ 30 سیکنڈز کے اندر imbalance negative ہو گیا اور 0.50 پر ہماری GTC sell کسی اور کے order سے hit ہو گئی۔ PnL: +5c × 10 shares = +$0.50۔
10 minutes بعد اسی مارکیٹ کا imbalance پھر positive ہو گیا۔ بوٹ نے دوبارہ 0.42 پر انٹری لی۔ اس بار imbalance recover نہیں ہوا؛ mid 0.18 تک drift کر گیا اور پوزیشن ریزولوشن تک 0 پر چلی گئی۔
Root cause: حکمتِ عملی نے imbalance کو سمتی سگنل سمجھا، مگر یہ track نہیں کیا کہ imbalance bounce کر رہا تھا - دونوں سگنلز noise تھے، information نہیں۔ بوٹ ایک ہی مارکیٹ میں 20 minutes کے اندر دو failed سگنلز کے درمیان whipsaw ہو گیا۔
Fix: ہر مارکیٹ کے لیے cooldown - فِل کے بعد اسی مارکیٹ میں 30 minutes تک کوئی نئی انٹری نہیں۔ مختلف مارکیٹس میں multiple انٹریز allowed تھیں، مگر ایک ہی مارکیٹ میں back-to-back نہیں۔
Lesson: جو سگنل bounce کرے، وہ سگنل نہیں۔ عمل کرنے سے پہلے persistence کے لیے فلٹر کریں۔
Premature go-live: 2025 wipe
ایک نئی market-making حکمتِ عملی نے 12 پیپر ٹریڈز پاس کیں۔ Builder نے 30 کا انتظار نہیں کیا، "looking good" سمجھا، اور $500 capital کے ساتھ لائیو deploy کر دیا۔ 18 hours کے اندر والٹ $200 پر آ گیا۔
Root cause: 12 ٹریڈز اتنا sample نہیں کہ 60% WR اور 35% WR میں فرق کیا جا سکے۔ اصل میں حکمتِ عملی 35% WR تھی؛ 12-ٹریڈ پیپر window بس ایک غیر نمائندہ streak نکلی۔
30-ٹریڈ gate ایک وجہ سے موجود ہے۔ 12-ٹریڈ sample کی variance اسے "حکمتِ عملی کام نہیں کرتی" سے الگ پہچاننا مشکل بنا دیتی ہے۔
Lesson: discipline، conviction سے بہتر ہے۔ 30-ٹریڈ gate پر سمجھوتہ نہیں کیا جا سکتا۔
Sleep-through-bug: kill switch worked
Bot کے time-of-day فلٹر میں off-by-one تھا - 02:00 UTC پر pause کرنا تھا، مگر اصل میں 03:00 UTC پر pause ہو رہا تھا۔ 02:00-03:00 کے un-paused hour کے دوران Polygon RPC ہماری درخواستیں کو بہت زیادہ rate-limit کر رہا تھا؛ بوٹ کا read path stale ڈیٹا واپس کر رہا تھا۔
Bot stale قیمتیں پر ٹریڈ کرتا رہا۔ اس hour کا PnL: 22 ٹریڈز پر -$3.20۔ daily-loss kill switch -5% پر trigger ہوا، بوٹ کو halt کیا، اور 03:08 UTC پر Telegram alert بھیجی۔ Builder 09:00 پر ایک halted بوٹ پر جاگا؛ total damage kill حد تک محدود رہا۔
Lesson: bug real تھا مگر kill switch نے کام کیا۔ -$3.20، -$50.00 کے بجائے۔ رسک controls bugs کو prevent نہیں کرتے؛ وہ ان bugs کی cost cap کرتے ہیں جنہیں آپ نے آتے ہوئے نہیں دیکھا۔
Lessons that generalize
تمام پوسٹ مارٹم میں چار patterns بار بار آتے ہیں۔
- API time ≠ chain time. تصفیہ lag، RPC lag، WebSocket lag - یہ سب gaps introduce کرتے ہیں جنہیں بوٹ code کو explicitly handle کرنا چاہیے۔
- Retries need idempotence. Client-order-id کے بغیر retry duplicate-order رسک ہے۔ ہمیشہ۔
- ہر مارکیٹ property explicitly پڑھیں. NegRisk flag، tick size، expiration۔ کبھی default نہ کریں؛ ہمیشہ ذریعہ of truth سے پڑھیں۔
- Kill switch floor ہے، feature نہیں. رسک controls bugs پر نقصانات cap کرتے ہیں۔ حکمتِ عملیاں bugs prevent نہیں کرتیں؛ وہ assume کرتی ہیں کہ بوٹ درست کام کر رہا ہے۔ بوٹ ہمیشہ درست کام نہیں کرے گا۔
اس series کے ہر chapter میں ان میں سے کوئی نہ کوئی pattern کہیں نہ کہیں embedded ہے۔ یہ پروڈکشن بوٹ کے load-bearing principles ہیں۔ انہیں skip کریں، اور آپ انہیں اپنی پوسٹ مارٹم میں پھر سے دیکھیں گے۔


