Appearance
VPIN
What it computes
Volume-synchronised Probability of Informed Trading (VPIN) per stock. The analytic slices the trade stream into equal-volume buckets, classifies each bucket's volume as buy / sell via either the tick-rule or Bulk-Volume Classification (BVC) with EWMA-smoothed σ_ΔP, and reports:
over a sliding window of n completed buckets. The denominator /methodology/vpin for the deviation rationale and the bound on the difference.
Spillover. When a trade's size overshoots the current bucket's remaining capacity, the surplus seeds the next bucket proportional to the trade's classifier-derived buy-fraction so the across-bucket aggregation matches the un-split aggregate.
Methodology
Easley, D., López de Prado, M. M. & O'Hara, M. (2012), Flow Toxicity and Liquidity in a High-Frequency World, Review of Financial Studies 25(5): 1457–1493. See /methodology/vpin.
The 2012 paper does not specify spillover; proportional spillover is the conventional production implementation choice to avoid biasing VPIN downward in high-activity windows.
Inputs
- Stock
TradeTick.
Output schema (VpinTick)
The field / type / description table below is regenerated from the VpinTick Rust source by docs-site/scripts/inject-doc-tables.ts on every npm run docs:build. Do not hand-edit between the sentinels.
| Field | Type | Description |
|---|---|---|
contract | Contract | Resolved contract metadata. Subscribers identify the security by (symbol, expiration, right, strike) on this field — the wire-level contract id is engine-internal and is not surfaced here. |
date | i32 | Trading-session date of the trade that closed this bucket (YYYYMMDD). |
ms_of_day | i32 | Millisecond-of-day of the trade that closed this bucket. |
vpin | f64 | VPIN value: `(1/n) Σ |
bucket_index | u64 | Zero-based index of the just-closed bucket since the state was first instantiated. |
buckets_in_window | usize | Number of completed buckets in the rolling window at the moment of emission. Equal to bucket_index + 1 until the window saturates, then constant at window_size. |
bucket_volume | u64 | Configured bucket volume (shares). |
buy_volume | u64 | Buyer-attributed share volume in the just-closed bucket. |
sell_volume | u64 | Seller-attributed share volume in the just-closed bucket. |
Configuration (VpinRequest)
Regenerated from the VpinRequest Rust source — see the note above.
| Field | Type | Description |
|---|---|---|
contracts | SecurityFilter | Contracts the subscription tracks. Stock contracts only; option / index / rate ticks are rejected at the dispatch gate. |
bucket_volume | u64 | Target shares per volume-time bucket. ELO (2012) use ~50 k for liquid US equities; the default of 10_000 trades the statistical floor for slightly faster signal turnover. |
window_size | usize | Sliding-window size — number of completed buckets averaged to produce the rolling VPIN value. Defaults to 50. |
min_buckets_for_emit | usize | Warmup floor: the analytic emits only after this many buckets have closed. Defaults to 30; lower values trade statistical noise for earlier signal. 0 is a valid bypass — every closed bucket emits immediately and the consumer filters on the bucket index. |
classification | VpinClassification | Trade-side classifier. |
conditions | ConditionPolicy | Trade-condition admission policy. |
venues | ExchangeFilter | Exchange / venue admission policy. |
VpinParams::validate rejects window_size = 0, bucket_volume = 0, and BvcWithEwmaSigma { alpha } outside (0, 1] at construction time — misconfigured specs surface VpinSpecError instead of silently degrading to a one-bucket window.
Operational characteristics
- Per-tick latency. O(1) — one classifier update, one bucket update.
- Allocation discipline. Sliding-window
RollingWindowis pre-sized; no hot-path allocations. - Replay parity. Deterministic given identical input tick order; spillover keeps across-bucket totals invariant under tick split.
Example
rust
// Cargo.toml:
// kairos = "0.1"
use kairos::{Client, VpinRow};
let client = Client::connect(("me@example.com", "secret"))?;
let sub = client
.live()
.vpin(["QQQ"])
.bucket_volume(100_000)
.window_size(50)
.min_buckets_for_emit(30)
.on_event(|row: &VpinRow| println!("vpin={} bucket={}", row.vpin, row.bucket_index))?;
// ... later ...
sub.unsubscribe();