Skip to content

feat(trade): expose order book source health and freshness (OB-049) - #818

Merged
IbrahimIjai merged 3 commits into
SO4-Markets:mainfrom
precious-akpan:feat/701-702-704-orderbook-health-conformance-grouping
Sep 27, 2026
Merged

IbrahimIjai merged 3 commits into
SO4-Markets:mainfrom
precious-akpan:feat/701-702-704-orderbook-health-conformance-grouping

Conversation

@precious-akpan

@precious-akpan precious-akpan commented Sep 26, 2026 •

Copy link
Copy Markdown
Contributor

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.

  • Separate clocks. 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 are tracked independently (lib/source-freshness.ts).
  • States: fresh, stale, reconnecting or unavailable, each with a 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.
  • Clock skew: provider lag is measured against a rolling baseline of delivery delays, so a constant offset between clocks, in either direction, is not lag. The window re-baselines if the offset steps.
  • Revisions: duplicate or out-of-order revisions never make a book look fresher.
  • Quotes: 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.
  • Wiring: the shared subscription records this from the frames it receives (getFreshness, getSourceHealth). useSourceFreshness re-evaluates on a timer, because silence raises no event, and only re-renders on a visible change. The order book panel shows the existing SourceHealthBadge.
  • The book contents and reconciliation are unchanged.

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 only Date faked.
  • useSourceFreshness.test.ts: 9 tests. OrderBookPanel.test.tsx: 3 new tests.
  • Evidence type: all of this is mocked-transport testing. Nothing was verified against the live venue, and it says nothing about a deployment or schema version.

Gate status (AGENTS.md section 1), reported honestly

The gate does not pass on upstream main independent of this change, so I could not run it green:

  • bun typecheck and bun run build fail on a syntax error in apps/web/src/features/trade/hooks/useOrderEventPolling.ts (a broken try/catch/finally), which this PR does not touch.
  • bun lint has existing errors in other files. The files I touched add none. market-data-stream.ts has 9 errors, the same 9 as on main.
  • bun run check:tokens reports 5 existing violations in DepthChart.tsx and DepthLadder.tsx.
  • apps/web tests: 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:content passes. The docs tests passed on a rerun after one flaky failure under load.
  • Not run: test:coverage and 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

  • There is no animation change in this PR. The panel only adds the existing badge.

Closes #701

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.
@vercel

vercel Bot commented Sep 26, 2026

Copy link
Copy Markdown

@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.

@drips-wave

drips-wave Bot commented Sep 26, 2026

Copy link
Copy Markdown

@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! 🚀

Learn more about application limits

@IbrahimIjai
IbrahimIjai merged commit 4c5cf1d into SO4-Markets:main Sep 27, 2026
1 of 4 checks passed
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.

OB-049: Expose source health and market-data freshness

2 participants