Skip to content

Commit 322c8de

Browse files
committed
feat: v0.5.0 - bounded-latency L3 orderbook, egui/wgpu refresh, cross-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.
1 parent 371220d commit 322c8de

17 files changed

Lines changed: 5792 additions & 224 deletions

.github/workflows/Release.yml

Lines changed: 53 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,33 @@
1-
name: Release
1+
name: CI and Release
22

33
on:
4+
pull_request:
45
push:
6+
branches:
7+
- main
58
tags:
6-
- 'v*'
9+
- "v*"
710
workflow_dispatch:
811

912
permissions:
10-
contents: write
13+
contents: read
1114

1215
jobs:
13-
release:
16+
build:
17+
name: Build (${{ matrix.os }})
1418
runs-on: ${{ matrix.os }}
1519
strategy:
20+
fail-fast: false
1621
matrix:
1722
os: [ubuntu-latest, windows-latest, macos-latest]
18-
rust: [stable]
23+
1924
steps:
2025
- name: Checkout repository
2126
uses: actions/checkout@v4
2227

23-
- name: Set up Rust
24-
uses: dtolnay/rust-toolchain@master
28+
- name: Set up stable Rust
29+
uses: dtolnay/rust-toolchain@stable
2530
with:
26-
toolchain: ${{ matrix.rust }}
2731
components: rustfmt, clippy
2832

2933
- name: Cache Rust dependencies
@@ -33,55 +37,59 @@ jobs:
3337
~/.cargo/registry
3438
~/.cargo/git
3539
target
36-
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
40+
key: ${{ runner.os }}-${{ runner.arch }}-cargo-${{ hashFiles('Cargo.lock') }}
3741
restore-keys: |
38-
${{ runner.os }}-cargo-
42+
${{ runner.os }}-${{ runner.arch }}-cargo-
43+
44+
- name: Check formatting
45+
run: cargo fmt --all -- --check
46+
47+
- name: Run Clippy
48+
run: cargo clippy --locked --all-targets -- -D warnings
3949

4050
- name: Run tests
41-
run: cargo test --release
51+
run: cargo test --locked --all-targets
4252

43-
- name: Build release
44-
run: cargo build --release
53+
- name: Build GPU-enabled release binary
54+
run: cargo build --locked --release
4555

46-
- name: Set binary extension
47-
id: set-ext
56+
- name: Stage binary
57+
id: stage
58+
shell: bash
4859
run: |
49-
if [ "${{ runner.os }}" == "Windows" ]; then
50-
echo "extension=.exe" >> $GITHUB_OUTPUT
51-
echo "binary_path=target/release/binance_l3_est.exe" >> $GITHUB_OUTPUT
52-
else
53-
echo "extension=" >> $GITHUB_OUTPUT
54-
echo "binary_path=target/release/binance_l3_est" >> $GITHUB_OUTPUT
60+
extension=""
61+
if [[ "${{ runner.os }}" == "Windows" ]]; then
62+
extension=".exe"
5563
fi
56-
shell: bash
64+
asset="binance_l3_est-${{ runner.os }}-${{ runner.arch }}${extension}"
65+
mkdir -p dist
66+
cp "target/release/binance_l3_est${extension}" "dist/${asset}"
67+
echo "asset=${asset}" >> "$GITHUB_OUTPUT"
5768
58-
- name: Rename binary (non-Windows)
59-
if: runner.os != 'Windows'
60-
run: |
61-
mv -f ${{ steps.set-ext.outputs.binary_path }} target/release/binance_l3_est-${{ runner.os }}-${{ runner.arch }}${{ steps.set-ext.outputs.extension }}
62-
shell: bash
69+
- name: Upload binary
70+
uses: actions/upload-artifact@v4
71+
with:
72+
name: ${{ steps.stage.outputs.asset }}
73+
path: dist/${{ steps.stage.outputs.asset }}
74+
if-no-files-found: error
6375

64-
- name: Rename binary (Windows)
65-
if: runner.os == 'Windows'
66-
run: |
67-
$dest = "target/release/binance_l3_est-${{ runner.os }}-${{ runner.arch }}${{ steps.set-ext.outputs.extension }}"
68-
if (Test-Path $dest) { Remove-Item $dest -Force }
69-
Rename-Item -Path "${{ steps.set-ext.outputs.binary_path }}" -NewName "binance_l3_est-${{ runner.os }}-${{ runner.arch }}${{ steps.set-ext.outputs.extension }}"
70-
shell: pwsh
76+
release:
77+
name: Publish release
78+
if: startsWith(github.ref, 'refs/tags/v')
79+
needs: build
80+
runs-on: ubuntu-latest
81+
permissions:
82+
contents: write
7183

72-
- name: Upload binaries as artifacts
73-
uses: actions/upload-artifact@v4
84+
steps:
85+
- name: Download platform binaries
86+
uses: actions/download-artifact@v4
7487
with:
75-
name: binaries-${{ runner.os }}
76-
path: target/release/binance_l3_est-${{ runner.os }}-${{ runner.arch }}${{ steps.set-ext.outputs.extension }}
88+
path: dist
89+
merge-multiple: true
7790

78-
- name: Create and upload release assets
79-
if: github.event_name == 'push'
91+
- name: Create GitHub release
8092
uses: softprops/action-gh-release@v2
8193
with:
82-
files: target/release/binance_l3_est-${{ runner.os }}-${{ runner.arch }}${{ steps.set-ext.outputs.extension }}
94+
files: dist/*
8395
generate_release_notes: true
84-
draft: false
85-
prerelease: false
86-
env:
87-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.gitignore

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
/target
22
.idea
33
*~
4-
Cargo.lock
54
**/*.rs.bk
65
*.pdb
76
**/mutants.out*/
8-
/design
7+
/design

0 commit comments

Comments
 (0)