Polymarket Bot Tutorial · Sura ya 12 kati ya 32

Jinsi ya kudetect Polymarket phantom fills (orders zinazoonekana zimefilled lakini hazijafilled), kuimplement idempotent retries, kutofautisha status=matched na rested-on-book, na kuishi transient failures.

Sura hii inafunika nini

Phantom fill ni Polymarket-specific failure mode ambapo CLOB inakubali match lakini chain bado haijaconfirm ERC-1155 transfer. Follow-up order ndani ya ~sekunde 5 inarejected na misleading "balance: 0" error. Tiba ni idempotence na settlement wait. Sura hii ni production playbook tuliyolipa kwa pesa halisi.

  • Ni nini phantom fill
  • status=matched vs status=delayed vs status=posted
  • Polling pattern: poll status kabla ya kuadhimisha
  • FOK kama anti-phantom-fill
  • Idempotent retries na client_order_id
  • Real production incident: jinsi tulivyofix yetu
  • Code: detect-then-act post-order pattern

Ni nini phantom fill

Phantom fill ni wakati CLOB API inajibu order yako na status: "matched" lakini on-chain ERC-1155 transfer bado haijasettle. CLOB matcher ni haraka kuliko Polygon block production (~sekunde 2 per block). Kwa takriban sekunde 2-5 baada ya API match, wallet yako on-chain haishikilii tokens ambazo matcher inasema unamiliki.

Bot bug inaemerge wakati follow-up action - kawaida GTC sell ya kuipost take-profit - inarun ndani ya window hiyo. CLOB inacheck chain balance, inaona zero, na inarejects na not enough balance / allowance: balance: 0, order amount: N. Error message inalaumu allowance; sababu ni settlement lag.

Mara ya kwanza hii inatokea unaaasume allowance bug na unapoteza saa. Tiba ni rahisi: subiri, hakiki, kisha post.

status=matched vs status=delayed vs status=posted

Order placement response inajumuisha status field na values tatu zinazojali.

  • matched: order ilimatch dhidi ya book mara moja. Inventory itasettle katika sekunde 2-5. Hii ndio FOK/FAK zinarudisha zinaposhinda.
  • delayed: matcher haikuweza kusettle synchronously na ilipangia match. Nadra; kawaida inaonyesha congestion. Treat kama matched kwa purpose ya wait + verify pattern.
  • posted (pia inaitwa live): order inapumzika kwenye book bila kufilled. Inarudishwa na GTC orders ambazo hazikumatch mara moja. Inventory haiathiriki; hakuna follow-up action inayohitajika bado.

Decision rule: ikiwa status ni matched au delayed, usiweke follow-up yoyote inayohitaji new inventory hadi uhakiki chain transfer.

Polling pattern: poll status kabla ya kuadhimisha

Verification pattern: baada ya successful match, poll CTF balance hadi ireflect new tokens, kisha endelea.

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: sekunde 2-5 katika good network conditions, hadi 15s wakati wa Polygon congestion. 5-second wait inafunika 95% ya cases; kwa production weka timeout kwa 15s na alert kwenye timeout.

Kwa high-frequency bots ambazo haziwezi kublock, alternative ni event-subscription: angalia CTF TransferSingle event kwa proxy address yako na trigger downstream actions kwenye receipt. Hii inapush wait kwa queue badala ya kublock strategy loop.

FOK kama anti-phantom-fill

Kuchagua FOK juu ya FAK ni partial defense dhidi ya phantom-fill chaos. FOK ofuli fills entire order au inarudisha cancelled; FAK inaweza kurudisha filled_size ambayo ni partial. Wakati partial fill inafollowed na GTC sell sized kwa original order, sell inashindwa kwenye settlement-lag pamoja na size mismatch - bugs mbili zinacompound.

Na FOK, size ni binary: ofuli full size ilimatch au hakuna kitu kilichofanya. Follow-up posting logic daima inajua nini cha kutarajia.

Hii haifuti haja ya wait - hata perfect FOK match iko subject kwa 2-5 second settlement window. Lakini inaondoa class moja ya bookkeeping divergence.

Idempotent retries na client_order_id

Network failures wakati wa order placement zinaunda worst-case scenario: HTTP call ya bot ilitimeout, lakini order inaweza kuwa au isiwe ilipokelewa. Kuretry naively kunaweza kudouble-place; kutoretry kunaweza kuangusha position.

Fix ni client_order_id field kwenye order placement. Tengeneza deterministic UUID per intended order; ikiwa server imeona ID hiyo kabla, inarudisha existing order status badala ya kutengeneza duplicate.

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: tengeneza ID kwanza, retry kwenye transport failure, kamwe kwenye logical rejection. Server-side dedup ni per-API-key, inadumu ~dakika 5.

Real production incident: jinsi tulivyofix yetu

Kutoka production diary yetu wenyewe, Mei 2025. Window ya dakika 60 ambapo trader bot ilipost FOK buys 22, zote zilimatch, lakini GTC sells 14 tu zilikubaliwa. Positions 8 hazikuwa na exit iliyopost.

Root cause: bot ilipost GTC sell ndani ya 800ms ya buy match, vizuri kabla chain ikahakiki ERC-1155 transfer. CLOB ilirejects na "balance: 0" message; bot iliilogi error lakini haikuretry. Positions 8 silently zikariden hadi resolution bila take-profit protection. Tatu zilifunga out of the money; moja ilifunga 0.99 kwa luck.

Fix iliship kama blocking wait ya sekunde 5 kati ya buy fill yoyote na GTC post yoyote kwa token sawa. Imeverified kupitia trades 30 za paper pamoja na trades 30 za live; zero balance-zero errors tangu hapo.

Somo: silent error path ni gharama zaidi kuliko loud one. Baada ya hii tulifanya phantom-fill errors zote zi-trigger Telegram alert, ili future drift mode ingeonekana ndani ya sekunde.

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 inaishi common failure modes: phantom-fill, transient network drop, under-minimum GTC size. Inarudisha information ya kutosha kwa strategy layer kuamua nini cha kuretry vs log vs alert.

Maswali yanayoulizwa mara kwa mara

Ni nini phantom fill kwenye Polymarket?
Phantom fill ni wakati bot yako inaamini order imefilled lakini exchange inairecord kama bado haijafilled (au partially filled). Inatokea wakati client code inatreat HTTP 200 response kama confirmation, wakati response inamaanisha tu order ilikubaliwa katika matching engine. Tulijifunza hii kwa njia ngumu - commits 06deaef, 8bb7761, na e68a087 katika trader history yetu zinafix exact hii.
Ninawezaje kuepuka phantom fills?
Rules tatu: (1) Tumia FOK orders kwa buys - order ni ofuli filled au ofuli gone, kamwe ambiguous. (2) Treat status yoyote non-matched kama not filled - poll order status hadi status=matched AU amount_filled > 0. (3) Tumia client_order_id (clientOrderId katika V2) kwa idempotence ili retries zisidouble-fill.
Status=delayed inamaanisha nini?
Order iko katika matching engine lakini bado haijafullycollabmatched. Inaweza kumatch ndani ya sekunde au inaweza kupumzika. Daima poll - ikiwa status inakaa delayed kwa zaidi ya sekunde 5-10 na amount_filled ni 0, treat kama unfilled na consider cancelling.
Ninawezaje kuretry safely bila kudouble-fill?
Tengeneza unique client_order_id per logical trade attempt na uipasse kwenye kila retry. Exchange inadedupe kwa client_order_id kwa hivyo retried order na id ile ile inarejected kama duplicate badala ya kuwekwa tena. Implementations: Python OrderArgs.client_order_id, Node CreateOrderOptions.clientOrderId.
Je, ninaweza kutrust 200 OK response kutoka order endpoint?
Hapana - 200 OK inamaanisha tu "request yako ilikubaliwa na matching engine," sio "order yako ilifilled." Lazima poll order status na orderId baada ya submission na treat tu status=matched (au amount_filled > 0) kama real fill.
Ni nini ikiwa bot yangu inacrash kati ya kutuma order na kuona response?
Kwenye restart, query open orders na recent fills kupitia SDK. Reconcile dhidi ya local diary/state yako - ikiwa ulituma order kwa wakati T lakini hakuna record iliyokuwepo, query orders tangu T na match kwa client_order_id. Ikiwa bado missing, order haikufika kwa matching engine na unaweza safely kutuma tena.