Skip to content

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:

VPIN=1nk=1n|VkbuyVksell|Vktotal

over a sliding window of n completed buckets. The denominator Vktotal is the actual closed volume of bucket k, not the configured target Vbucket. The two are equal whenever a bucket fills exactly on a trade boundary; under proportional spillover they differ by a bounded surplus. See /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.

FieldTypeDescription
contractContractResolved 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.
datei32Trading-session date of the trade that closed this bucket (YYYYMMDD).
ms_of_dayi32Millisecond-of-day of the trade that closed this bucket.
vpinf64VPIN value: `(1/n) Σ
bucket_indexu64Zero-based index of the just-closed bucket since the state was first instantiated.
buckets_in_windowusizeNumber 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_volumeu64Configured bucket volume (shares).
buy_volumeu64Buyer-attributed share volume in the just-closed bucket.
sell_volumeu64Seller-attributed share volume in the just-closed bucket.

Configuration (VpinRequest)

Regenerated from the VpinRequest Rust source — see the note above.

FieldTypeDescription
contractsSecurityFilterContracts the subscription tracks. Stock contracts only; option / index / rate ticks are rejected at the dispatch gate.
bucket_volumeu64Target 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_sizeusizeSliding-window size — number of completed buckets averaged to produce the rolling VPIN value. Defaults to 50.
min_buckets_for_emitusizeWarmup 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.
classificationVpinClassificationTrade-side classifier.
conditionsConditionPolicyTrade-condition admission policy.
venuesExchangeFilterExchange / 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 RollingWindow is 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();

Proprietary. All rights reserved.