PINK: fix fee-sign bug + WARN-unfreeze — 451/451 tests green
Defect A (fee sign): bingx_user_stream._normalise_order flipped to
fee = -raw_fee so BingX negative-n costs arrive as positive kernel
costs. k_maker_rebates no longer accumulates phantom rebates.
Defect B (opening fee dropped): fill_qty now falls back to "z"
(cumFilledQty) when "l" (lastFilledQty) is zero/absent, so
apply_predicted_fill computes a non-zero opening-leg fee.
Architectural fix (WARN unfreezes): lib.rs reconcile() now unfreezes
capital_frozen on WARN as well as OK. WARN (0.01-20 USDT delta) is
normal in-flight settlement — only ERROR (≥20, unexplained) should
halt ENTERs. The old keep-state logic trapped the kernel permanently
frozen after the first trade's ENTER predicted-fee phase pushed delta
briefly into ERROR.
Acceptance criterion: |k_capital - bingx_balance| < 1 USDT, frozen=False
after every round-trip trade — verified numerically against T-1/T-2
ground truth from the CRITICAL doc.
Docs: CRITICAL_AGENT-TODO_ACCOUNTING_BUGFIX.md §12-13 (fix record),
CAPITAL_BOOKKEEPING_DESIGN.md §8 (kernel spec), SYSTEM_BIBLE §11.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -333,15 +333,22 @@ class BingxUserStream:
|
||||
str(o.get("o") or o.get("type") or "MARKET").upper() == "LIMIT"
|
||||
and status in {"FILLED", "PARTIALLY_FILLED"}
|
||||
))
|
||||
# Fees: BingX sends commission as positive for costs, negative for rebates
|
||||
# DEFECT A FIX: BingX sends "n" (commission) NEGATIVE for costs, POSITIVE for
|
||||
# rebates on VST. Kernel convention is the opposite: POSITIVE = cost, NEGATIVE
|
||||
# = rebate. Flip at this boundary so the kernel never sees wrong-sign fees.
|
||||
raw_fee = _safe_float(o.get("n") or 0.0)
|
||||
fee = raw_fee # may be negative (rebate)
|
||||
fee = -raw_fee # BingX cost (negative n) → kernel cost (positive fee)
|
||||
# DEFECT B FIX: prefer "l" (lastFilledQty) when non-zero; fall back to "z"
|
||||
# (cumFilledQty) for ENTER fills where BingX reports qty only in "z".
|
||||
_last_qty = _safe_float(o.get("l") or 0.0)
|
||||
_cum_qty = _safe_float(o.get("z") or o.get("cumFilledQty") or 0.0)
|
||||
fill_qty = _last_qty if _last_qty > 0.0 else _cum_qty
|
||||
return ExchangeEvent(
|
||||
kind=kind,
|
||||
event_id=str(o.get("i") or o.get("orderId") or uuid.uuid4().hex),
|
||||
exchange_ts=ts,
|
||||
fill_price=_safe_float(o.get("L") or o.get("ap") or o.get("p")),
|
||||
fill_qty=_safe_float(o.get("l") or o.get("lastFilledQty") or 0.0),
|
||||
fill_qty=fill_qty,
|
||||
fee=fee,
|
||||
fee_asset=str(o.get("N") or ""),
|
||||
realized_pnl=_safe_float(o.get("rp") or o.get("realizedPnl") or 0.0),
|
||||
|
||||
Reference in New Issue
Block a user