Appearance
AggressorSign
Preview on the public Rust client. The analytic ships in the engine and is reachable today through the Python wheel and the TypeScript / Node addon; a per-analytic builder on the public Rust thin client (
kairos::Client) is tracked on the roadmap. The output tick schema below is stable.
What it computes
Per-option-print aggressor classification — 'B' (buy-side) or 'S' (sell-side) — with a confidence tier per emission. Improves on the classical Lee–Ready 1991 algorithm by reading the post-trade quote revision in addition to the pre-trade NBBO mid.
Published classification accuracy on the post-revision rule is roughly 95% on options, vs ~85% on the Lee–Ready baseline; the lift comes from observing the dealer's reaction (offer pulled / bid raised) and attributing the print to the side the dealer revised against.
Methodology
The classifier is tiered — fall-through stops at the first confident tier:
- At-quote (
Confidence::AtQuote) —price >= pre.ask→ Buy;price <= pre.bid→ Sell. Highest confidence; ~70% of options prints fall here. - Post-quote revision (
Confidence::PostRevision) —post.mid > pre.mid→ Buy (dealer raised the bid/ask after the print);post.mid < pre.mid→ Sell. Captures the ~25% of options prints that print between the pre-trade bid/ask but trigger a quote revision. - Tick rule (
Confidence::Tick) — sign ofprice − last_price. Fallback when the prior two rules cannot resolve a side. ~5% of prints.
References:
- Lee, Ready (1991). Inferring Trade Direction from Intraday Data. Journal of Finance 46(2): 733–746.
- Holden, Jacobsen (2014). Liquidity Measurement Problems in Fast, Competitive Markets. Journal of Finance 69(4): 1747–1785 — contemporary classifier review.
- Easley, Kiefer, O'Hara (1996). Cream-Skimming or Profit-Sharing? The Curious Role of Purchased Order Flow. Journal of Finance 51(3): 811–833.
See /methodology/lee-ready for the formula derivation.
Inputs
- Option
TradeTickadmitted byconditions+venues. - Pre-trade NBBO snapshot (per the engine's cycle-phase tracker).
- Post-trade NBBO snapshot (next quote on the same cycle).
- Cell-local
last_pricefor the tick-rule fall-through.
Output schema (AggressorSignTick)
The field / type / description table below is regenerated from the AggressorSignTick 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 |
|---|---|---|
symbol | Arc<str> | Underlying symbol. Stock-only analytic — the symbol is the full user-facing identity for the print. |
date | i32 | Trading session date (YYYYMMDD). |
ms_of_day | i32 | ms_of_day of the classified print. |
price | f64 | Print price. |
size | i32 | Print size (contracts). |
exchange | i32 | Exchange code that printed the trade. |
aggressor | char | Aggressor side — 'B' (buy-side) or 'S' (sell-side). |
confidence | String | Classification confidence tier: - "at_quote" — print was at or beyond the pre-trade NBBO; ~70% of options prints. - "post_revision" — post-quote tightening revealed the side; ~25% of options prints. - "tick_rule" — fallback when neither prior rule fires. |
pre_bid | f64 | Pre-trade NBBO bid. |
pre_ask | f64 | Pre-trade NBBO ask. |
post_mid | f64 | Post-trade NBBO mid that triggered the post-revision branch. f64::NAN when the classification came from at_quote or tick_rule. |
Configuration (AggressorSignRequest)
Regenerated from the AggressorSignRequest Rust source — see the note above.
| Field | Type | Description |
|---|---|---|
contracts | SecurityFilter | Contracts the subscription tracks. |
conditions | ConditionPolicy | Trade-condition admission policy. |
venues | ExchangeFilter | Exchange / venue admission policy. |
Operational characteristics
- Per-tick latency. One bid/ask compare + one mid compare in the common case; cell-local read for the tick-rule fall-through.
- Allocation discipline. No allocations on the hot path. The pending-classification slot is a single
Option<...>field on the analytic instance. - Replay parity. Deterministic given identical input tick order (specifically: pre-trade quote → trade → post-trade quote arrives on the same dispatch cycle).
Example
Preview. This analytic ships in the engine and is reachable today through the Python wheel and the TypeScript / Node addon. A per-analytic builder on the public Rust thin client (
kairos::Client) is tracked on the roadmap — bare-string symbol filters passed to the analytic accessor will match the other analytics already exposed there. The output tick rows are stable and documented above.
Cross-language surface
python
import kairos_thetadata as kt
client = kt.Client.connect(kt.Credentials.from_env())
def on_event(tick):
print(
f"{tick.symbol} {tick.ms_of_day} px={tick.price:.2f} "
f"aggressor={tick.aggressor} confidence={tick.confidence}"
)
sub = client.live().aggressor_sign(["SPY"]).on_event(on_event)
sub.wait(timeout_seconds=60.0)typescript
import { Client, Credentials } from "kairos-thetadata";
const client = await Client.connect(Credentials.fromEnv());
const sub = await client.live().aggressorSign(["SPY"]).onEvent((tick) => {
console.log(
`${tick.symbol} ${tick.msOfDay} px=${tick.price.toFixed(2)} ` +
`aggressor=${tick.aggressor} confidence=${tick.confidence}`,
);
});