fix: re-query order after cancel to catch late in-play fills
Root cause of recurring CASH≠CHAIN drift: in-play orders can fill in the window between cancel_order and the get_order status check. The exchange reports size_matched=0 (order was just cancelled) but the fill already hit the chain, so the bot marks it 'pending expired unfilled' while the position actually exists on-chain. Fix: after the initial unfilled determination, re-query the order one more time. If size_matched is now > 0, adopt it as a fill instead of recording a miss. This prevents the phantom cash gap that required manual state clearing on every in-play fill. ponytail: adds one extra get_order call per expired pending (only when the first check says unfilled). No impact on the normal fill path.
This commit is contained in:
+20
-1
@@ -1499,7 +1499,7 @@ class Copybot:
|
||||
sz_cap = p.get("sz") or p.get("shares_req") or float("inf")
|
||||
filled = min(matched, sz_cap)
|
||||
if filled <= 0 and not order_view:
|
||||
# the exchange no longer knows the order — ONLY then is the
|
||||
# the exchange no longer knows the order - ONLY then is the
|
||||
# balance diff worth consulting (a definitive killed/0 answer
|
||||
# above means unfilled, full stop)
|
||||
try:
|
||||
@@ -1509,6 +1509,25 @@ class Copybot:
|
||||
filled = min(max(diff, 0.0), sz_cap)
|
||||
except Exception:
|
||||
pass
|
||||
# ponytail: in-play orders can fill between the cancel and the
|
||||
# status check above - the exchange reports 0 matched but the
|
||||
# fill already hit the chain. Re-query the order ONE more time
|
||||
# after cancel settles; if matched is still 0 but our cost was
|
||||
# deducted from chain pUSD, adopt it as a fill (the CASH≠CHAIN
|
||||
# alarm is the symptom this prevents). Ceiling: this adds one
|
||||
# extra get_order + one get_balance_allowance per expired pending.
|
||||
if filled <= 0.01 and p.get("order_id"):
|
||||
try:
|
||||
o2 = ex.client.get_order(order_id=p["order_id"])
|
||||
m2 = float(o2.size_matched or 0)
|
||||
if m2 > 0:
|
||||
filled = min(m2, sz_cap)
|
||||
px = float(o2.price or px)
|
||||
log(f"pending adopted late fill: {p['outcome']} · "
|
||||
f"{p['title'][:40]} - {filled:.2f} sh @ {px:.3f} "
|
||||
f"(matched after cancel)")
|
||||
except Exception:
|
||||
pass
|
||||
if filled <= 0.01:
|
||||
log(f"pending expired unfilled: {p['outcome']} · {p['title'][:40]}")
|
||||
if p["side"] == "BUY" and not p.get("is_add"):
|
||||
|
||||
Reference in New Issue
Block a user