Skip to content

SweepDetector

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-contract burst-detection signal: a sweep is a sequence of prints on the same aggressor side, hitting at least min_venues_for_sweep distinct exchanges, within window_ms milliseconds, with cumulative premium ≥ min_notional_usd.

One emission per detected sweep cluster, carrying:

  • Aggressor side ('B' or 'S').
  • Print count, venue count, total contracts, total premium.
  • Per-venue contract breakdown (Vec<SweepVenueRow>).
  • Cluster start / end ms_of_day.

Sweeps are the institutional idiom for "execute a size order quickly enough that the dealer doesn't reprice." The cross-venue multiplexer is the discriminating signal — a single venue print of size N is a block; the same N spread across ≥ 3 venues inside 100 ms is a sweep.

Methodology

The analytic maintains a rolling per-contract trade buffer (max_window_trades slots, default 256). Each admitted print:

  1. Inherits the aggressor side from the same-cycle pre-trade NBBO (post-revision Lee-Ready+; see AggressorSign).
  2. Drops every buffered print older than window_ms ms.
  3. Counts distinct venues across the surviving prints.
  4. Sums contracts × price × 100 across the surviving prints.
  5. Fires a SweepDetectorTick if venue count and cumulative premium both clear their thresholds AND the latest print is on the same aggressor side as every other print in the window.

References:

  • SEC Rule 611 (Reg NMS) — the order-protection rule that motivates inter-exchange spraying.
  • OPRA Plan §7 — multi-venue print sequencing.

Inputs

  • Option TradeTick admitted by conditions + venues.
  • Per-cycle pre-trade NBBO snapshot for aggressor classification.

Output schema (SweepDetectorTick)

The field / type / description table below is regenerated from the SweepDetectorTick 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.
expirationOption<i32>Expiration date (YYYYMMDD). None for non-option contracts (which should never produce a sweep — kept for schema parity).
rightOption<char>Option right — 'C' / 'P'. None for non-option.
strikeOption<f64>Strike in dollars. None for non-option.
datei32Trading session date (YYYYMMDD).
cluster_start_msi32ms_of_day of the first print in the cluster.
cluster_end_msi32ms_of_day of the last print in the cluster.
aggressorcharAggressor side — 'B' for buy-side sweep, 'S' for sell-side.
print_countu32Number of prints in the cluster.
venue_countu32Number of distinct venues in the cluster.
total_contractsu64Total contracts traded across the cluster.
total_premium_usdf64Total premium (USD) across the cluster.
per_venueVec<SweepVenueRow>Per-venue contract count breakdown. Vector of (exchange, contracts) pairs sorted ascending by exchange code.

Configuration (SweepDetectorRequest)

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

FieldTypeDescription
contractsSecurityFilterContracts the subscription tracks.
conditionsConditionPolicyTrade-condition admission policy.
venuesExchangeFilterExchange / venue admission policy.
window_msi32Rolling window in milliseconds. Trades older than this from the latest admitted print drop out of the cluster. Default 100 ms — the institutional sweep convention.
min_venues_for_sweepu32Minimum distinct venues for a sweep emission. Default 2 — the SEC Reg NMS / Rule 611 intermarket-sweep threshold. Set to 1 to disable the multi-venue gate (single-venue mode: emissions fire on any same-side cluster above min_notional_usd, regardless of venue count).
min_notional_usdf64Minimum cumulative premium (dollars) for a sweep emission. Default 50_000.0 — filters out small bursts that just happen to cross multiple exchanges.
max_window_tradesusizeMaximum entries kept in the rolling window. Bounds memory growth on illiquid contracts that quote rarely but trade in bursts. Default 256.

Operational characteristics

  • Per-tick latency. O(max_window_trades) on every admitted print — bounded constant in the engine's hot path.
  • Allocation discipline. Rolling buffer is preallocated; the per-venue breakdown vector is the only per-emission allocation.
  • Replay parity. Deterministic given identical input tick order.

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.expiration}/{tick.right}/{tick.strike} "
        f"aggressor={tick.aggressor} venues={tick.venue_count} "
        f"premium=${tick.total_premium_usd:.0f}"
    )

sub = client.live().sweep_detector(["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().sweepDetector(["SPY"]).onEvent((tick) => {
  console.log(
    `${tick.symbol} ${tick.expiration}/${tick.right}/${tick.strike} ` +
      `aggressor=${tick.aggressor} venues=${tick.venueCount} ` +
      `premium=$${tick.totalPremiumUsd.toFixed(0)}`,
  );
});

Proprietary. All rights reserved.