Polymarket Bot Tutorial · अध्याय 12 of 32
Polymarket phantom fills (ऐसे ऑर्डर जो filled दिखते हैं लेकिन वास्तव में नहीं होते), idempotent retries लागू करना, status=matched को rested-on-book से अलग पहचानना, और transient failures से बचकर निकलना कैसे है।
यह अध्याय क्या कवर करता है
एक phantom fill Polymarket-विशिष्ट failure mode है, जहाँ CLOB मैच को स्वीकार कर लेता है लेकिन chain ने अभी तक ERC-1155 transfer को confirm नहीं किया होता। ~5 seconds के भीतर आने वाला follow-up order भ्रामक "balance: 0" error के साथ reject हो जाता है। इसका इलाज है idempotence और settlement wait। यह अध्याय वही production playbook है जिसके लिए हमने असली पैसे चुकाए थे।
- phantom fill क्या है
- status=matched vs status=delayed vs status=posted
- Polling pattern: celebrate करने से पहले status poll करें
- phantom-fill के खिलाफ FOK
- client_order_id के साथ Idempotent retries
- Real production incident: हमने अपना कैसे fix किया
- Code: detect-then-act post-order pattern
phantom fill क्या है
Phantom fill तब होता है जब CLOB API आपके order का जवाब status: "matched" के साथ देता है, लेकिन on-chain ERC-1155 transfer अभी तक settle नहीं हुआ होता। CLOB matcher, Polygon block production (~2s per block) से तेज़ है। API match के बाद लगभग 2-5 seconds तक आपका wallet on-chain उन tokens को hold नहीं कर रहा होता, जिन्हें matcher आपके होने का दावा करता है।
Bot bug तब उभरता है जब कोई follow-up action - आम तौर पर take-profit पोस्ट करने के लिए GTC sell - उस window के भीतर चलती है। CLOB chain balance चेक करता है, zero देखता है, और not enough balance / allowance: balance: 0, order amount: N के साथ reject कर देता है। Error message allowance को blame करता है; असली कारण settlement lag है।
पहली बार ऐसा होने पर आप इसे allowance bug समझते हैं और एक घंटा बर्बाद कर देते हैं। इलाज सरल है: wait करें, verify करें, फिर post करें।
status=matched vs status=delayed vs status=posted
Order placement response में एक status field होती है, जिसमें तीन values मायने रखती हैं।
matched: order तुरंत book के साथ match हो गया। Inventory 2-5 seconds में settle होगी। सफल होने पर FOK/FAK यही return करते हैं।delayed: matcher synchronous रूप से settle नहीं कर सका और match को queue कर दिया। यह rare है; आम तौर पर congestion का संकेत है। wait + verify pattern के लिए इसेmatchedकी तरह treat करें।posted(जिसेliveभी कहा जाता है): order book पर resting है और अभी fill नहीं हुआ। यह उन GTC orders से return होता है जो तुरंत match नहीं हुए। Inventory प्रभावित नहीं होती; अभी follow-up action की ज़रूरत नहीं।
Decision rule: अगर status matched या delayed है, तो नई inventory की ज़रूरत वाले किसी भी follow-up को chain transfer verify किए बिना place न करें।
Polling pattern: celebrate करने से पहले status poll करें
Verification pattern: successful match के बाद, CTF balance को तब तक poll करें जब तक वह नए tokens reflect न करने लगे, फिर आगे बढ़ें।
def wait_for_settlement(token_id, expected_size, timeout=15):
"""Block until on-chain balance reaches expected_size or timeout."""
start = time.time()
while time.time() - start < timeout:
bal = ctf_contract.functions.balanceOf(PROXY, token_id).call()
if bal >= expected_size:
return True
time.sleep(0.5)
return False
Typical settlement: अच्छी network conditions में 2-5 seconds, Polygon congestion के दौरान 15s तक। 5-second wait 95% मामलों को cover करता है; production के लिए timeout 15s रखें और timeout पर alert करें।
ऐसे high-frequency bots के लिए जो block नहीं हो सकते, एक alternative है event-subscription: अपने proxy address के लिए CTF के TransferSingle event को watch करें और receipt मिलते ही downstream actions trigger करें। इससे wait strategy loop को block करने के बजाय queue में चला जाता है।
phantom-fill के खिलाफ FOK
FAK की बजाय FOK चुनना phantom-fill chaos के खिलाफ आंशिक defense है। FOK या तो पूरे order को fill करता है या cancelled return करता है; FAK filled_size के रूप में partial return दे सकता है। जब partial fill के बाद original order के size के हिसाब से GTC sell किया जाता है, तो sell settlement-lag और size mismatch - दो कंपाउंड होने वाले bugs - के कारण fail हो जाता है।
FOK के साथ size binary होता है: या तो पूरा size match हुआ या कुछ भी नहीं हुआ। Follow-up posting logic को हमेशा पता रहता है कि क्या expect करना है।
इससे wait की ज़रूरत खत्म नहीं होती - perfect FOK match भी 2-5 second settlement window के अधीन होता है। लेकिन इससे bookkeeping divergence की एक class हट जाती है।
client_order_id के साथ Idempotent retries
Order placement के दौरान network failures worst-case scenario बनाते हैं: bot का HTTP call timeout हो गया, लेकिन order मिला था या नहीं, यह पता नहीं। Blind retry double-place कर सकता है; retry न करने से position छूट सकती है।
इसका fix है order placement पर client_order_id field। Intended order के लिए deterministic UUID generate करें; अगर server ने वह ID पहले देखी है, तो वह duplicate बनाने के बजाय existing order status return करेगा।
import uuid
oid = str(uuid.uuid4()) # generate once, retry with same value
for attempt in range(3):
try:
resp = c.create_and_post_order(args, OrderType.FOK, client_order_id=oid)
return resp
except (TimeoutError, ConnectionError):
time.sleep(0.5 * (2 ** attempt))
raise RuntimeError("post failed after 3 attempts")
Pattern: पहले ID generate करें, transport failure पर retry करें, logical rejection पर कभी नहीं। Server-side dedup प्रति-API-key होता है, और लगभग 5 minutes तक रहता है।
Real production incident: हमने अपना कैसे fix किया
हमारी अपनी production diary से, मई 2025। 60-minute window में trader bot ने 22 buy orders place किए, सभी matched हुए, लेकिन सिर्फ 14 GTC sells accepted हुए। आठ positions के लिए exit पोस्ट नहीं हुआ था।
Root cause: bot ने buy match के 800ms के भीतर GTC sell post कर दिया, यानी chain द्वारा ERC-1155 transfer confirm होने से काफी पहले। CLOB ने "balance: 0" message के साथ reject किया; bot ने error log किया लेकिन retry नहीं किया। आठ positions silently resolution तक चली गईं, बिना take-profit protection के। तीन out of the money close हुए; एक luck से 0.99 पर close हुआ।
Fix एक 5-second blocking wait के रूप में shipped हुआ, जो एक ही token पर किसी भी buy fill और किसी भी GTC post के बीच लगाया गया। 30 paper trades और 30 live trades से verify किया गया; तब से zero balance-zero errors।
Lesson: एक silent error path, loud one से ज़्यादा महंगा होता है। इसके बाद हमने सभी phantom-fill errors को Telegram alert trigger करने के लिए set किया, ताकि future drift mode कुछ ही seconds में दिख जाए।
Code: detect-then-act post-order pattern
Production buy-then-post pattern.
def buy_then_post_tp(token_id, size, buy_price, tp_price):
# 1. Place FOK buy
buy_args = OrderArgs(token_id=token_id, price=buy_price, size=size, side="BUY")
buy_resp = c.create_and_post_order(buy_args, OrderType.FOK)
if buy_resp.status != "matched":
return {"ok": False, "stage": "buy", "reason": buy_resp.status}
# 2. Wait for on-chain settlement (5s sane default; bump for congestion)
if not wait_for_settlement(token_id, expected_size=size, timeout=15):
return {"ok": False, "stage": "settle", "reason": "timeout"}
# 3. Confirm minimum size for GTC
if size < 5:
# GTC won't accept; fall back to ride-to-resolve or FOK sell at TP later
return {"ok": True, "stage": "buy_only", "note": "size<5, no GTC posted"}
# 4. Post GTC sell
sell_args = OrderArgs(token_id=token_id, price=tp_price, size=size, side="SELL")
sell_resp = c.create_and_post_order(sell_args, OrderType.GTC)
return {"ok": sell_resp.status in ("posted","live"), "buy": buy_resp, "sell": sell_resp}
यह pattern सामान्य failure modes से बचता है: phantom-fill, transient network drop, under-minimum GTC size। यह strategy layer को इतना information देता है कि वह तय कर सके क्या retry करना है, क्या log करना है, और क्या alert करना है।










