vike
Order flow

Flow bars

Tick, volume and dollar bars with a buy/sell/delta split, how each one treats the trade that crosses its boundary, and the opt-in imbalance and runs bars that sample by information rather than by count.

crates/vike-orderflow/src/bars.rs is "tick-, volume- and dollar-interval bar aggregation with a buy/sell/delta split, plus the opt-in López-de-Prado information-driven bars". All five builders reuse the one classification semantic and emit the same bar.

The bar

pub struct OrderflowBar {
    pub o: f64, pub h: f64, pub l: f64, pub c: f64,
    pub buy_vol: f64, pub sell_vol: f64, pub delta: f64,
    pub trade_count: u32,
    pub first_ts: i64, pub last_ts: i64,
}

Behind every builder sits one private accumulator, BarAcc, which "accumulates the running fields of one open bar": OHLC, the buy and sell sums, a trade count, and a running notional Σ price·size that "is only consulted by DollarBarBuilder; the tick/volume builders carry it inertly". At close delta is buy − sell, and first_ts/last_ts are the first and last contributing trade's ts.

The five builders

buildercloses whenthe boundary tradepush returns
TickBarBuilder::new(n)the count reaches nwhole — a trade is one tickOption<OrderflowBar>
VolumeBarBuilder::new(threshold)buy + sell reaches thresholdsplit by volume; the filling slice closes the barVec<OrderflowBar>
DollarBarBuilder::new(threshold)Σ price·size reaches thresholdsplit by the size that fills the remaining dollar roomVec<OrderflowBar>
ImbalanceBarBuilder::new(unit, threshold)|Σ ±unit| reaches thresholdincluded whole — no splitOption<OrderflowBar>
RunsBarBuilder::new(unit, threshold)max(buy-run, sell-run) reaches thresholdincluded whole — no splitOption<OrderflowBar>

Every constructor asserts its parameter (n >= 1, threshold > 0). Each builder has a forming() read — "Snapshot of the OPEN (unclosed) bar — the chart's forming bar. Pure read." — and a batch from_trades over a slice, which returns closed bars only; the tick and imbalance forms say so in place ("trailing partial bar dropped"). Zero- and negative-size trades never open or advance a bar on any path.

Why volume and dollar bars return a Vec

vol_fold closes bars "whenever acc.buy+acc.sell reaches threshold" and "splits the trade across boundaries by volume": the trade is cut into a synthetic sub-trade of the size that fills the open bar's room, at the trade's own price, side and timestamp, and the remainder starts the next bar. One large print can therefore close several bars in a single push, which is why the return type is a list.

dollar_fold is "the dollar twin of vol_fold": it splits by room / price, the size that fills the remaining dollar room. It carries a guard the volume form does not need. If the computed take is non-positive, or too small to advance the remainder at all (sub-ulp), "the whole remainder is absorbed into the open bar instead of looping". That is also where a degenerate price <= 0 trade lands: it contributes volume but no notional, "so it can never close a bar on its own".

The two boundary conventions are deliberate and different

Volume and dollar bars split the crossing trade so a bar's volume is exactly its threshold. The imbalance and runs bars include the crossing trade whole, because — in the module's words — "LdP samples AT the crossing tick". Do not expect an imbalance bar's total volume to be a round number.

Information-driven bars

The two remaining builders are the López de Prado imbalance and runs bars (AFML §2.3.2), in their "fixed-threshold deterministic form". Both are opt-in: "a caller that never constructs this leaves every existing bar path byte-identical", and the header restates that a default path which builds none leaves the tick/volume/dollar behavior unchanged.

FlowUnit is the per-trade quantity they weight each signed trade by — Tick weights every trade as 1, Volume as its size, Dollar as price·size. It is fixed at construction and affects only when a bar closes; "the ordinary buy/sell/delta volume split on the emitted bar is unaffected by the chosen unit".

Imbalance bars advance a running signed sum θ by +weight for a buy aggressor and −weight for a sell, and close when |θ| reaches the threshold.

Runs bars grow two cumulative per-side sums and close when the larger reaches the threshold. The doc comment is emphatic about what this is: "LdP's cumulative-side statistic, NOT the longest CONSECUTIVE same-signed streak."

The EWMA-adaptive thresholds from the same chapter — E[T]·|2P(buy)−1| for imbalance, E[T]·max{P, 1−P} for runs — are named as the documented future extension; what ships is the fixed-threshold variant.

On the chart

crates/vike-app-core/src/tickvol.rs is where these bars become chart intervals. Its BarKind classifies an interval string: the fixed kline set passes through unchanged, while "<n>t" and "<x>v" select the tick and volume aggregators. TickVolAgg wraps one builder — the header names TickBarBuilder or VolumeBarBuilder — and converts each closed OrderflowBar to a vike_model::Bar, with funding, bid, ask and symbol left None the way the kline path leaves its own not-sourced fields. Dollar, imbalance and runs bars are not named on that interval path.

On this page