Options Sweep Detection: The 500ms Coalescing Rule and Cross-Venue Routing | FlashAlpha

Options Sweep Detection: The 500ms Coalescing Rule and Cross-Venue Routing

How FlashAlpha detects options sweeps: same-side, same-contract OPRA prints coalesced within 500ms into one signal. Published rule with audit examples.

T
Tomasz Dobrowolski Quant Engineer
May 23, 2026
33 min read
OptionsSweeps BlockTrades OPRA ExecutionMicrostructure OptionsFlow API Methodology

If you have ever watched a fast-moving SPY contract get lifted across three exchanges in the same second, you have seen a sweep. To the underlying institutional order, it was one decision: "fill 2,000 contracts of this strike now, at whatever venues will quote them." To the OPRA tape, it looks like four prints landing in 80 milliseconds, each on a different exchange, each showing a fraction of the intended size. To a naive flow tracker, those four prints look like four independent trades.

That mismatch between intent and tape is the central problem in options sweep detection. Get it wrong and you either inflate the unusual-activity rank (counting one $5M order as four $1.25M orders) or lose it entirely (the largest single print being too small to qualify as a block on its own). The FlashAlpha Flow Signals API handles it with a deterministic coalescing rule. This is the article that explains the rule.

500ms
Coalescing window: same-side same-contract prints within this gap join one group
≥2
Prints in a coalesced group required for a sweep classification
100
Minimum block-size threshold (contracts) at the upstream notable-trade ring

What a Sweep Really Is

The textbook definition: a sweep is a multi-leg order that walks the inside markets of multiple exchanges in immediate succession to fill faster than a single-venue order could. In equity options that means the routing layer takes a single parent order, breaks it into child orders sized to the displayed liquidity at each exchange, and fires them off in parallel.

Cross-Venue Routing

In the US options market, sixteen exchanges quote on the same OPRA NBBO. A liquid contract typically has displayed depth at three to six exchanges simultaneously. When an institutional broker wants to fill a large order with minimum slippage, the order management system splits the parent into per-venue child orders sized to each exchange's displayed liquidity. The fills come back as separate prints on the OPRA consolidated tape.

The signal you care about is the parent intent, not the per-venue child print. The child prints are an artefact of how exchange routing works, not the trader's decision. A sweep is the trader's decision; a print is the exchange's record.

This is why the urgency signal in a sweep is so high. A trader willing to walk multiple venues is saying "fill me at whatever spread is showing, I do not have time to wait." Patient orders rest on one book at one venue and get a single print. Sweeps imply urgency, which usually implies information.

Why a Single-Print Scorer Gets It Wrong

Imagine a 2,000-lot SPY call sweep that prints as four 500-lot fills across PHLX, CBOE, AMEX, and MIAX in 60 milliseconds. A scorer that treats each print independently has two failure modes.

Failure mode 1: false amplification
  • One parent intent counted four times
  • Unusual-activity rank inflated 4x
  • Watchlist alerts fire four times for the same trade
  • Duplicate signals in any premium aggregation
  • Looks like a clustered storm of buying when it is one order
Failure mode 2: false dismissal
  • Each child print is sub-block-sized in isolation
  • Block filters miss the parent intent entirely
  • $2.5M parent sweep filed as four ordinary $625K prints
  • The actual urgency signal goes unrecorded
  • The signal you most want to see is the one you miss

Neither outcome is acceptable for an unusual-activity feed. The coalescing rule is the deterministic preprocessing step that fixes both.

The 500ms Coalescing Rule

The rule is a single line of code in the scorer and a single sentence of policy:

Coalescing Rule $$ \text{group}(t_i, t_{i+1}) \iff \text{contract}(t_i) = \text{contract}(t_{i+1}) \land \text{side}(t_i) = \text{side}(t_{i+1}) \land (t_{i+1} - t_i) \le 500\text{ms} $$

Three conditions must hold simultaneously for two adjacent prints to join the same group:

  1. Same contract. Different strike, different expiry, different right are different intents by definition.
  2. Same side. A buy followed by a sell, even at zero latency, is two opposing decisions. Side is preserved.
  3. Within 500ms of the prior print. The window is interval-to-interval, not relative to the group's first print. Two prints 300ms apart join; a third print 400ms after the second also joins; a fourth print 600ms after the third does not.

A coalesced group with two or more prints is classified as a sweep. A singleton block-sized print is a block. The combined group is treated as one execution event: the size is the sum, the price is the size-weighted average, the timestamp is the last print in the group.

The interval-to-interval rule (rather than first-to-last) is what makes the window robust to genuinely fast multi-venue routing. A real sweep that takes 1.4 seconds to finish across seven exchanges still coalesces correctly because no individual gap exceeds 500ms. A coincidental pair of unrelated trades 1.4 seconds apart does not.

Why 500 Milliseconds

The window is wide enough to catch most observed sweeps and narrow enough that unrelated prints rarely join in our calibration set. We arrived at it empirically, not theoretically.

Real Sweep Latency

Cross-venue routing on US options exchanges typically completes the full multi-venue fan-out in under 100 milliseconds. The first child print and the last child print of one parent order are usually separated by 30 to 80 milliseconds. The longest credibly-attributable sweep latencies we have measured are around 200 milliseconds, almost always involving venues with slower acknowledgement paths.

Coincidental Adjacency

Unrelated prints on the same liquid contract land at random times. On a busy 0DTE strike, the median time between block-sized prints is several seconds. The rate of two genuinely independent prints landing within 500ms on the same contract on the same side is low in our calibration set even on the busiest names; the rate at which a third unrelated print joins them is lower still.

500ms sits comfortably in the gap: well above any plausible sweep latency, well below any normal cadence of independent trades on the same contract. Setting the window to 100ms would risk splitting genuine sweeps on slower routing paths; setting it to 2 seconds would risk joining unrelated prints on the busiest names. 500ms is the deliberate, defensible middle.

The window is a private constant in the scorer (UnusualFlowScorer.SweepWindow). One source of truth, easy to retune if the microstructure shifts. We have not changed it since the feed launched; the residual rate of obvious splits and joins is low enough to leave the value alone.

Block vs Sweep vs Single: The Three-Way Distinction

Every scored signal in the feed gets one of three structure labels. Each carries different information about how the trade was executed and how strong an urgency signal it represents.

StructureDefinitionImpliesSweep component score
sweepCoalesced group of two or more printsMulti-venue urgency. Trader paid for speed.1.00
blockSingleton print at or above block-size thresholdPatient single-venue execution. Size without urgency.0.55
singleSingleton sub-block printOrdinary print. Rare in this feed because the upstream ring is block-only.0.20

The score weights are not arbitrary. Sweeps get the maximum because urgency is the strongest signal in the structure dimension; blocks get a substantial discount because size without speed is consistent with patient execution; singles exist only for upstream completeness and rarely appear because the notable-trade ring filters to block size at ingestion.

How the API Returns It

Every entry in the signals array of the Flow Signals response carries a structure field and the coalesced group's combined fields. Inspecting a single signal tells you whether it was one print or many.

{
  "ts": "2026-05-22T18:14:32.421Z",
  "expiry": "2026-05-22",
  "strike": 587.0,
  "right": "C",
  "side": "buy",
  "price": 1.43,
  "size": 2000,
  "premium": 286000.0,
  "dte": 0,
  "structure": "sweep",
  "aggressor": "at_ask",
  "open_close_bias": "opening_bias",
  "open_close_confidence": 0.43,
  "intent": "bullish",
  "score": 81,
  "conviction": "high",
  "tags": ["sweep", "opening", "0dte", "golden"],
  "score_breakdown": {
    "premium": 15,
    "size_vs_oi": 14,
    "aggressor": 14,
    "sweep": 18,
    "opening_bias": 9,
    "tenor": 11
  },
  "enrichment": { /* greeks + IV vs ATM + delta notional */ }
}

That single record represents four child prints that landed within 240ms total. The size is the parent sum, the price is the size-weighted average, the timestamp is the last child. The tag list includes sweep, which is true for any structure-coalesced group with at least two prints, and the score_breakdown.sweep bucket carries the maximum-weight contribution from this dimension.

Audit a coalesced signal yourself

Pull the same window through /v1/flow/options/{symbol}/recent on the raw flow surface and you will see the four child prints separately. Match them by contract, side, and timestamps within 500ms - they should sum to the size and average to the price reported on the single coalesced signal. The pipeline is deterministic; the reconciliation matches within timestamp and rounding tolerance.

How to Filter for Sweeps Only

Pass structure=sweep to the signals endpoint to drop blocks and singles. Combine with a score floor and an intent filter for a focused alert ruleset.

import requests

resp = requests.get(
    "https://lab.flashalpha.com/v1/flow/signals/SPY",
    headers={"X-Api-Key": "YOUR_KEY"},
    params={
        "structure": "sweep",
        "intent": "bullish",
        "minScore": 70,
        "windowMinutes": 60,
    },
)

for sig in resp.json()["signals"]:
    n_prints_estimated = "multiple" if sig["structure"] == "sweep" else "1"
    print(
        f"{sig['ts']}  {sig['expiry']} {sig['strike']}{sig['right']}"
        f"  size={sig['size']:>5} (sweep group)"
        f"  ${sig['premium']:>10,.0f}"
        f"  score={sig['score']}"
        f"  {','.join(sig['tags'])}"
    )

The opposite filter, structure=block, gives you the patient single-venue side of unusual activity. Both filters together (which is the default - leave structure unset) give you the full feed.

Edge Cases and Honest Limitations

The coalescing rule is a heuristic. It is right almost all of the time and worth knowing the boundary cases for the times it is not.

Genuine Independent Prints Within 500ms

On the very busiest contracts (SPY 0DTE near close, mega-cap earnings nights) two unrelated traders occasionally happen to print same-side block trades within the window. The coalescer joins them. The result is one signal with combined size from two different parents. Detection cost: the size is real (it really did print that aggregated quantity in 500ms) but the "single intent" interpretation is wrong. There is no clean way to separate them on the OPRA tape.

Slow Multi-Venue Routes

A few exchanges have acknowledgement paths slow enough that the final child print of a sweep can land beyond 500ms after the first. The coalescer treats the late child as a separate event. The resulting two signals (the early coalesced group and the late singleton) report a smaller-than-actual parent size. The bias is conservative - splitting a sweep is preferred to joining unrelated prints.

Cross-Side Sweeps Are Two Signals

A trader simultaneously buying calls and selling puts (a synthetic long) shows up as two independent same-side groups by design. Each leg is scored separately. The combined intent is unclear from the tape alone, so the scorer does not assume a structure. Multi-leg structure inference is a candidate for future work but is not in scope at the coalescer layer.

None of these failure modes break the methodology in aggregate. The dominant cost is occasional split sweeps on slow venues - which under-counts size but does not corrupt direction or intent. The residual rate is low enough that we have not retuned the window. If you have a workflow where it matters, the field structure tells you which classification fired and you can run any deduplication you want against the raw /v1/flow/options/{symbol}/recent surface.

Comparing With Other Vendor Approaches

Most unusual-activity vendors do not publish their sweep-detection rule. The few that do hint at it use one of three patterns:

Exchange-tag heuristics
  • Tag a print as "sweep" if the exchange feed marks it
  • Tagging coverage varies by venue and order type
  • Misses sweeps when routing splits but no venue tags it
  • False positives on retail "intermarket sweep" tagged orders
Time-only joins (no contract or side check)
  • Group every print within a window
  • Conflates unrelated contracts on busy names
  • Joins opposite sides into nonsense "sweep" intents
FlashAlpha (contract + side + 500ms interval-to-interval)
  • Same-contract requirement: never conflates strikes or expiries
  • Same-side requirement: preserves direction
  • Interval-to-interval window: robust to slow venues
  • Coalesced group reported as one signal, fields documented
  • Audit against raw trades is a request away

Our approach is closer to how the order books actually work than the exchange-tag method, and far stricter than time-only joins. The whole point is that "what was the trader trying to do" is recoverable from the tape with a careful enough rule, and a 500ms same-contract same-side interval is the rule we settled on.

Frequently Asked Questions

A single parent order on one options contract that crosses multiple US options exchanges in rapid succession to fill faster than any single venue could. The OPRA tape sees it as several prints, one per venue. The trader's intent was one decision. Sweeps imply execution urgency, which usually implies information.
Same-contract same-side prints whose timestamps fall within 500 milliseconds of the previous print join one group. A coalesced group with two or more prints is classified as a sweep. The window is interval-to-interval, so a real seven-exchange sweep over 1.4 seconds still coalesces correctly because no single gap exceeds 500ms.
A sweep is a coalesced group of two or more prints across one contract within the 500ms interval-to-interval window. A block is a singleton block-sized print that did not coalesce with any neighbour. Both imply size; only the sweep adds an explicit urgency signal because the trader paid for cross-venue speed. The sweep component of the score is 1.00, the block component is 0.55.
Empirical observation. Real cross-venue sweeps almost always finish in under 100 milliseconds; the slowest credibly-attributable sweeps we have measured run around 200ms. Independent prints on the same liquid contract are usually seconds apart even on busy names. 500ms sits well above plausible sweep latency and well below typical independent-print cadence, with a conservative bias toward not joining unrelated prints.
Direction comes from the intent classifier, not the sweep structure. A buy-call sweep is bullish, a sell-call sweep is bearish, a buy-put sweep is bearish, a sell-put sweep is bullish. Closing-bias sweeps (the OI simulator says the position is being unwound) collapse to Neutral intent because the tape generally cannot tell which direction the original position was. Sweep is the structure signal; intent is the direction signal.
The signals endpoint returns the coalesced group as one signal. To see the individual child prints, pull the raw trade surface at /v1/flow/options/{symbol}/recent for the same time window. Match by contract, side, and timestamps within 500ms; the sizes will sum and the prices will average back to the coalesced signal's fields. The pipeline is deterministic and the reconciliation matches within timestamp and rounding tolerance.
Almost certainly not in the legal sense. A sweep signals an institutional participant willing to pay for cross-venue speed, which usually implies a time-sensitive view: positioning ahead of an earnings call they have already analysed, hedging an existing position before a known event, or executing a tactical trade. The dominant drivers are informed but legitimate decision-making (quant model firing, research-team conviction trade, delta-hedger rebalancing fast). Illegal insider trading is a vanishingly small fraction of sweep volume and is invisible from the tape - the regulator-visible cases are pursued after the fact via clearing-firm records, not by reading OPRA.
Direction comes from the intent classifier, not the sweep structure. A buy-call sweep is bullish, a sell-call sweep is bearish, a buy-put sweep is bearish, a sell-put sweep is bullish - and any closing-bias sweep collapses to Neutral because the tape cannot recover the original direction. The sweep tag tells you "this was an urgent execution"; the intent tag tells you "this is the direction it implies (if any)". Treat them as separate signals: a low-conviction bullish print is not the same as an urgent bullish sweep, and a bullish-direction sweep that is actually closing a short is not the same as a fresh bullish bet.
Like every signal, sweeps have signal and noise. The score-based filtering helps: a high-conviction sweep (score ≥ 80, opening bias, large size-vs-OI) is more interesting than a low-score sweep on a sleepy contract. The most useful sweep alerts combine the sweep structure with the intent classifier and the chain context (does it hit a wall, is it near the gamma flip, what is the dealer regime). Sweeps in isolation are a screening signal; sweeps cross-referenced against dealer positioning are a trade hypothesis. The audit-able score_breakdown lets you tune the threshold that matches your tolerance for false positives.

Related Reading

Get Alpha access

The Flow Signals endpoints are gated to the Alpha plan. The same key unlocks raw flow data, advanced volatility analytics, VRP analytics, and the live OI simulator state. View pricing, or open the interactive playground to try the endpoints before subscribing.

Live Market Pulse

Get tick-by-tick visibility into market shifts with full-chain analytics streaming in real time.

Intelligent Screening

Screen millions of option pairs per second using your custom EV rules, filters, and setups.

Execution-Ready

Instantly send structured orders to Interactive Brokers right from your scan results.

Join the Community

Discord

Engage in real time conversations with us!

Twitter / X

Follow us for real-time updates and insights!

GitHub

Explore our open-source SDK, examples, and analytics resources!