Appearance
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:
- Inherits the aggressor side from the same-cycle pre-trade NBBO (post-revision Lee-Ready+; see
AggressorSign). - Drops every buffered print older than
window_msms. - Counts distinct venues across the surviving prints.
- Sums contracts × price × 100 across the surviving prints.
- Fires a
SweepDetectorTickif 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
TradeTickadmitted byconditions+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.
| Field | Type | Description |
|---|---|---|
symbol | Arc<str> | Underlying symbol. |
expiration | Option<i32> | Expiration date (YYYYMMDD). None for non-option contracts (which should never produce a sweep — kept for schema parity). |
right | Option<char> | Option right — 'C' / 'P'. None for non-option. |
strike | Option<f64> | Strike in dollars. None for non-option. |
date | i32 | Trading session date (YYYYMMDD). |
cluster_start_ms | i32 | ms_of_day of the first print in the cluster. |
cluster_end_ms | i32 | ms_of_day of the last print in the cluster. |
aggressor | char | Aggressor side — 'B' for buy-side sweep, 'S' for sell-side. |
print_count | u32 | Number of prints in the cluster. |
venue_count | u32 | Number of distinct venues in the cluster. |
total_contracts | u64 | Total contracts traded across the cluster. |
total_premium_usd | f64 | Total premium (USD) across the cluster. |
per_venue | Vec<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.
| Field | Type | Description |
|---|---|---|
contracts | SecurityFilter | Contracts the subscription tracks. |
conditions | ConditionPolicy | Trade-condition admission policy. |
venues | ExchangeFilter | Exchange / venue admission policy. |
window_ms | i32 | Rolling 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_sweep | u32 | Minimum 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_usd | f64 | Minimum cumulative premium (dollars) for a sweep emission. Default 50_000.0 — filters out small bursts that just happen to cross multiple exchanges. |
max_window_trades | usize | Maximum 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)}`,
);
});