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.
How FlashAlpha detects options sweeps: same-side, same-contract OPRA prints coalesced within 500ms into one signal. Published rule with audit examples.
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.
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.
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.
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.
Neither outcome is acceptable for an unusual-activity feed. The coalescing rule is the deterministic preprocessing step that fixes both.
The rule is a single line of code in the scorer and a single sentence of policy:
Three conditions must hold simultaneously for two adjacent prints to join the same group:
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.
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.
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.
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.
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.
| Structure | Definition | Implies | Sweep component score |
|---|---|---|---|
sweep | Coalesced group of two or more prints | Multi-venue urgency. Trader paid for speed. | 1.00 |
block | Singleton print at or above block-size threshold | Patient single-venue execution. Size without urgency. | 0.55 |
single | Singleton sub-block print | Ordinary 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.
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.
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.
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.
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.
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.
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.
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.
Most unusual-activity vendors do not publish their sweep-detection rule. The few that do hint at it use one of three patterns:
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.
/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.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.
by Tomasz Dobrowolski
by Tomasz Dobrowolski
by Tomasz Dobrowolski
Get tick-by-tick visibility into market shifts with full-chain analytics streaming in real time.
Screen millions of option pairs per second using your custom EV rules, filters, and setups.
Instantly send structured orders to Interactive Brokers right from your scan results.