Flow Toxicity Metrics¶
Market microstructure measures for detecting informed trading and quantifying price impact.
Functions¶
compute_vpin ¶
compute_vpin(
trades: DataFrame,
bucket_volume: float,
n_buckets: int = 50,
sign_method: str | None = None,
quotes: DataFrame | None = None,
) -> pd.DataFrame
Compute the Volume-Synchronized Probability of Informed Trading.
Partitions cumulative trade volume into equal-sized buckets and
measures the normalised buy/sell imbalance within each bucket. The
trailing average of vpin over n_buckets is the headline VPIN
metric.
Works on feeds without a native aggressor side. When the trades frame
has no direction, the buy/sell split is inferred with a trade-sign
classifier (see sign_method), so VPIN runs on L2 / aggregated captures
too.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
trades
|
DataFrame
|
Trades with at least |
required |
bucket_volume
|
float
|
Total volume per bucket. This is highly instrument-specific — a reasonable starting point is average daily volume / 50. |
required |
n_buckets
|
int
|
Window length (in buckets) for the trailing VPIN average. Default is 50, following the original paper. |
50
|
sign_method
|
str
|
How to obtain the buy/sell split when there is no native
|
None
|
quotes
|
DataFrame
|
Quote frame for |
None
|
Returns:
| Type | Description |
|---|---|
DataFrame
|
One row per completed bucket with columns:
|
Raises:
| Type | Description |
|---|---|
ConfigError
|
If required columns are missing. |
ObAnalyticsError
|
If trades is empty. |
ValueError
|
If bucket_volume is not positive. |
compute_kyle_lambda ¶
Estimate Kyle's Lambda via OLS regression.
For each time window, computes:
- ΔPrice = last trade price − first trade price
- signed_volume = Σ(buy volume) − Σ(sell volume)
Then regresses ΔPrice on signed_volume across all windows. The slope (λ) measures how much the price moves per unit of net order flow — a proxy for market illiquidity and adverse selection.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
trades
|
DataFrame
|
Trades with |
required |
window
|
str
|
Pandas frequency string for grouping trades. Default |
'5min'
|
Returns:
| Type | Description |
|---|---|
KyleLambdaResult
|
Frozen dataclass with |
Raises:
| Type | Description |
|---|---|
ConfigError
|
If required columns are missing. |
ObAnalyticsError
|
If trades is empty. |
order_flow_imbalance ¶
order_flow_imbalance(
trades: DataFrame,
window: str = "1min",
sign_method: str | None = None,
quotes: DataFrame | None = None,
) -> pd.DataFrame
Compute normalised order flow imbalance per time window.
For each window:
ofi = (buy_volume − sell_volume) / (buy_volume + sell_volume)
Values range from −1 (all sells) to +1 (all buys). Zero indicates balanced flow.
Works on feeds without a native aggressor side: when the trades frame
has no direction, it is inferred with a per-trade trade-sign
classifier (see sign_method).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
trades
|
DataFrame
|
Trades with |
required |
window
|
str
|
Pandas frequency string. Default |
'1min'
|
sign_method
|
str
|
How to obtain the buy/sell split when there is no native
|
None
|
quotes
|
DataFrame
|
Quote frame for |
None
|
Returns:
| Type | Description |
|---|---|
DataFrame
|
Columns: |
Raises:
| Type | Description |
|---|---|
ConfigError
|
If required columns are missing. |
ObAnalyticsError
|
If trades is empty. |
Models¶
KyleLambdaResult
dataclass
¶
KyleLambdaResult(
lambda_: float,
t_stat: float,
r_squared: float,
n_windows: int,
regression_df: DataFrame = pd.DataFrame(),
)
Result of a Kyle's λ OLS regression.
Attributes:
| Name | Type | Description |
|---|---|---|
lambda_ |
float
|
Slope — price change per unit signed order flow (higher = less liquid). |
t_stat |
float
|
t-statistic for |
r_squared |
float
|
Fraction of ΔPrice variance explained by signed order flow. |
n_windows |
int
|
Number of time windows in the regression. |
regression_df |
DataFrame
|
Per-window |