dob: scale wire qty fields to per-instrument qty_exponent#5
Merged
Conversation
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary of Changes
The DoB emitter was writing
OrderAdd.quantity,OrderExecute.exec_quantity, andSnapshotOrder.quantityat the publisher's internal 10^8 fixed-point (Sz::value()) while the accompanyingInstrumentDefinitionreported the venue's per-instrumentqty_exponent. Subscribers decodedisplay = raw * 10^qty_exponentper spec, so the on-wire error was10^(8 + qty_exponent)xfor every instrument withqty_exponent != -8— i.e. every HL instrument. TOB was correct because it parsed the original string withqty_to_fixed(level.sz(), inst.qty_exponent); this PR makes DoB do the equivalent.order_book::sz_to_fixed: helper that converts an internalSz(qty * 1e8) to the wireu64for a givenqty_exponent. Lossless for HL: venue qty is always a multiple of10^qty_exponent, so the divisor10^(8 + qty_exponent)divides exactly.dob_tap:CoinResolverwidened toFn(&Coin) -> Option<(u32, i8)>so the apply tap can scale per-instrument. Applied atOrderAdd.quantityandOrderExecute.exec_quantityemission.multicast/dob:emit_snapshottakesqty_exponentand applies the same scaling atSnapshotOrder.quantity.run_dob_snapshot_taskthreadsqty_exponentthrough the priority and round-robin paths from the registry.websocket_server: construction-site resolver returnsqty_exponentalongsideinstrument_idfrom the registry's active map.Testing Verification
cargo test --workspace: 119/119 lib + tests pass on the branch (the single pre-existingtest_trade_listenerfailure reproduces onmainun-modified and is unrelated).cargo build --releasesucceeds.cargo clippy --lib --tests --no-depsproduces no errors.dob_tap.rs::testsassertOrderAdd.quantityandOrderExecute.exec_quantityscale correctly atqty_exponent = -3andqty_exponent = 0(the integer-only case from the live observation).multicast::dob::snapshot_qty_scaling_testsdecode the actualSnapshotOrderbytes off a UDP loopback atqty_exponent = -3andqty_exponent = 0and assert the wirequantityfield matches the venue scale.parity_tests.rsend-state TOB-vs-DoB scenario factored to takeqty_exponentand instantiated at-8,-3, and0. The-3and0variants would have failed pre-fix withdob_qty / tob_qty == 10^(8 + qty_exponent), exactly matching the symptom from the field.aws-tyo-hl-mainnetvia the ansible role withdz_hl_publisher_branch=fix/dob-qty-exponent; service restarted, publisher healthy, on-wire output verified as expected.Refs: hyperliquid hyperliquid-dex#10