feat(trade): expose order book source health and freshness (OB-049) - #818
Merged
IbrahimIjai merged 3 commits intoSep 27, 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.
|
@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.
Summary
Implements OB-049 (#701): source health and market-data freshness for the order book feed. #702 and #704 are intentionally not part of this PR, because they depend on the reconciliation and aggregation work in #697 and #698.
lib/source-freshness.ts).quoteFromDepth()is the only sanctioned way to derive a price from depth and returns null unless the feed is fresh with an applied snapshot and the book is uncrossed.getFreshness,getSourceHealth).useSourceFreshnessre-evaluates on a timer, because silence raises no event, and only re-renders on a visible change. The order book panel shows the existingSourceHealthBadge.Test evidence
source-freshness.test.ts: 42 tests covering initial and unavailable states, no trades versus dead transport versus malformed updates versus a lagging provider, clock skew both ways, revision handling, and stale depth never seeding a quote.market-data-stream.freshness.test.ts: 11 tests through a fake WebSocket, with the REST snapshot served by an explicit MSW handler (onUnhandledRequest: error) and onlyDatefaked.useSourceFreshness.test.ts: 9 tests.OrderBookPanel.test.tsx: 3 new tests.Gate status (AGENTS.md section 1), reported honestly
The gate does not pass on upstream
mainindependent of this change, so I could not run it green:bun typecheckandbun run buildfail on a syntax error inapps/web/src/features/trade/hooks/useOrderEventPolling.ts(a brokentry/catch/finally), which this PR does not touch.bun linthas existing errors in other files. The files I touched add none.market-data-stream.tshas 9 errors, the same 9 as onmain.bun run check:tokensreports 5 existing violations inDepthChart.tsxandDepthLadder.tsx.apps/webtests: the same 20 files and 90 or 91 tests fail with and without this change. The failing set is identical, and this PR adds 65 passing tests.check:contentpasses. The docs tests passed on a rerun after one flaky failure under load.test:coverageand the indexer integration steps, since they sit behind the failing steps above.I did not fix
useOrderEventPolling.ts, to avoid scope creep. It needs its own fix.Notes
Closes #701