Files
siloqy/prod/docs/VIOLET_PART_SPEC_OA_TODO_PASS5.md
Codex 28a2b90b1d VIOLET OA: partial spec PASS 5 — mock-BingX execution stack (quirks seam-only, deferred)
VIOLET_PART_SPEC_OA_TODO_PASS5.md — the DARK mock-BingX execution stack so the full order
lifecycle is buildable/testable with zero keys/risk (V4-live later = swap mock for real client).
Five independent units extending contracts_v3 (Order/OrderAck/Fill/OrderStatus/PositionDelta):
  20. Execution contracts + QuirkProfile seam registry (no quirk logic).
  21. Mock BingX order FSM (legal lifecycle, partials, reject, reduce-only-increase rejection).
  22. Mock BingX venue adapter (port-conformant, injectable fill model, quirk hooks INERT by default).
  23. Fill→position/PnL reducer (PnL from fill_price ONLY; ×leverage-aware) → PositionDelta.
  24. DARK E2E gate: ExecIntent → router/driver → mock venue → fill reducer → PASS-4 ledger.

CRITICAL per operator: a prominent ⚠️ section makes explicit that BingX "quirks" (zero-wb WS
frames, ownership/foreign-fill collision, bound-price poison, ×leverage notional, settle desync,
reduce-only edge, setLeverage race, dead .pro TLS) are EXPLICITLY OUT OF SCOPE for PASS 5 — the
mock carries injection SEAMS (QuirkProfile, default OFF) to accommodate them LATER, and a real-key
boundary smoke remains MANDATORY before V4-live. Quirks sourced from PINK orphan/reconcile +
DITAv2 audit memory.

Standing ready to write PASS 6–9 (exec internals/reconcile, V5 selection+slots, V6 bible
consumers, economics/observability). Added PASS 5 to the review queue in VIOLET_TODO_CRITICAL.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 10:26:15 +02:00

13 KiB
Raw Blame History

VIOLET — partial spec for another agent, PASS 5 (OA TODO): MOCK-BINGX EXECUTION STACK

Date: 2026-06-17. Continues PASS 14 (same V0→V6 plan, same independent-unit discipline). PASS 5 builds the mock-BingX execution stack — the DARK path that lets the ENTIRE order lifecycle (submit → ack → fill/partial/reject → reconcile → position/PnL) be built and bit-tested with zero keys, zero risk, so that V4-live later becomes "swap the mock adapter for the real BingX client." This is the largest remaining buildable-while-DARK chunk of the spec.

Read first: the V2 execution work already shipped — prod/clean_arch/violet/scripted_venue.py, exec_driver.py, exec_harness.py, observe_guard.py, synthetic_intents.py, exchange_leverage.py, and PINK's prod/clean_arch/runtime/pink_direct.py + prod/clean_arch/exec/ ExecutionRouter (READ for maker/taker policy conventions; do NOT edit). PASS 5 extends, does not replace, these.


⚠️ ⚠️ READ THIS FIRST — EXCHANGE "QUIRKS" ARE EXPLICITLY OUT OF SCOPE FOR PASS 5 ⚠️ ⚠️

The mock built here implements BingX's NORMATIVE / documented semantics only (clean order lifecycle, fills, leverage, reduce-only, maker/taker, partials). BingX has a substantial set of undocumented / edge "quirks" that broke PINK in production. They are NOT to be implemented or worked around during PASS 5. Two hard requirements instead:

  1. The mock MUST be structured with explicit INJECTION SEAMS so each quirk can later be turned on (a QuirkProfile config + hook points) to test the reconcile/filter logic against it — WITHOUT rewriting the mock.
  2. A real-key smoke at the venue boundary remains MANDATORY before V4-live — a mock can only validate logic, never undocumented venue reality.

Known BingX quirks to ACCOMMODATE-LATER (do NOT implement now; just leave seams + a registry):

Source: memory project_pink_orphan_fixes, ditav2_kernel_audit_20260611, incident_*.

  1. Zero-wallet-balance WS frames — BingX transiently pushes account/position frames with walletBalance=0; treating them as truth zeroes capital. (Reconcile must ignore zero-wb / use reseed-on-update.) Seam: mock can emit a zero-wb frame on demand.
  2. Ownership / foreign-fill collision — fills/orders for OTHER subaccounts appear on a shared user-data stream (PRODGREEN↔PINK shared-account hazard); must be filtered by ownership. Seam: mock can emit a fill tagged with a foreign ownership_id.
  3. Bound-price poison — orders carry a "bound" price; using it for PnL instead of the actual fill price poisons accounting. Seam: mock fill carries both bound and fill price; PnL must use fill price (Task 23).
  4. ×leverage notional — BingX reports notional/qty with leverage applied; double-applying leverage corrupts size. Seam: documented in the Fill→position reducer (Task 23).
  5. Settle / funding desync — settlement & funding events arrive desynced from fills; capital deltas must be event_seq-ordered (ties to PASS-4 economics ledger). Seam: mock can interleave a settle event out of fill order.
  6. Reduce-only edge — a reduce-only order that would INCREASE the position is rejected by BingX. Implement this ONE as normative (it is documented), but note the partial/zero-qty edge.
  7. setLeverage racesetLeverage is a separate call with its own ack; a race vs the order can apply the wrong leverage. Seam: mock set_leverage is a distinct, separately-ackable op.
  8. Dead .pro TLS WS backup — the .pro WS endpoint TLS can die silently (connection-layer). Out of mock scope (connection layer); note for the real-client adapter.

A new file prod/clean_arch/violet/exec/bingx_quirks.py holds ONLY a QuirkProfile (StrictModel, all quirks default OFF) + an enum registry of the above — the seam contract. No quirk LOGIC. Tasks below reference QuirkProfile at their hook points but implement none.


0. HARD RULES (identical to PASS 14 — summarized)

  • Never edit shared files (prod/nautilus_event_trader.py, clean_arch/dita_v2/**, dita/decision.py, nautilus_dolphin/**, blue_parity.py, prod/bingx/leverage.py). READ only. (You MAY read prod/clean_arch/exec/** + pink_direct.py for router/venue conventions; do not edit.)
  • VIOLET DARK — the mock NEVER touches a real venue, network, or key. No service/HZ control.
  • V-TYPES on all new code; faithful poison-guards only.
  • NEW-FILE-ONLY under a NEW package prod/clean_arch/violet/exec/ (plus extending contracts_v3.py). Do NOT modify in-flight files (the V3.4 sourcing/engine set, cadence.py, clock.py, exchange_leverage.py, scripted_venue.py, exec_driver.py, 22_violet_decisions.sql). READ + IMPORT them.

0a. COMMIT / BRANCH POLICY (3 shared-index collisions on 2026-06-16 — non-negotiable)

Own git worktree (git worktree add ../vp-oa5 -b agent/oa-violet5) strongly preferred. Else never git add -A; git commit -F msg -- <files> with explicit pathspec; verify git show --stat --format="" HEAD lists ONLY your files. One commit/task, prefix VIOLET OA:, Co-Authored-By trailer. Tests on /home/dolphin/siloqy_env/bin/python3. git grep only.


I. SHARED INTERFACE EXTENSIONS (add to contracts_v3.py; never fork a parallel type)

Reuse PASS-3/4 types (VenueTick, OpenPositionView, ExitDecision, ExecIntent, CapitalState). ADD the execution vocabulary (all StrictModel / Annotated V-TYPES):

  1. OrderType = Literal["MAKER","TAKER"]; OrderSide = Literal["BUY","SELL"]; OrderStatus = Literal["NEW","ACK","PARTIALLY_FILLED","FILLED","CANCELED","REJECTED"].
  2. Orderclient_order_id: str, asset: Symbol, side: OrderSide, qty: Qty, price: Px (limit; maker), order_type: OrderType, reduce_only: bool, leverage: Annotated[int, Field(ge=1)], ts_ns: MonoNs.
  3. OrderAckclient_order_id: str, venue_order_id: str, status: OrderStatus, ts_ns: MonoNs, reject_reason: str = "".
  4. Fillvenue_order_id: str, asset: Symbol, side: OrderSide, fill_qty: Qty, fill_price: Px, bound_price: Px (quirk #3 seam — carry it, never use it for PnL), fee: float (ge=0), is_maker: bool, ownership_id: str (quirk #2 seam), event_seq: Seq, ts_ns: MonoNs.
  5. PositionDeltaasset: Symbol, qty_delta: float, realized_pnl_delta: float, fee: float (ge=0), event_seq: Seq — the unit that feeds PASS-4 EconomicsLedger.

If a task needs another field, ADD it here and note it.


TASK 20 — Execution contracts + QuirkProfile seam registry

Why. The shared execution vocabulary + the quirk-seam contract everything else depends on. Affected files (NEW): extend prod/clean_arch/violet/contracts_v3.py (the §I types); prod/clean_arch/violet/exec/__init__.py; prod/clean_arch/violet/exec/bingx_quirks.py (QuirkProfile StrictModel, all flags default False + the quirk enum registry — NO logic); prod/clean_arch/violet/exec/test_violet_exec_contracts.py. Pass criteria. All types construct + poison-reject (negative qty, non-finite price, etc.); QuirkProfile() defaults every quirk OFF; the enum lists exactly the 8 quirks above. No logic in bingx_quirks.py. No edits outside the new/extended files.

TASK 21 — Mock BingX order FSM

Why. The deterministic order lifecycle state machine — the heart of the mock. Affected files (NEW): prod/clean_arch/violet/exec/order_fsm.py, prod/clean_arch/violet/exec/test_violet_order_fsm.py. Interface/approach. OrderFSM holds per-order state; legal transitions only: NEW→ACK→{PARTIALLY_FILLED*→FILLED | CANCELED | REJECTED}. submit(order)->OrderAck, apply_fill(venue_order_id, fill_qty, fill_price)->(OrderStatus, Fill), cancel(...). Illegal transitions raise/reject (never silently). Reduce-only that would increase position → REJECTED (quirk #6, normative). Partial fills accumulate; FILLED only when cumulative == order qty. Monotonic event_seq. Pure/in-memory; deterministic (no wall clock — caller passes ts_ns). Pass criteria. Full-fill, multi-partial→fill, cancel-after-partial, reject (reduce-only increase), illegal-transition rejection; Hypothesis: cumulative fill ≤ order qty always; event_seq strictly increasing. No edits outside the 2 files.

TASK 22 — Mock BingX venue adapter (port-conformant, quirk-seamed)

Why. The venue itself: a MockBingxVenue implementing the execution-venue port so the router / driver can drive it exactly like the real one — but deterministic + DARK. Affected files (NEW): prod/clean_arch/violet/exec/mock_bingx_venue.py, prod/clean_arch/violet/exec/test_violet_mock_bingx_venue.py. Interface/approach. Define ExecutionVenuePort (ABC) if one isn't already agreed: set_leverage(asset, lev)->OrderAck (quirk #7 seam — separate ackable op), submit(order)->OrderAck, cancel(client_order_id)->OrderAck, poll_fills()->list[Fill], close(). Implement MockBingxVenue(fill_model, quirks: QuirkProfile = QuirkProfile()) over OrderFSM. Fill model is INJECTABLE (ScriptedFillModel for tests: immediate full / N-partials / reject / maker-vs-taker). Maker orders fill at limit price; taker at a provided reference (VenueTick.ask/bid). Fee = maker vs taker rate (read the router's rate conventions; pass rates in, don't hardcode a venue-specific number). Quirk hooks present but inert: when quirks.zero_wb/foreign_fill/settle_desync are False (default) behave normatively; the hook points exist for a later quirk-injection pass. Pass criteria. A ScriptedFillModel drives submit→ack→fill→FILLED; partial sequence; reject; maker vs taker fill price + fee differ correctly; set_leverage acks independently; with default QuirkProfile() NO quirk behaviour occurs. No live network. No edits outside the 2 files.

TASK 23 — Fill → position/PnL reducer (fill-price PnL, ×leverage-aware)

Why. Turn Fills into PositionDeltas correctly — the accounting seam where bound-price poison (#3) and ×leverage (#4) would bite. Feeds PASS-4 EconomicsLedger. Affected files (NEW): prod/clean_arch/violet/exec/fill_reducer.py, prod/clean_arch/violet/exec/test_violet_fill_reducer.py. Interface/approach. apply_fill(pos: OpenPositionView | None, fill: Fill) -> (OpenPositionView, PositionDelta). PnL uses fill.fill_price ONLY — never bound_price (assert/comment quirk #3). Notional/qty handled WITHOUT double-applying leverage (quirk #4 — document the convention explicitly). Realized PnL on reducing fills = signed (entryfill)×qty for SHORT, etc.; fees subtracted; event_seq carried through. Pure function. Pass criteria. Open, add, partial-reduce, full-close produce correct qty + realized PnL + fee; a test asserts that a fill with bound_price != fill_price yields PnL from fill_price; Hypothesis: qty ≥ 0; realized PnL finite. No edits outside the 2 files.

TASK 24 — Mock execution integration harness (router + driver + venue, DARK storm)

Why. Prove the whole stack composes: ExecIntent (PASS-4 Task 17) → router/maker-policy → ExecDeadlineDriver (V2) → MockBingxVenueFillfill_reducerPositionDeltaEconomicsLedger (PASS-4 Task 16). The end-to-end DARK proof. Affected files (NEW): prod/clean_arch/violet/exec/mock_exec_harness.py, prod/clean_arch/violet/exec/test_violet_mock_exec_scenarios.py, gate report → prod/VIOLET_dev/reports/violet_mock_exec_<UTC>.json. Interface/approach. Wire the components (READ exec_harness.py/exec_driver.py for the V2 storm pattern; reuse, don't fork). Run a seeded scenario matrix: clean entry→fill→exit; partial fills; cancel/requote on TTL; reject; maker_both policy. Assert: every order reaches a terminal state, capital == anchor + Σ deltas (ledger), NO order ever leaves the mock (DARK guard), and the run is deterministic (same seed ⇒ same fills/deltas twice). Pass criteria (@pytest.mark.gate). ≥ 8 scenarios green; determinism proven; ObserveOnly/DARK guard asserts zero real-venue calls; report archived. No edits outside the new files.


Composition map

contracts_v3 (+Order/OrderAck/Fill/OrderStatus/PositionDelta) + exec/bingx_quirks(QuirkProfile)
   20 contracts + quirk seams
   21 order_fsm                → legal lifecycle, partials, reject
   22 mock_bingx_venue(fsm, fill_model, quirks=OFF) → OrderAck + Fill stream   (quirk hooks inert)
   23 fill_reducer(pos, fill)  → (OpenPositionView, PositionDelta)             (fill-price PnL)
   24 mock_exec_harness: ExecIntent → router/driver → 22 → 23 → PASS-4 EconomicsLedger  (DARK E2E)

Integration into the live reactor + the real BingX client swap is the OWNER's job later. The quirk-injection pass (turning QuirkProfile flags ON to test reconcile/filter logic) and the mandatory real-key boundary smoke are SEPARATE, LATER work — NOT PASS 5.

20 (contracts+quirk seams) → 21 (FSM) → 23 (fill reducer) → 22 (venue) → 24 (harness/gate).

Still NOT in scope (operator/owner only, or a later pass)

  • Quirk LOGIC / reconcile-against-quirks — later quirk-injection pass (seams only here).
  • Real BingX client + real-key smoke — mandatory before V4-live; operator-gated.
  • DARK soak start; HZ-bridge refactor; VIBRISS; the V3.4c parity root-cause (CRITICAL #1).
  • Any edit to the in-flight files / shared files in §0.