Skip to content

dob: scale wire qty fields to per-instrument qty_exponent#5

Merged
armcconnell merged 1 commit into
mainfrom
fix/dob-qty-exponent
Apr 28, 2026
Merged

dob: scale wire qty fields to per-instrument qty_exponent#5
armcconnell merged 1 commit into
mainfrom
fix/dob-qty-exponent

Conversation

@armcconnell
Copy link
Copy Markdown
Collaborator

Summary of Changes

The DoB emitter was writing OrderAdd.quantity, OrderExecute.exec_quantity, and SnapshotOrder.quantity at the publisher's internal 10^8 fixed-point (Sz::value()) while the accompanying InstrumentDefinition reported the venue's per-instrument qty_exponent. Subscribers decode display = raw * 10^qty_exponent per spec, so the on-wire error was 10^(8 + qty_exponent)x for every instrument with qty_exponent != -8 — i.e. every HL instrument. TOB was correct because it parsed the original string with qty_to_fixed(level.sz(), inst.qty_exponent); this PR makes DoB do the equivalent.

  • order_book::sz_to_fixed: helper that converts an internal Sz (qty * 1e8) to the wire u64 for a given qty_exponent. Lossless for HL: venue qty is always a multiple of 10^qty_exponent, so the divisor 10^(8 + qty_exponent) divides exactly.
  • dob_tap: CoinResolver widened to Fn(&Coin) -> Option<(u32, i8)> so the apply tap can scale per-instrument. Applied at OrderAdd.quantity and OrderExecute.exec_quantity emission.
  • multicast/dob: emit_snapshot takes qty_exponent and applies the same scaling at SnapshotOrder.quantity. run_dob_snapshot_task threads qty_exponent through the priority and round-robin paths from the registry.
  • websocket_server: construction-site resolver returns qty_exponent alongside instrument_id from the registry's active map.

Testing Verification

  • cargo test --workspace: 119/119 lib + tests pass on the branch (the single pre-existing test_trade_listener failure reproduces on main un-modified and is unrelated). cargo build --release succeeds. cargo clippy --lib --tests --no-deps produces no errors.
  • New unit tests in dob_tap.rs::tests assert OrderAdd.quantity and OrderExecute.exec_quantity scale correctly at qty_exponent = -3 and qty_exponent = 0 (the integer-only case from the live observation).
  • New multicast::dob::snapshot_qty_scaling_tests decode the actual SnapshotOrder bytes off a UDP loopback at qty_exponent = -3 and qty_exponent = 0 and assert the wire quantity field matches the venue scale.
  • parity_tests.rs end-state TOB-vs-DoB scenario factored to take qty_exponent and instantiated at -8, -3, and 0. The -3 and 0 variants would have failed pre-fix with dob_qty / tob_qty == 10^(8 + qty_exponent), exactly matching the symptom from the field.
  • Deployed to aws-tyo-hl-mainnet via the ansible role with dz_hl_publisher_branch=fix/dob-qty-exponent; service restarted, publisher healthy, on-wire output verified as expected.

Refs: hyperliquid hyperliquid-dex#10

The DoB emitter was writing OrderAdd.quantity, OrderExecute.exec_quantity,
and SnapshotOrder.quantity at the publisher's internal 10^8 fixed-point
(Sz::value()) while the accompanying InstrumentDefinition reported the
venue's per-instrument qty_exponent. Subscribers decode display = raw *
10^qty_exponent, so the on-wire error was 10^(8 + qty_exponent)x for every
instrument with qty_exponent != -8 — i.e. every HL instrument.

TOB scaled correctly because it parsed level.sz() as a string with
qty_to_fixed(level.sz(), inst.qty_exponent). DoB emitted Sz::value()
directly. Fix scales DoB qty fields the same way.

- order_book::sz_to_fixed: helper that converts an internal Sz (qty * 1e8)
  to the wire u64 for a given qty_exponent. Lossless for HL: venue qty is
  always a multiple of 10^qty_exponent so the divisor 10^(8+qty_exponent)
  divides exactly.
- dob_tap: CoinResolver widened to Fn(&Coin) -> Option<(u32, i8)> so the
  apply tap can scale per-instrument. Applied at OrderAdd.quantity and
  OrderExecute.exec_quantity emission.
- multicast/dob: emit_snapshot takes qty_exponent and applies the same
  scaling at SnapshotOrder.quantity. run_dob_snapshot_task threads
  qty_exponent through the priority and round-robin paths from registry.
- websocket_server: construction-site resolver returns qty_exponent
  alongside instrument_id from the registry's active map.

Tests:
- dob_tap unit tests: scaling assertions at qty_exponent = -3 and 0.
- multicast/dob: snapshot_qty_scaling_tests verify SnapshotOrder.quantity
  on the wire at qty_exponent = -3 and 0.
- parity_tests: TOB-DoB end-state parity at qty_exponent = -8, -3, and 0.
  Without the fix the -3 and 0 variants fail with dob_qty / tob_qty ==
  10^(8 + qty_exponent), which is the exact symptom from the field.

Refs: hyperliquid#10
@armcconnell armcconnell merged commit c293030 into main Apr 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant