Skip to content

v0.5.0: bounded-latency L3 orderbook, egui/wgpu refresh, cross-platform CI#1

Merged
OctopusTakopi merged 1 commit into
mainfrom
release/v0.5.0
Jul 12, 2026
Merged

v0.5.0: bounded-latency L3 orderbook, egui/wgpu refresh, cross-platform CI#1
OctopusTakopi merged 1 commit into
mainfrom
release/v0.5.0

Conversation

@OctopusTakopi

Copy link
Copy Markdown
Owner

Release candidate for v0.5.0. Opening to exercise the 3-platform (ubuntu/windows/macos) build matrix — build + test + clippy -D warnings on each OS. No release is published (that needs a v* tag).

See the commit message for full detail. Highlights:

  • Bounded-latency L3 orderbook (POPCNT top-N ranking, bounded cancellation scan, sparse reset).
  • Dependency refresh: eframe/egui 0.35, egui_plot 0.36, wgpu 29 (pinned to match eframe), rand 0.10, criterion 0.8, reqwest 0.13, tokio-tungstenite 0.30.
  • Cross-platform CI fixes: aarch64 clippy (popcnt_available gating), wgpu Vulkan backend, committed Cargo.lock for --locked.

…-platform CI

Reworks the hot path of the flat ring-buffer L3 estimator so per-update
latency is bounded rather than growing with queue age, refreshes the
whole dependency tree onto the egui 0.35 / wgpu 29 generation, and makes
the three-platform release build actually reproducible in CI.

Bounded-latency L3 orderbook
----------------------------
The 65,536-tick (1 << 16) ring-buffer storage, the three-level
hierarchical bitsets, the pooled OrderBlock arena, and lazy window
re-centring are unchanged. The steady-state maintenance paths that
scaled with book size or queue age have been rewritten.

Top-N ranking. `is_in_top_n` no longer walks the bitset level by level
from the best price (O(N) per call, hit on every applied level change).
It now maps the price to its window index and answers the query with a
bounded hardware POPCNT over the occupancy bitmap:
  - count_below_limited / count_above_limited sum set bits on the far
    side of the index, stopping once the limit is reached, so the scan
    is capped at N regardless of how many levels are occupied.
  - A runtime `popcnt`-detected specialisation is dispatched once per
    book (the detection flag is x86_64-only and cfg-gated out of the
    struct entirely on other targets).
  - Out-of-window prices are decided in O(1) from the total occupancy.

Cancellation. `remove_order_by_size` previously scanned the whole queue
from the oldest block forward, so exact-size cancel latency grew with
queue length and with the zeroed "holes" left by in-place removals.
The search now runs newest-to-oldest and is capped at the most recent
blocks (MAX_EXACT_MATCH_BLOCKS), so cancel cost is flat regardless of
how long a level has lived; a miss falls back to the existing pop_back
trim, so the aggregate level quantity is always exact. The AVX2 path is
now selected through a safe `remove_order_by_size` wrapper with an
always-available scalar fallback, replacing the `unsafe` public
`remove_order_by_size_simd`. Both paths guard head_offset, tail_offset
and non-zero sizes, so stale/unfilled slots can never false-match.

Snapshot / gap reset. `reset_with_base` no longer clears all 65,536
queue slots on every snapshot and gap recovery. It scans the merged
bid|ask occupancy words and frees only the queues that are actually
populated, relying on the maintained invariant "queue has blocks <=>
occupancy bit is set". `Bitset::set` is now idempotent (early-out when
the bit is already set) so it never re-touches the L1/L0 summary words.

Correctness fixes
-----------------
- Liquidity caches. `get_liquidity_curves` and `calculate_liquidity_
  impact` shared one `last_liquidity_id`, so calling either marked the
  other's cache fresh and could return stale curves/impact. Split into
  independent `last_liquidity_curve_id` and `last_impact_book_id`.
- Empty snapshots are now authoritative: a snapshot with no levels
  resets the book instead of being skipped, clearing stale levels.
- Snapshot fetch failures (`client.rs`) now check `error_for_status`,
  abort the socket, surface a UI warning and retry, instead of logging
  and continuing without a book.
- Depth-update buffering (`app.rs`) is capped (dropping the oldest when
  full) and a sequence gap during snapshot replay triggers a refetch
  rather than being silently ignored.
- TWAP binning aligns to absolute time (`ts - ts % bin_ms`), assigns the
  boundary trade to the new bin, fills quiet gaps with empty bins
  (bounded by the window), and bumps an `update_id` so the PSD-bar UI
  cache invalidates on value changes, not just length changes.

Benchmark harness
-----------------
The order-book benches replayed updates with static sequence ids, so
after the first apply every update was rejected as stale and the loop
measured the no-op reject path. They now assign monotonic ids and assert
`DepthUpdateStatus::Applied`, so the numbers reflect real work.

Dependency refresh
------------------
Whole tree bumped to latest, with compatibility fixes:
  - eframe / egui 0.32 -> 0.35, egui_plot 0.33 -> 0.36.
    `App::update(&Context)` became `App::ui(&mut egui::Ui)` and panels'
    `.show()` now take a `Ui`; the app clones the Arc-backed context from
    `ui.ctx()`, drives the `CentralPanel` on the passed `Ui`, and keeps
    the still-`Context`-based floating `Window`s. `NativeOptions` lost
    `hardware_acceleration` (a glow-era GL hint, irrelevant to wgpu).
  - wgpu 25 -> 29, pinned to 29 (not the latest 30) so the direct
    Vulkan-backend dependency unifies with eframe 0.35's own wgpu
    instead of pulling a second, unused copy.
  - rand 0.9 -> 0.10: `random_range` moved to the `RngExt` trait
    (engine + benches).
  - criterion 0.5 -> 0.8: `criterion::black_box` -> `std::hint::black_box`.
  - reqwest 0.12 -> 0.13, tokio-tungstenite 0.27 -> 0.30, tokio 1.46 ->
    1.52, plus transitive patch bumps.

Cross-platform build / CI
-------------------------
- The wgpu backend that eframe enables (`metal` + `webgpu`) has no usable
  native backend on Linux/Windows, which aborted at startup. A direct
  `wgpu` dependency re-adds `vulkan` (+ vulkan-portability), unified into
  eframe's wgpu; macOS keeps `metal`.
- `popcnt_available` was written but only read under
  `cfg(target_arch = "x86_64")`, so it tripped `dead_code` under
  `-D warnings` on the aarch64 macOS runner. The field and its
  initialiser are now cfg-gated to x86_64.
- `Cargo.lock` is committed (removed from `.gitignore`) so the workflow's
  `--locked` steps have a lockfile; without it all three jobs failed at
  the first cargo command.
- Release workflow builds/tests/clippies on ubuntu, windows and macos
  for every PR and push, and publishes the three binaries only on `v*`
  tags.

Performance
-----------
On the corrected criterion benchmark, a fully applied depth update
(top-N ranking + L3 queue maintenance) drops from ~7.5 us to ~500 ns
(~15x), landing within ~1.4x of a plain L2 BTreeMap that does none of
the L3 work. More importantly, worst-case per-update latency is now
bounded instead of scaling with queue age.

Bumps the crate version to 0.5.0 and documents the storage/ranking
design and the throughput numbers under a new README "Performance"
section.
@OctopusTakopi OctopusTakopi merged commit f4876f0 into main Jul 12, 2026
8 checks passed
@OctopusTakopi OctopusTakopi deleted the release/v0.5.0 branch July 12, 2026 13:44
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