Trade-Sign Classification¶
Infer the aggressor side of each trade when the feed doesn't label it.
L3 crypto (Bitstamp) ships buy_order_id / sell_order_id, so the trades
frame carries a real direction; L2 / aggregated feeds and many CCXT
sources don't, so the signed-flow metrics
(compute_vpin,
order_flow_imbalance)
have nothing to work with. These classifiers fill that gap and are wired in
as an automatic fallback.
Functions¶
classify_trade_sign ¶
classify_trade_sign(
trades: DataFrame,
method: str = "lee_ready",
quotes: DataFrame | None = None,
) -> pd.Series
Classify the aggressor side of each trade.
A drop-in source of the direction column for feeds that don't label
the aggressor. Sorts trades chronologically, applies the chosen
per-trade classifier, and returns the result realigned to the original
index.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
trades
|
DataFrame
|
Trades with at least |
required |
method
|
str
|
|
'lee_ready'
|
quotes
|
DataFrame
|
Required for |
None
|
Returns:
| Type | Description |
|---|---|
Series
|
Categorical |
Raises:
| Type | Description |
|---|---|
ConfigError
|
If method is unknown, if |
ObAnalyticsError
|
If trades is empty. |
tick_rule ¶
Classify trade signs by the tick rule.
Signs each trade from the sign of its price change relative to the
previous trade: an uptick is buyer-initiated (+1), a downtick
seller-initiated (-1). A zero tick (unchanged price) inherits
the last non-zero sign — the classic Lee–Ready convention.
prices must already be in trade order (chronological). A leading run
of zero ticks (before the first price move) is back-filled from the
first determinable sign; a perfectly flat series has no information and
defaults to +1.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
prices
|
ndarray or Series
|
Trade prices in chronological order. |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
|
lee_ready ¶
Classify trade signs by the Lee–Ready quote-midpoint test.
A trade above the prevailing mid is buyer-initiated (+1), below it
seller-initiated (-1). A trade at the mid — or one with no
prevailing quote (mid is NaN) — falls back to the
:func:tick_rule.
prices and mid must be equal-length and in chronological trade
order; mid is the quote midpoint prevailing at (or just before) each
trade — see :func:classify_trade_sign for aligning quotes to trades.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
prices
|
ndarray or Series
|
Trade prices in chronological order. |
required |
mid
|
ndarray or Series
|
Prevailing quote midpoint per trade ( |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
|
bulk_volume_classification ¶
bulk_volume_classification(
trades: DataFrame,
bucket_volume: float,
*,
sigma: float | None = None,
) -> pd.DataFrame
Split volume bars into buy / sell fractions (Easley–LdP–O'Hara BVC).
Partitions cumulative trade volume into equal-sized buckets (the same
volume bars :func:~ob_analytics.flow_toxicity.compute_vpin uses; a
trade straddling a boundary is split proportionally) and estimates each
bucket's buy fraction as
buy_fraction = Φ(ΔP / σ)
where ΔP is the bucket's close-to-close price change and σ the
standard deviation of those changes. Unlike a per-trade classifier this
labels volume, so it needs neither the aggressor side nor quotes — the
VPIN-native method for feeds that carry only trade prints.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
trades
|
DataFrame
|
Trades with |
required |
bucket_volume
|
float
|
Total volume per bucket (instrument-specific). |
required |
sigma
|
float
|
Standard deviation of bucketed price changes. Estimated from the data (sample std of the bucket ΔP series) when omitted. |
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, or sigma is not positive. |