242 lines
23 KiB
Markdown
242 lines
23 KiB
Markdown
|
|
# JEV‑Trader vs FLIGHT13 SOA — Execution‑Flow Comparison
|
|||
|
|
|
|||
|
|
**Read‑only study. No files in the production trees, CH, or HZ were altered. New doc only
|
|||
|
|
(`/mnt/dolphinng5_predict/prod/docs/`).**
|
|||
|
|
|
|||
|
|
**Sources (all read‑only):** jev‑trader `src/{trader,market,trades,book,chain,model,server}.ts`
|
|||
|
|
(`b587759e` `main`); FLIGHT13 r29 formal spec
|
|||
|
|
`/root/uv-wt/f13-r29/prod/docs/formal/F13_ExecLifecycle_SOA_v5.tla` (`Next` + all transitions);
|
|||
|
|
`/root/uv-wt/f13-r29/prod/docs/F13_EXECUTION_FLOW_DIAGRAM.md`;
|
|||
|
|
`/root/uv-wt/f13-r29/prod/docs/VIOLET_PASS2.4_LIVE_EXEC_WIRING.md`;
|
|||
|
|
`/root/uv-wt/f13-r29/prod/docs/EXEC_SOA_SUPERSESSION_AUDIT_20260808.md`;
|
|||
|
|
`/root/uv-wt/f13-r29/prod/clean_arch/dita_v2/{hl_venue,exec_router}.py`; and
|
|||
|
|
`/mnt/dolphinng5_predict/prod/docs/DITA_V2_KERNEL_REFERENCE.md`.
|
|||
|
|
|
|||
|
|
**Operator premise (accepted):** “Our state machine is tad more complete… the `.ts` execs
|
|||
|
|
against the book is worth taking in/studying.” This doc agrees the FLIGHT13 FSM is strictly
|
|||
|
|
more complete (it must be — it is built for Hyperliquid, where ack‑or‑not / partial / network
|
|||
|
|
hell is the baseline), and isolates **exactly what jev‑trader does *against the book* that is
|
|||
|
|
worth porting**: the post‑only‑inside‑the‑touch, replace‑every‑block maker‑capture policy and its
|
|||
|
|
serialized one‑send‑per‑cadence gate.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 0. TL;DR
|
|||
|
|
|
|||
|
|
| dimension | jev‑trader | FLIGHT13 DITAv3.00 / ASEx | verdict for an HL port |
|
|||
|
|
|---|---|---|---|
|
|||
|
|
| **order model** | 1 resting order / asset; serialized replaces | multi‑slot (`MaxSlots`); maker chase ladder + standing TP | jev’s *policy* is a special case FLIGHT already hosts |
|
|||
|
|
| **exec against the book** | post‑only, 1 tick inside the touch, **cancel‑prev + place‑new every block** (atomic from the bot’s view) | post‑only at the touch (`TOUCH_ALO`); **atomic modify to touch** (same cloid, fee‑free, ≤1/bar) | jev ≈ FLIGHT’s maker chase w/ `offset_ticks=1`; drop‑in not viable — needs the hl_venue adapter |
|
|||
|
|
| **ack model** | tx‑receipt = placed/reverted; `lost` after 10 blocks; assumes receipt == truth | `VenueAccept / VenueReject / VenueIndeterminate`; **submit exception → INDETERMINATE unless provenance proves NOT_ATTEMPTED; venue‑truth polls the OBJECT**; `UNKNOWN is never FLAT` | FLIGHT strictly stronger — jev’s 10‑block timeout is the only recovery |
|
|||
|
|
| **partial fills** | taker hits resting order; size shrinks; **re‑quoted next block** (no remainder re‑quote within a bar) | `PartialFill` → `pendingFill`; `IocExpire` residual handled by Add.13 `TakeResidualAbandon`; IOC residual inherits the live TAKE obligation | FLIGHT keeps the residual; jev just re‑quotes |
|
|||
|
|
| **exit** | none — side flip → new opposite quote naturally closes | `TP_FLOOR → TP → SL → MAX_HOLD → V7(retract ½) → ADVSL(shadow)`; reduce‑only, side‑flipped, MUST_FILL; MAX_HOLD monotonic latch | jev has *no* exit authority — the port must add FLIGHT’s exit policy layer |
|
|||
|
|
| **venue** | Kuru AMM on Monad (eth_call book, eth_sendRaw batchUpdate, Trade‑log fills) | Hyperliquid VST (off‑chain OB, REST/WS, batchOrders, postOnly/reduceOnly, cloid nonce) | plumbing differs; policy transfers |
|
|||
|
|
| **hot cadence** | ~300 ms block; 2 RPC round trips (1 eth_call book + 1 send) | scan bar ~5–6 s decision; BLUE bar 11 s; chase 33 s; rest ceiling 132 s | jev’s 300 ms cadence is **not** portable to HL‑VST timing — map the *policy*, not the clock |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 1. Two state machines, side by side
|
|||
|
|
|
|||
|
|
### 1.1 jev‑trader — single‑slot, replace‑every‑block (one in‑flight)
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
IDLE(busy=false, block t)
|
|||
|
|
│ confirmPending(t): poll receipts for prior inflight → placed|reverted|lost
|
|||
|
|
│ (placed → this.orders[orderId] = {side,price,size,block} ← next cancel list)
|
|||
|
|
│ (fills: trades.poll(t).then(harvest) → applyFill → position FIFO realized)
|
|||
|
|
│
|
|||
|
|
├─ IF busy (prev read+decide+send still running) ──► emit(lateness, decision.hold) ──► IDLE (no order)
|
|||
|
|
│
|
|||
|
|
│ readBook() — 1 eth_call getL2Book (+vault batched); throws on empty side
|
|||
|
|
│ decision = model.decide(state) // the question: ↑/↓ after horizonBlocks; {buy,sell,hold}+upIn10
|
|||
|
|
│ wanted = (decision.action==sell)? sell : buy // buy wins ties
|
|||
|
|
│ side = allowed(wanted)? wanted : allowed(other)? other : null // cap(5×) + margin(live)
|
|||
|
|
│
|
|||
|
|
├─ IF side==null ──► emit(block, book, decision, null, false, capped=true) ──► IDLE (no order)
|
|||
|
|
│
|
|||
|
|
│ cancel = [...this.orders.keys].filter(id>0) // confirmed resting ids ONLY
|
|||
|
|
│ market.send(t, side, 200 MON, book, cancel, capped) // ONE batchUpdate: cancel[] + place 1 post‑only
|
|||
|
|
│ → sign → eth_sendRawTransaction → hash (status=SENT, inflight[hash]=quote, gasMon charged)
|
|||
|
|
│ → DARK : status=sim, orders.clear()+orders.set(-simId)
|
|||
|
|
└─ emit(BlockEvent{snapshot,history/events,block,quote}) ──► IDLE(t+1)
|
|||
|
|
```
|
|||
|
|
**There is no EXIT state.** A model side‑flip (long→sell) cancels the bid and posts a one‑tick‑inside **ask**; the next taker print against that ask closes the position. The FSM’s only “memory” across blocks is `orders` (confirmed resting), `inflight` (unacked txs), and `position` (FIFO cost basis).
|
|||
|
|
|
|||
|
|
State inventory (`trader.ts`): `busy`, `orders:Map(id,Resting)`, `inflight:Map(hash,Quote)`,
|
|||
|
|
`position:{mon,costUsd}`, `totals`, `lastBook`, plus per‑block RPC `feeWei`/`gasLimit`.
|
|||
|
|
|
|||
|
|
### 1.2 FLIGHT13 DITAv3.00/ASEx — multi‑slot FSM with reconciliation (formal TLA+)
|
|||
|
|
|
|||
|
|
**Order‑lifecycle FSM (TLA `OrderStates`):**
|
|||
|
|
`NONE → RESERVED → QUEUED → {INDETERMINATE | LIVE} → {PARTIAL | CANCEL_PENDING | FILLED | CANCELLED | REJECTED}`
|
|||
|
|
(plus `pendingFill`, `linkedOwner`, `venueOwner` per object).
|
|||
|
|
|
|||
|
|
**Per‑trade FSM (`entryPhase`):**
|
|||
|
|
`NONE → SELECT_ENTRY_ARM(REST|CHASE) → ReserveInitialMaker(ENTRY_MAKER, TOUCH_ALO, RESERVED)`
|
|||
|
|
→ `QueueSubmit` (`QUEUED`, `parityHeld`) → `VenueAccept` (`LIVE`, `venueOwner`) / `VenueReject` (`REJECTED`) / `VenueIndeterminate` (`INDETERMINATE`)
|
|||
|
|
→ on `LIVE`: `EntryHold | EntryReprice` (atomic modify‑to‑touch, CHASE only) | `EntryTerminalLive/Dark`
|
|||
|
|
→ `EntryEconomicAbandon` (explicit policy verdict, book usable) OR `ReserveEntryTake` (BOUNDED IOC, MUST_FILL) → Add.13 `TakeResidualAbandon`
|
|||
|
|
→ `EntryFillOne/DeliverOne` (`kernelPos++`, `undeliveredEntry++`, entry `COMPLETE` or `ABORT_REQUIRED`)
|
|||
|
|
→ `POSITION_OPEN` → [`ArmProtection` → `STANDING_TP`] / [exit]
|
|||
|
|
→ `RaiseExitSignal` → `SelectExitIntent` (same‑tick `KILL>TP_FLOOR>FIXED_TP>STOP_LOSS>ADVSL>MAX_HOLD`; urgency `KILL/SL>TP_FLOOR/ADVSL>FIXED_TP>MAX_HOLD`) → `ReserveExitTake` (`EXIT_TAKE`, MUST_FILL, reduce‑only, side‑flipped) → `ExitFillOne` (`kernelPos--`, realized) → `ClearExitWhenFlat` → `FLAT`.
|
|||
|
|
→ recovery at any tick: `STALE_STATE_RECONCILING` blocks progression until venue‑truth reconciled;
|
|||
|
|
`VenueSnapshot/VenueTruthStale/BookTruthStale` + `VenueFillOne/DeliverOne` + `RetireOrder`.
|
|||
|
|
|
|||
|
|
**Exit is terminal & monotonic:** `MAX_HOLD` deadline latches (`maxHoldFired`, never cleared); `ExitCostRefuse` (cost‑ceiling veto) is legal **only** before the deadline — at terminal it is forced (`TerminalCeilingVetoBug` is a defect): cross with full telemetry. **Invariant `LIV_ExitEventuallyFlat`** (with the cost‑veto + MAX_HOLD latch) is the liveness tooth.
|
|||
|
|
|
|||
|
|
**Foreign‑exposure path (the orphan class jev cannot express):** `ForeignFill(s,side)` → `foreignExposure++` (a fill for *our* intent‑id that the kernel did not originate — i.e. a stale cloid collision or a foreign place). This arms `ReserveFlatten` (`FLATTEN`, MUST_FILL) whose `quantity = foreignExposure`, side‑flipped, reduce‑only — a **forced flatten whose obligation persists through reject/partial/unknown until flat**. (`NoFlattenerBug` would drop this to `unownedSeen` — a defect.)
|
|||
|
|
|
|||
|
|
**Invariant teeth (TLA+ `Invs`):** `UNKNOWN is never FLAT`; `cancel REQUEST is never cancel TRUTH`;
|
|||
|
|
`one trade may own several simultaneous physical orders`; `pending/self acquisition consumes parity
|
|||
|
|
before venue truth catches up`; the `Bug`-prefixed switches are **mutation‑litmus defect flags**
|
|||
|
|
(`FillBeforeLinkDropsBug`, `CancelRequestTerminalBug`, `ExitNotReduceOnlyBug`, `WrongSideExitBug`,
|
|||
|
|
`OptimisticResizeBug`, …) — TLC proves each fires only under its bug gate.
|
|||
|
|
|
|||
|
|
### 1.3 The structural gap in one line
|
|||
|
|
jev: **`one block ≈ one send ≈ one resting order ≈ book is replaced, not reconciled.** FLIGHT:
|
|||
|
|
**`the venue owns the truth; the kernel owns the book; the gap between them is where every
|
|||
|
|
defect lives and is exhaustively modeled.** jev’s `lost` (no receipt in 10 blocks) is the *only*
|
|||
|
|
reconciliation jev has. FLIGHT has `STALE_STATE_RECONCILING`, venue‑truth polls, foreign‑recovery,
|
|||
|
|
standing TPs, MAX_HOLD latches, and a 30‑item defect‑flag lattice over exactly that gap.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 2. How each execs against the book (the user’s focus)
|
|||
|
|
|
|||
|
|
### 2.1 jev‑trader — "replace‑every‑block, one tick inside, post‑only by construction"
|
|||
|
|
|
|||
|
|
**Against the book, per block (`trader.onBlock`):**
|
|||
|
|
1. `readBook()` → one `eth_call getL2Book`; the entire resting‑order price is recomputed from the
|
|||
|
|
**current** book each block. There is no "keep the old level"; the old level is cancelled and a
|
|||
|
|
new one is placed.
|
|||
|
|
2. `quotePrice` (`market.ts:121`) → **`bid + 1 tick` for buy, `ask − 1 tick` for sell**, clamped to the
|
|||
|
|
touch when the spread is thinner than a tick. The clamp is the **never‑cross** guarantee: the
|
|||
|
|
order always rests at or inside the touch on the model’s side.
|
|||
|
|
3. `batchUpdate(buyPrices,buySizes,sellPrices,sellSizes,cancelIds, **postOnly=true**)`
|
|||
|
|
(`market.ts:185`) — the cancel list (last block’s confirmed resting `orderId`s) **and** the new
|
|||
|
|
post‑only quote are sent **in one Kuru tx**. From the book’s perspective the quote hops every
|
|||
|
|
block with no gap: the moment the new order is placed, the old one is cancelled in the same
|
|||
|
|
atomic batch. (It is not a true on‑chain modify; it is cancel‑prev + place‑new. FLIGHT does
|
|||
|
|
a same‑cloid atomic modify instead — see §3.)
|
|||
|
|
4. The resting order therefore **sits one tick inside the touch** and waits for the next taker
|
|||
|
|
print that crosses the spread. A taker `sell` fills our `buy` at `bid+1tick`; a taker `buy`
|
|||
|
|
fills our `sell` at `ask−1tick`. We are, by construction, **the counterparty the taker hits**,
|
|||
|
|
earning the tick plus the spread‑capture edge — exactly “be the one whose order flow gets
|
|||
|
|
picked up.”
|
|||
|
|
5. Because `tradeSizeMon` is **fixed at 200 MON** and `quoteInsideTicks=1` is **fixed**, the exec is
|
|||
|
|
size‑ and distance‑blind to depth. The model sees `depthBps{10/25/50}` and `imbalance` but the
|
|||
|
|
placement ignores them — jev earns edge only so long as it (a) is first to the touch each block
|
|||
|
|
and (b) the book is wide enough that one tick is inside it.
|
|||
|
|
|
|||
|
|
**Net:** jev’s book‑exec is a **deterministic, stateless, post‑only hopping** of a single order one
|
|||
|
|
tick inside the touch, every ~300 ms. It wins or loses the spread every block; there is no
|
|||
|
|
reprice‑to‑touch ladder, no venue‑truth, no partial remainder. The policy is attractive because
|
|||
|
|
it is *obviously correct* about not crossing.
|
|||
|
|
|
|||
|
|
### 2.2 FLIGHT13 — "maker chase ladder + atomic modify + venue‑truth"
|
|||
|
|
|
|||
|
|
**Against the book, per the timeline + TLA (`F13_EXECUTION_FLOW_DIAGRAM.md`):**
|
|||
|
|
1. **t=0 ExecCtl urgency stamp** → `high(do‑or‑die)` = `MARKET Ioc` (taker, 4.5 bp measured);
|
|||
|
|
`low(maker‑first)` = `LIMIT Alo + TTL` (maker, 1.5 bp). Order type is chosen **once**, at
|
|||
|
|
submit, by the control plane — not by a per‑bar re‑quote.
|
|||
|
|
2. `QueueSubmit` → `VenueAccept` (`LIVE`, `venueOwner=o`, `cloid=sha256(intent_id)` journaled as a
|
|||
|
|
`(cloid, intent_id)` pair — see `hl_venue.py` `_cloid_for` / `_remember_cloid`). The kernel
|
|||
|
|
**does not** re‑quote every bar by default.
|
|||
|
|
3. **Chase ladder** (`EntryReprice`, bars = `wall/11s`): bar 0–1 → **atomic modify to the touch**
|
|||
|
|
(same cloid, ≤1 bar, **fee‑free**, owner‑side sizing remainder, `join bid — can never cross`);
|
|||
|
|
measured edge starts ~6 bp, concave decays; bar 2, edge ≤ 2 bp floor → **`ABANDON` →
|
|||
|
|
`expire_now` → same‑sweep `CANCEL`**. So FLIGHT *does* re‑price toward the touch — but only
|
|||
|
|
for the CHASE arm, and it does it with a **same‑cloid atomic modify** (fee‑free), not a
|
|||
|
|
cancel‑+‑place.
|
|||
|
|
4. **Modify‑error taxonomy** (`hl_venue._events_from_modify`→`_modify_error_class`): a failed
|
|||
|
|
reprice is **resolved, never guessed**:
|
|||
|
|
- `old_order_intact` → quote untouched, retry the reprice;
|
|||
|
|
- `old_order_consumed` → cancel half succeeded / place failed → quote gone unfilled, slot freed
|
|||
|
|
(no double‑fill: atomicity holds; presence does not);
|
|||
|
|
- `selector_gone` → order already gone before the modify (filled/cancelled) → **decide via
|
|||
|
|
venue‑truth**, never by assumption.
|
|||
|
|
5. **Venue‑truth on stuck** (`VENUE‑TRUTH read`, 60 s cadence, query the *object* not the action):
|
|||
|
|
`VenueSnapshot` — GONE → slot freed; FILLED → feed‑gap alarm + `fill path owns position`;
|
|||
|
|
`RESTING < ceiling` → re‑cancel; `RESTING ≥ 132 s` → **HALT new ENTERs** (loud alarm);
|
|||
|
|
`UNKNOWN` → ask again. Submit exception → `_submit_exc_proves_no_order` →
|
|||
|
|
`NOT_ATTEMPTED` (ConnectError/NameResolution/SSLError/timeout‑before‑send) ⇒ safe REJECTED;
|
|||
|
|
**anything else ⇒ `VenueIndeterminateError` (INDETERMINATE)** ⇒ the order MAY exist and is
|
|||
|
|
**never rolled back to flat at the book layer** (`UNKNOWN is never FLAT`).
|
|||
|
|
6. **Fills** (`FillOne`/`DeliverOne`, `VenueFillOne`): taker lifts the maker → `actualPos++` +
|
|||
|
|
`pendingFill++`; **self‑acquisition consumes parity before venue truth catches up**; the
|
|||
|
|
fill is delivered into the book (`delivered < filled` → pending). `STANDING_TP` is registered
|
|||
|
|
when `protection == ARMED`.
|
|||
|
|
7. **Exit** (`ReserveExitTake`, MUST_FILL, reduce‑only, side‑flipped; `SMART_EXIT` maker LIMIT
|
|||
|
|
+ TTL → `sweep CANCEL → confirm/retry → MARKET cross EXACTLY ONCE`), arbitrated
|
|||
|
|
`KILL>TP_FLOOR>FIXED_TP>STOP_LOSS>ADVSL>MAX_HOLD` same‑tick, MAX_HOLD a monotonic latch.
|
|||
|
|
8. **Single‑writer** (VIOLET_PASS2.4): `ShadowDecision → ExecIntent → ExecDeadlineDriver@100ms → VST
|
|||
|
|
adapter`; fills fold back into the **ASEx‑guarded working‑order + position/capital ledger
|
|||
|
|
(single writer — no race with the decision loop’s reads)**. The race test (“decision‑loop reads
|
|||
|
|
vs fill‑apply writes through ASEx”) is a named self‑test.
|
|||
|
|
|
|||
|
|
**Net:** FLIGHT’s book‑exec is a **chase ladder that modifies‑to‑the‑touch for cheap**, keeps the
|
|||
|
|
quote alive across bars, resolves every failure by *asking the venue about the object*, and never
|
|||
|
|
rolls back a possibly‑live order. jev does the opposite: it *re‑places* the quote every bar and
|
|||
|
|
only ever recovers via a 10‑block receipt timeout.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 3. Comparison matrix (exec‑against‑book focus)
|
|||
|
|
|
|||
|
|
| axis | jev‑trader | FLIGHT13 DITAv3.00 / ASEx (VST/HL) | delta |
|
|||
|
|
|---|---|---|---|
|
|||
|
|
| **quote distance from touch** | fixed 1 tick inside (`QUOTE_INSIDE_TICKS`) | TOUCH / touch + chase‑modify; maker offset configurable (`offset_ticks`) | jev’s “1 tick in” = FLIGHT maker offset=1 |
|
|||
|
|
| **how the quote moves** | **cancel‑prev + place‑new every block** (fresh oid each block) | **atomic modify to touch**, same cloid, fee‑free (`EntryReprice`) | jev pays 2 txs/round (cancel+place); FLIGHT 1 modify — FLIGHT cheaper |
|
|||
|
|
| **post‑only guarantee** | `quotePrice` clamp **×** `batchUpdate(postOnly=true)` | `postOnly` flag + TOUCH_ALO (Kuru/HL) | FLIGHT also rejects‑and‑reprice on cross |
|
|||
|
|
| **order‑state knowledge** | receipt `OrderCreated`/`OrdersCanceled` only | `VenueStatus{NEW/ACKED/PARTIAL_FILLED/FILLED/CANCELED/REJECTED/RATE_LIMITED/CANCELED_REJECTED}` + `VenueIndeterminate` | FLIGHT has partial + rate‑limit states jev lacks |
|
|||
|
|
| **ack‑or‑not** | receipt mined ⇒ placed; timeout 10 blocks ⇒ `lost`; else assumed | submit exception ⇒ `VenueIndeterminate` unless `NOT_ATTEMPTED` proven; **venue‑truth polls the OBJECT** | FLIGHT strictly stronger; jev’s `lost` is the only backstop |
|
|||
|
|
| **partial fills** | taker fills resting → size shrinks → **re‑quoted next block** | `PartialFill` → held as `pendingFill`; `IocExpire` residual handled by Add.13 (`TakeResidualAbandon`) | FLIGHT preserves the residual obligation; jev just re‑quotes |
|
|||
|
|
| **orphan / foreign fill** | none modelable | `ForeignFill` (our cloid, not our intent) → `FLATTEN` MUST_FILL forced‑close | jev has no defense; this is the HL “shared‑key contamination” risk |
|
|||
|
|
| **reconciliation cadence** | receipts every block off‑path; Trade‑log every block | venue‑truth 60 s; max 8 cancel retries; rest ceiling 132 s; feed‑gap alarm | FLIGHT survives minutes‑of‑darkness; jev survives ~3 s |
|
|||
|
|
| **stuck‑order policy** | timeout 10 blocks ⇒ `lost`, resync nonce | venue‑truth GONE ⇒ free; RESTING ≥132 s ⇒ HALT ENTERs; UNKNOWN ⇒ ask again | FLIGHT escalates to HALT; jev silently gives up |
|
|||
|
|
| **exit (close)** | none — side flip posts opposite‑touch quote | `TP_FLOOR>TP>SL>MAX_HOLD>V7½>ADVSL` arbitrated same‑tick; MAX_HOLD monotonic; cost‑ceiling advisory at terminal | jev **has no exit authority at all** |
|
|||
|
|
| **concurrency** | 1 asset (MON), 1 order | multi‑asset, `MaxSlots`, one trade may own several simultaneous physical orders | jev is the degenerate single‑slot case |
|
|||
|
|
| **single‑writer** | single event‑loop process | ASEx single‑writer lane (P0‑P4 queue); fill‑apply vs read race‑free by design | FLIGHT is concurrency‑safe by construction |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 4. Mapping jev’s exec onto FLIGHT/HL (concrete port‑under‑HL recommendation)
|
|||
|
|
|
|||
|
|
jev‑trader is **not a drop‑in** under `nautilius` (the exec driver) — it is Kuru/Monad‑coupled.
|
|||
|
|
But its **book‑placement policy** maps onto an existing FLIGHT surface:
|
|||
|
|
|
|||
|
|
| jev concept | FLIGHT/HL realization | action |
|
|||
|
|
|---|---|---|
|
|||
|
|
| post‑only 1‑tick‑inside the touch | `exec_router.plan_entry` maker mode + `maker_price(asset,order_side,ref)` + `post_only=True` + `offset_ticks=1` + `max_spread_bps` gate | **already exists** — set `DOLPHIN_PINK_EXEC_STYLE=maker_entry`, `DOLPHIN_PINK_POST_ONLY=1`, `DOLPHIN_PINK_MAKER_OFFSET_TICKS=1` |
|
|||
|
|
| cancel‑prev + place‑new every block | `EntryReprice` atomic modify to touch (CHASE arm) | **replace jev’s cancel+place with FLIGHT’s atomic modify** — cheaper, fee‑free; jev’s replace‑every‑block cadence must be expressed as the chase ladder’s reprice ticks |
|
|||
|
|
| one send / block cadence (busy gate) | ASEx single‑writer lane P0 + scan‑bar cadence | do **not** drive 300 ms; map to scan‑bar (5–6 s) so the kernel’s book truth and the HL rate governor stay coherent |
|
|||
|
|
| model.side ∈ {buy,sell} + upIn10 | `Intent.action` + `ExecControl.urgency` stamp + FLIGHT brain `upIn10` field | wire jev‑as‑oracle behind the existing `Intent` producer; **do not** let jev touch the book |
|
|||
|
|
| position cap (maxPositionMon 1000) | ExecCtl governor / `MaxSlots` + capital‑fresh gate | reuse the governor; jev’s 5× cap is a special case of FLIGHT’s slot + capital gate |
|
|||
|
|
| fills = taker hits our resting order | HL `userFills` WS (maker) + `HlFillAdmission` dedup on `tid`; venue size wins (L10) | **hl_venue already owns this** — the fill path is the part that does NOT transfer from jev |
|
|||
|
|
| no TP/SL/exit | FLIGHT `EXIT_*` policy (`ReserveExitTake`, MUST_FILL, reduce‑only, side‑flipped) + MAX_HOLD latch | **add FLIGHT’s exit authority** — this is jev’s single biggest missing piece for an HL port. jev’s “side flip closes naturally” is **unsafe on VST** (no forced flatten; orphan/foreign fills exist). |
|
|||
|
|
| gas = static ceiling | HL: gas free (off‑chain); HL nonce per‑order, USD fee | drop the whole jev gas model — HL is gasless, but **HL nonce + budget (1 req/$1 USDC traded)** must be honored by the adapter |
|
|||
|
|
|
|||
|
|
### Recommendation (ranked)
|
|||
|
|
|
|||
|
|
1. **Port the policy, not the plumbing.** Jev’s attractive thing is *“post one‑tick‑inside, replace toward the touch, post‑only always, one order per cadence.”* That is **FLIGHT maker_entry maker_exit with chase** — already present in `exec_router.py` + `hl_venue.py`. Do not ship jev’s `batchUpdate`/Trade‑log/gasLimit/nonce code.
|
|||
|
|
|
|||
|
|
2. **Graft jev‑as‑oracle onto the Intent producer.** `model.ts` exposes a clean oracle contract (`{buy,sell} + upIn10`). Wire it behind the existing `ExecIntent → ExecDeadlineDriver@100ms → VST adapter` (VIOLET_PASS2.4 wiring). The oracle must **not** know it posts orders.
|
|||
|
|
|
|||
|
|
3. **Replace jev’s “cancel+place every block” with FLIGHT’s atomic modify‑to‑touch.** jev pays a cancel gas‑ish on Kuru every 300 ms; on HL the modify‑to‑touch is fee‑free and is the whole point of the chase ladder. Map jev’s cadence to scan‑bar so the book truth / rate governor / `repriceSafe` leash stay coherent.
|
|||
|
|
|
|||
|
|
4. **Add FLIGHT’s exit authority — jev has none.** A pure side‑flip exit is **not safe on HL/VST** (no orphan/foreign recovery, no MAX_HOLD latch, no reduce‑only enforce). Use `ReserveExitTake` (MUST_FILL, reduce‑only, side‑flipped) + the same‑tick arbitration
|
|||
|
|
`KILL>TP_FLOOR>FIXED_TP>STOP_LOSS>ADVSL>MAX_HOLD`. This is the non‑negotiable delta.
|
|||
|
|
|
|||
|
|
5. **Inherit the ack/partial/venue‑truth stack from hl_venue.** jev’s 10‑block receipt timeout is the only recovery and it assumes “receipt == truth.” Under HL, `_submit_exc_proves_no_order` ⇒ INDETERMINATE‑unless‑NOT‑ATTEMPTED, and venue‑truth polls the OBJECT — port the *policy* but keep FLIGHT’s ack/partial/foreign/FLATTEN recovery verbatim. This is the “tad more complete” part the operator flagged.
|
|||
|
|
|
|||
|
|
6. **Stay gasless, honor the HL nonce + budget.** jev’s `MAX_FEE_GWEI`/`gasLimitFallback` are pure noise for HL. HL charges per signed action against a **1‑request/$1 USDC‑traded bucket** — `HlVenueAdapter.__init__` already maintains the `_refill_seen`/`_refill_order` ledger with the #59 fix (dedup on venue `tid`, refill via `record_trade_volume`). jev’s fixed‑size 200‑MON orders map cleanly onto that bucket.
|
|||
|
|
|
|||
|
|
**Bottom line:** jev‑trader’s book‑exec *policy* (post‑only 1‑tick‑inside, replace‑every‑cadence,
|
|||
|
|
single‑in‑flight gate) is a **special case** of the FLIGHT13 maker chase ladder already wired through
|
|||
|
|
`hl_venue.py` + `exec_router.py`. The port is an **adapter‑layer concern** (map jev’s two RPC
|
|||
|
|
round trips onto HL REST/WS batchOrders + userOrders/fills + cloid), **not** a drop‑in, and it
|
|||
|
|
**must** import FLIGHT’s exit authority (jev has none) and ack/partial/venue‑truth recovery
|
|||
|
|
(jev’s 10‑block timeout is insufficient on VST). The `.ts` is attractive because it is *obvious*;
|
|||
|
|
FLIGHT is more complete precisely because the obvious version does not survive HL’s
|
|||
|
|
ack‑or‑not / partial / orphan / network‑partition regime.
|