Buy/sell classification
The one load-bearing semantic of the order-flow crate — how a trade's aggressor side is read off the maker flag, and how every bar, delta, footprint and profile inherits it unchanged.
crates/vike-orderflow/src/classify.rs opens by calling itself "the one load-bearing semantic of
the whole crate", and the rest of the crate agrees: every study that splits volume into buy and
sell reaches this module rather than deciding for itself. It is three items long.
pub enum Side { Buy, Sell }
pub fn classify(t: &TradeTick) -> Side // is_buyer_maker → Sell, else Buy
pub fn signed(t: &TradeTick) -> (f64, f64) // (buy_add, sell_add) for the full sizeThe convention
TradeTick::is_buyer_maker is the Binance-aggTrades convention, and the header spells out what
it means: the buyer was the resting maker — a bid — so the aggressor was the seller. classify
therefore maps is_buyer_maker = true to Side::Sell and false to Side::Buy. The module's own
tests put it in market terms: an ask lifted is a buy aggressor; a bid hit is a sell aggressor.
signed is the form the builders actually consume. Its doc comment: "(buy_add, sell_add) for the
full size of t, routed to the aggressor side" — a buy contributes (size, 0.0), a sell
(0.0, size). A trade is never split between sides; it goes whole to the side that crossed the
spread.
The module ports no Python twin — it is new native code — so there is no frozen fixture behind it, only the hand-computed tests beside it (the index explains why that is the crate's oracle).
Everything downstream inherits it
The crate header (crates/vike-orderflow/src/lib.rs) states the consequence: delta = buy_vol − sell_vol everywhere below. Concretely:
| study | how it uses the semantic |
|---|---|
| flow bars | the header of bars.rs: all builders "reuse the one crate::classify::signed buy/sell semantic"; OrderflowBar::delta is buy − sell at close. The information-driven bars call classify directly to sign each trade's weight. |
| CVD | CvdAccumulator::push adds b − s from signed |
| footprint | FootprintBuilder::push adds signed's pair to the trade's price bucket |
| volume profile | VolumeProfileBuilder::push does the same into its own grid |
Because there is exactly one classification site, a feed whose maker flag is inverted would invert every delta, footprint cell and profile bin at once — there is no second reading anywhere to disagree with.
A missing flag reads as a buy
On crates/vike-marketdata/src/bar.rs's TradeTick the field is #[serde(default)], and the struct's
own description says the aggressor flag is present "where available". A trade record that carries
no flag deserializes with is_buyer_maker = false, which classify reads as a buy aggressor. On
a tape that never supplies the flag, every study on this page's list would report all volume as
buy volume and a delta equal to total volume — a number that looks like a signal and is not.
The aggressor-free alternatives
Not every venue publishes an aggressor flag, and crates/vike-orderflow/src/vpin.rs answers that
in two places. Vpin itself has a flagless mode: "feeds without a real aggressor flag construct
with has_aggressor = false and pass the prevailing mid to push: price >= mid → Buy, else
Sell (the quote-midpoint test)", and a missing mid falls back to the flag so no trade is dropped;
the chart's study registry exposes the same switch as its aggressor flag parameter set to 0. The
crate header names the other: BvcVpin, "the aggressor-free" VPIN of Easley, López de Prado and
O'Hara (2012), which classifies volume in bulk by the price change of each bucket, Φ(ΔP/σ), with
a tick-rule option. It does not go through classify at all — that is the point of it. Both are
described on the VPIN page.
Order flow
The pure trade-aggregation core behind every order-flow study — one buy/sell semantic, flow bars, CVD, footprint and volume profile folded from the trade tape, the book-fed microstructure studies beside them, and the parity discipline each builder is gated by.
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.