feat(trade): order book grouping, size units, layouts and conformance suite (OB-050, OB-052, OB-053) - #819
Merged
IbrahimIjai merged 6 commits intoSep 26, 2026
Conversation
The order book feed had only a connection status, so a healthy but quiet market and a dead transport were indistinguishable, and nothing stopped stale depth from being treated as current. Track separately: transport state, the time of the last frame of any kind (heartbeats and acks count), the last valid book revision, and the provider timestamp of each update. Derive fresh / stale / reconnecting / unavailable with an explicit reason: silence past the heartbeat timeout, a run of malformed frames, or a provider delivering updates late are stale; a long gap with no book change while frames keep arriving is a quiet market and stays fresh. Provider lag is measured against a rolling baseline of delivery delays, so a constant clock offset in either direction is not lag. Duplicate or out-of-order revisions never make a book look fresher. quoteFromDepth() is the only sanctioned way to derive a suggested price from depth and returns null unless the feed is fresh with an applied snapshot. The shared subscription now records this from the frames it receives, and the order book panel shows the existing source health badge. Verification: 42 pure-logic tests, 11 stream tests through a fake WebSocket with an explicit MSW snapshot handler, 9 hook tests and 3 panel tests, all passing. These are mocked-transport tests; no live Binance verification. The repository gate (lint, typecheck, check:tokens, tests, build) does not pass on upstream main independent of this change: a syntax error in useOrderEventPolling.ts breaks typecheck and build, and pre-existing lint, token and test failures remain. The failing test set is identical with and without this change.
…s (OB-052, OB-053) Pure, exact-integer logic for the depth view's display controls, plus a persisted store. Grouping is offered as multiples of the market's native tick and stored per market as a multiplier, so switching between markets with different tick sizes keeps each market's own choice. Stored values are never trusted: they are sanitized against the market's current metadata, a value the market no longer offers is reset (and reported), and unusable metadata offers no grouping at all. Quote-unit sizes are exact (price x size carries priceScale + sizeScale decimals), and bucket prices never appear better than a level they contain. Layouts (both, bids only, asks only) allocate rows deterministically, keep rows best-first from the spread, and scale depth bars against what is actually visible. The store caps remembered markets, recovers from corrupt storage and holds no market data. There is no market-metadata source in the repo yet, so these take metadata as input rather than inventing tick sizes.
DepthControls uses the shared Select and RadioGroup, with explicit accessible names carried in visually hidden label text (the shared radio links itself to its wrapping label, which overrides aria-label). DepthPanel shows the market identity, spread and freshness badge in every layout, keys rows by price, and has no transitions on the ladder so re-sorting rows does not shimmer. The panel receives levels as props from whatever owns the live subscription, so switching layout, grouping or unit only recomputes a display view and cannot remount the feed; a test drives 20 rapid switches and asserts one mount. When a market's metadata becomes unusable a stored grouping is reset rather than kept. Not wired into OrderBookPanel: that panel intentionally shows reference data only until a verified matching source exists, and there is no market-metadata source yet. No new motion was added, so the animation skills named in the issue (not present in the repository) were not needed.
47 deterministic scenarios over the production book reducer and order lifecycle derivation: duplicates, disconnect and reconnect, missing revisions, partial fills and cancellation races, plus seeded randomized convergence. The expected book comes from an independent oracle rather than the reducer under test, and every arrival permutation of each cancel/fill race is checked. Mutation checks confirm it has teeth: breaking the reducer's gap check, duplicate check or reset, or making the stage derivation call a partial fill filled, each fails the suite. README.md names the tested modules and their revisions and separates what is mocked from what is not verified. All fixtures are hand-built; nothing was verified against a live venue or indexer, and order/fill events go through a test-support reference fold because the app does not yet consume them as a stream.
|
@precious-akpan is attempting to deploy a commit to the Ijai's projects Team on Vercel. A member of the Team first needs to authorize it. |
|
@precious-akpan Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
2 tasks
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.
Stacked on #818
This branch is built on the OB-049 work in #818, because the conformance suite uses its freshness module. Until #818 merges, this PR's diff also shows #818's two commits (
2e6bcdf,c1401d0). The new work is the last four commits:eb6c546,700279b,e3d247band the changelog entry.Summary
Implements OB-050 (#702), OB-052 (#704) and OB-053 (#705), now unblocked by the merged reducer and depth work (#817).
#704: grouping and unit controls
price x sizecarriespriceScale + sizeScaledecimals). Bucket prices never look better than a level they contain. Spread comes from the canonical book, so grouping cannot move it.#705: layouts
bothnever drops a side). Rows stay best-first from the spread, and depth bars scale against what is visible.DepthPanelkeeps the market identity, spread and freshness badge in every layout. Rows are keyed by price and there are no transitions, so re-sorting does not shimmer. A test drives 20 rapid switches and asserts the live feed component mounts exactly once.#702: conformance suite (
lib/orderbook/conformance/)README.mdnames the tested modules with revisions, the schema covered and what is mocked versus not verified.What is not done or not verified
OrderBookPanel. That panel intentionally shows reference data only until a verified matching source exists, and there is no market-metadata source in the repo, so the controls take metadata as input rather than inventing tick sizes. Mounting them is a follow-up once both exist.Orderentity carries ledgers and timestamps, not book revisions, so there is nothing to test ("where the source exposes that relationship").main, and the repo has none. This PR adds no animation, and it removes the ladder's width transition in the new panel, so there are no clips or review table to attach.Test evidence
tscreports nothing in them. No newcheck:tokensviolations.Gate status (AGENTS.md section 1), reported honestly
The gate does not pass on upstream
mainindependent of this change:bun typecheckandbun run buildfail on syntax errors in files this PR does not touch (useOrderEventPolling.ts,TVChartContainer.tsx,TradePanel.tsx,useLiveBar.ts).check:tokensreports 5 existing violations inDepthChart.tsxandDepthLadder.tsx.tradefeature has a set of failing test files with and without this change. The failures I checked are unrelated, andCollateralDialog.test.tsxfailed once under load and passes alone with and without my changes.test:coverageand the indexer integration steps, since they sit behind the failing steps.Closes #702
Closes #704
Closes #705