Skip to content

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:

  1. At-quote (Confidence::AtQuote) — price >= pre.ask → Buy; price <= pre.bid → Sell. Highest confidence; ~70% of options prints fall here.
  2. 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.
  3. Tick rule (Confidence::Tick) — sign of price − 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 TradeTick admitted by conditions + 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_price for 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.

FieldTypeDescription
symbolArc<str>Underlying symbol. Stock-only analytic — the symbol is the full user-facing identity for the print.
datei32Trading session date (YYYYMMDD).
ms_of_dayi32ms_of_day of the classified print.
pricef64Print price.
sizei32Print size (contracts).
exchangei32Exchange code that printed the trade.
aggressorcharAggressor side — 'B' (buy-side) or 'S' (sell-side).
confidenceStringClassification 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_bidf64Pre-trade NBBO bid.
pre_askf64Pre-trade NBBO ask.
post_midf64Post-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.

FieldTypeDescription
contractsSecurityFilterContracts the subscription tracks.
conditionsConditionPolicyTrade-condition admission policy.
venuesExchangeFilterExchange / 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}`,
  );
});

Proprietary. All rights reserved.