vike
Order flow

In the GUI

Where each order-flow study renders — the one footprint substrate the app feeds, the Footprint style, the volume-profile overlay and CVD pane derived from it, the study panes the chart engine carries, and which of those the desktop app has wired.

The order-flow crate computes; vike-chart draws. Its header claims "the per-bar orderflow substrate rendering (chart::ChartStyle::Footprint + the CVD/volume-profile overlays, SP2)", and crates/vike-chart/Cargo.toml lists vike-orderflow among the five vike-* crates it depends on, annotated "per-bar footprint substrate → CVD/profile/footprint render (SP2)". Everything below follows from one decision stated in crates/vike-chart/src/orderflow.rs's header: CVD and the visible volume profile are derived from &[FootprintBar] — "the single orderflow input — so the app feeds one structure and the renderer computes the three views."

One input: the footprint substrate

ChartInputs::footprint (crates/vike-chart/src/chart/mod.rs) is "the per-bar footprint substrate (SP2), index-aligned to state.bars — the single orderflow input CVD/profile/footprint are all derived from chart-side", and it is None "when the app hasn't aggregated trades for this chart (default-off: no orderflow rendering happens without this)".

The app builds it in crates/vike-app-core/src/orderflow.rs's OrderflowAgg, which buckets "the drained trade tape into the chart's bars (by open-time)" and retains per-bar footprint cells; the per-frame drain that feeds it is crates/vike-app-core/src/core_sync.rs, which runs every frame outside the snapshot gate so a workspace with no kline feed no longer stalls. The bucket width defaults to nice_orderflow_tick: "a 'nice' 1/2/5×10^k bucket tick ~ price × 0.0002 (≈2 bps)" — about $10 on BTC, $0.5 on ETH, $0.0001 on a $0.5 coin — computed through the libm crate so a price on a decade boundary cannot bucket ten times finer on one machine than another.

Turning it on

WinState::orderflow_on (crates/vike-app-core/src/workspace/state.rs) is true "when either toggle is on, OR the chart style is Footprint on a Kline interval"; the window loop uses it "to lazily subscribe the trade feed and register an orderflow::OrderflowAgg — never on a fresh, all-default window". The toggles sit behind the chart toolbar's OF button (crates/vike-desktop/src/chart_window.rs): a popup with CVD and Volume Profile checkboxes and a tick-size override, Auto or a pinned value; the label tints while either toggle is on. All three persist per window (crates/vike-app-core/src/workspace/persist.rs's cvd_on, profile_on, of_tick_size), and a workspace file written before they existed loads them off.

The subscription is the window's own venue tape — "any venue with a subscribe_trades feed (OKX/Bybit/…) drives CVD/footprint, not just Binance" — and footprint stays None "until this chart's aggregator exists AND has ingested at least one trade batch (a one-two-frame startup lag after the toggle flips)". The tick size the chart receives is the aggregator's actual bucketing width, so what is drawn matches the granularity the data was bucketed at.

The three views

Footprint is a chart style. ChartStyle::Footprint is the "per-bar buy/sell price-cell grid — the orderflow footprint chart", rendered from the substrate and falling back to plain candles when it is None. It has its own "Orderflow" section in the style menu because it is not a reindexing transform like Renko or Kagi but "an alternate per-bar substrate (buy/sell price cells) for the SAME bar axis". draw_footprint (crates/vike-chart/src/render.rs) paints, per bar, a column of per-price cells with a faint imbalance tint; the bar's point-of-control cell — max buy + sell volume within that bar — gets an amber outline; buy/sell numbers appear only when cell_text_legible passes, a cell at least 11 px tall — "below that the tinted rect alone carries the imbalance read". It draws the visible bar range only, clamped against both lists: a substrate shorter than the bar list ("the aggregator hasn't caught up to a just-opened forming bar yet") draws the bars that have cells and stops at the shorter of the two, and a stale or out-of-range bound degrades to drawing nothing rather than a panic. The cells themselves are on the footprint page.

Volume profile is an overlay on the price pane. paint_volume_profile (crates/vike-chart/src/chart/price_render.rs) draws "a visible-range volume-at-price histogram

  • POC + value-area band painted on the price pane, pinned to the RIGHT edge of the visible x-range". It is windowed to the same visible bar bounds as the footprint, and the bucket width comes from one derivation, orderflow_tick_size, "so the two always agree for the same visible range". ChartState::visible_profile_shared caches the profile by visible bounds, substrate length and tick size, so an unchanged frame is an O(1) clone rather than a VolumeProfile::from_footprints recompute. Its POC line is the palette's amber, PROFILE_POC_COLOR — "same hex as dom.rs's private LAST, the DOM ladder's last-trade row". POC and value area are on the volume profile page.

CVD is a sub-pane. PaneKey::Cvd is "a singleton pane like Volume, not per-uid like Study, since there is at most one CVD pane per chart", and draw_cvd_pane plots cvd_from_footprints over the whole substrate — "the CVD pane plots the full running series, so it can't be windowed the same way" as the profile. ChartState::cvd_shared caches it on footprint_gen, a monotonic generation the aggregator bumps on every rebuild. The pane's gate is cvd_on and footprint data present; the layout tests pin that "cvd_on alone must not produce a pane without footprint data". Volume, CVD and study panes are peers in the order the user arranged, sized by the identity-keyed PaneFractions. The fold is on the CVD page.

The study panes

crates/vike-chart/src/studies.rs is "the tick-driven twin of crate::indicators::Active": vike-orderflow owns the compute, and this adapter "turns those TICK-driven folds into BAR-INDEXED oscillator-pane series", reusing the indicator paint state and ParamSpec surface so "a study pane is indistinguishable from an indicator pane to the renderer and to the settings dialogs". study_registry() holds three:

namelabelneeds a bookbands
vpinVPIN (order-flow toxicity)no0.2 · 0.5 · 0.8
book_imbalanceBook imbalance (depth-N)yes−0.5 · 0 · 0.5
otrOrder-to-trade ratioyes0

The header's load-bearing decision is the tick-to-bar mapping: each ActiveStudy keeps one incremental instance folded over the live tick stream and samples its committed value when a bar closes; a quiet bar carries the last value ("a flat segment is the truthful reading"); bars before the first committed value sample NaN; the forming bar gets a speculative tail from the study's interim read, dropped at the next sync. A book-dependent study renders nothing until a book snapshot has been fed — "no fabricated values" — while VPIN needs trades only. render_study draws through the same oscillator body indicators use, and a study pane shares the indicator uid space, so "a study and an indicator can even share one pane". Each fold is on its own page: VPIN, depth imbalance, OTR.

The desktop app does not build a study yet

crates/vike-desktop/src/app_ui.rs, the one place the app assembles ChartInputs, passes studies: &[] — "empty until the app grows its own study menu + tick fan-in — an empty slice renders byte-identically to the pre-studies chart". The three studies above are a chart-engine capability with tests, not a menu item: no picker in the shipped app adds a VPIN, book-imbalance or OTR pane. The Footprint style, the volume-profile overlay and the CVD pane are the order-flow views you can turn on today.

What is not an order-flow study

Two neighbours look like this crate's work and are not. The DOM ladder and its order-flow heatmap live in vike-panels, whose manifest calls it "chart-engine-free" and depends on vike-model, vike-ui-theme and egui only — L2Book is what it paints over, and it never reads vike-orderflow. And tick and volume bars ("<n>t", "<x>v" intervals) are a bar source for the chart's ordinary sync path, built by crates/vike-app-core/src/tickvol.rs from the flow bars builders — not a pane; orderflow_on notes they are "otherwise out of scope for the Footprint STYLE", which applies on kline intervals. The engine as a whole is on the chart page, and per-window persistence on workspaces.

On this page