Mass Cancels and Purge Ports Explained
How fast can a market maker pull every quote? Inside mass cancel messages, dedicated purge ports, cancel on disconnect, and the gateway queue in the way.
A mass cancel is one instruction that removes a whole set of a firm's resting orders at once, and a purge port is a dedicated exchange connection whose only job is to carry that instruction. Market makers build on both for a reason that has little to do with wire speed. The moment a quote needs to come down is the same moment every other participant is pushing messages into the same order-entry gateway, and a cancel sitting in that line can reach the matching engine after the trade it was meant to prevent.
What is a mass cancel, and what is a purge port?
Three different things get called cancelling on a trading desk, and they behave nothing alike under stress.
- A single-order cancel names one live order by its exchange order ID. Pulling 500 resting quotes takes 500 messages, and each one queues behind the last.
- A mass cancel is one message the exchange fans out across many orders on the firm's behalf. Cboe's US equities Purge Orders message cancels open orders across all of a member's sessions at once, filtered by MPID, by symbol, or by RiskGroupID, a tag the firm attaches to individual orders at entry. Up to ten RiskGroupIDs fit in a single message, and the RiskGroupID filter cannot be combined with the symbol filter.
- A purge port is the path that message travels on. Cboe accepts Purge Orders only on dedicated purge ports. Nasdaq's options markets run the same idea under another name: an SQF Purge port is a purge-only port on the Specialized Quote Feed, the quoting interface used by options market makers, and it carries purge requests and purge notifications alone.
Futures split the job differently. On CME Globex a Quote Cancel (FIX message type Z) pulls resting mass quotes at four levels: a single instrument, a group such as ES, a quote set, or every quote the session holds. An Order Mass Action Request (message type CA) cancels working orders for a SenderCompID per instrument, per instrument group, or per market segment. The two cover different inventory. CME documents that the mass order cancel does not cancel mass quotes, which matters a great deal to a firm that wired its panic button to one message and assumed it swept the whole book.
Why the ordinary cancel path is slowest exactly when it matters
An order-entry session is a queue. The gateway reads messages in arrival order, and it is shared infrastructure sized for an ordinary day. Raw distance to the matching engine is the part everyone measures. Contention for the gateway is the part that decides the outcome, and contention peaks on the same minutes that make the cancel urgent.
The two sessions below are pinned to fixed dates. April 7, 2025 was a high-volatility session. June 10, 2025 was an ordinary one. Both are counted on the same New York clock in ten-minute buckets, from the consolidated quote feed for AAPL.
| et_time | busy_day_quotes_per_sec | quiet_day_quotes_per_sec | busy_to_quiet_ratio |
|---|---|---|---|
| 08:00 | 4 | 1 | 6.4 |
| 08:10 | 2 | 0 | 7.6 |
| 08:20 | 4 | 1 | 4.7 |
| 08:30 | 3 | 0 | 6.6 |
| 08:40 | 4 | 0 | 10.6 |
| 08:50 | 3 | 1 | 4.1 |
| 09:00 | 3 | 1 | 4.2 |
| 09:10 | 4 | 1 | 6.6 |
| 09:20 | 13 | 2 | 8.3 |
| 09:30 | 193 | 59 | 3.3 |
| 09:40 | 174 | 64 | 2.7 |
| 09:50 | 145 | 60 | 2.4 |
| 10:00 | 157 | 54 | 2.9 |
| 10:10 | 239 | 47 | 5 |
| 10:20 | 150 | 33 | 4.6 |
| 10:30 | 150 | 27 | 5.6 |
| 10:40 | 117 | 31 | 3.7 |
| 10:50 | 89 | 35 | 2.6 |
| 11:00 | 101 | 36 | 2.8 |
| 11:10 | 138 | 27 | 5.1 |
The exact SQL behind every number
SELECT
concat(leftPad(toString(intDiv(et_minute, 60)), 2, '0'), ':',
leftPad(toString(et_minute % 60), 2, '0')) AS et_time,
toUInt32(round(countIf(session_date = '2025-04-07') / 600)) AS busy_day_quotes_per_sec,
toUInt32(round(countIf(session_date = '2025-06-10') / 600)) AS quiet_day_quotes_per_sec,
round(countIf(session_date = '2025-04-07')
/ greatest(countIf(session_date = '2025-06-10'), 1), 1) AS busy_to_quiet_ratio
FROM
(
SELECT
toDate(toTimeZone(sip_timestamp, 'America/New_York')) AS session_date,
intDiv(toHour(toTimeZone(sip_timestamp, 'America/New_York')) * 60
+ toMinute(toTimeZone(sip_timestamp, 'America/New_York')), 10) * 10 AS et_minute
FROM global_markets.cache_stocks_quotes
WHERE ticker = 'AAPL'
AND (
(sip_timestamp >= '2025-04-07 12:00:00' AND sip_timestamp < '2025-04-07 21:00:00')
OR (sip_timestamp >= '2025-06-10 12:00:00' AND sip_timestamp < '2025-06-10 21:00:00')
)
)
GROUP BY et_minute
ORDER BY et_minuteIn the ten minutes beginning 09:30 ET, the AAPL quote feed ran at 193 updates a second on the volatile session, against 59 in the same ten minutes of the quiet one, a factor of 3.3. Every one of those updates began life as an inbound message to some exchange's gateway, and that is one symbol out of the several thousand a large quoting firm keeps live.
Traffic on its own is survivable. The second panel measures what the price does over the identical buckets.
| et_time | busy_day_travel_pct | quiet_day_travel_pct |
|---|---|---|
| 08:00 | 6.62 | 1.08 |
| 08:10 | 0.58 | 0.06 |
| 08:20 | 2.12 | 0.28 |
| 08:30 | 0.56 | 0.09 |
| 08:40 | 1.27 | 0.08 |
| 08:50 | 0.63 | 0.16 |
| 09:00 | 0.74 | 0.2 |
| 09:10 | 0.66 | 0.14 |
| 09:20 | 4.29 | 0.53 |
| 09:30 | 1.68 | 1.29 |
| 09:40 | 2.72 | 0.47 |
| 09:50 | 1.63 | 0.62 |
| 10:00 | 1.66 | 0.58 |
| 10:10 | 7.17 | 0.42 |
| 10:20 | 4.22 | 0.21 |
| 10:30 | 3.46 | 0.25 |
| 10:40 | 1.79 | 0.45 |
| 10:50 | 1.88 | 0.45 |
| 11:00 | 2.4 | 0.58 |
| 11:10 | 3.98 | 0.29 |
The exact SQL behind every number
SELECT
concat(leftPad(toString(intDiv(et_minute, 60)), 2, '0'), ':',
leftPad(toString(et_minute % 60), 2, '0')) AS et_time,
round(maxIf(travel_pct, session_date = '2025-04-07'), 2) AS busy_day_travel_pct,
round(maxIf(travel_pct, session_date = '2025-06-10'), 2) AS quiet_day_travel_pct
FROM
(
SELECT
session_date,
et_minute,
100 * (toFloat64(max(high)) - toFloat64(min(low))) / toFloat64(min(low)) AS travel_pct
FROM
(
SELECT
toDate(toTimeZone(window_start, 'America/New_York')) AS session_date,
intDiv(toHour(toTimeZone(window_start, 'America/New_York')) * 60
+ toMinute(toTimeZone(window_start, 'America/New_York')), 10) * 10 AS et_minute,
high,
low
FROM global_markets.delayed_stocks_minute_aggs
WHERE ticker = 'AAPL'
AND (
(window_start >= '2025-04-07 12:00:00' AND window_start < '2025-04-07 21:00:00')
OR (window_start >= '2025-06-10 12:00:00' AND window_start < '2025-06-10 21:00:00')
)
)
GROUP BY session_date, et_minute
)
GROUP BY et_minute
ORDER BY et_minuteOver that opening bucket the stock covered 1.68% of its own price between the bucket's high and its low, against 1.29% on the quiet session. Set the two panels next to each other and the market maker's problem takes a shape. The buckets carrying the heaviest message traffic are the buckets over which the price travels farthest. A queueing delay that costs nothing on a quiet afternoon is expensive inside a window where the tape is covering that much ground, since a stale quote stays executable for every millisecond it remains up.
What actually makes a purge port fast
The speed comes from the emptiness of the path rather than from its length. A purge port accepts a narrow set of messages, carries almost no traffic on an ordinary day, and terminates on capacity that order-entry flow never touches. Nothing can be in front of the cancel. Cboe restricts identical purge requests to twenty per second per port, which tells you what the port is for: one message facing an empty queue, never throughput.
The Cboe message also carries an optional self-imposed lockout, which blocks the firm's new order entry after the purge until the firm deliberately clears it. A desk that has just pulled everything rarely wants its own automated quoter refilling the book a millisecond later. RiskGroupID adds a second dimension to the same tooling: orders are tagged by strategy or book at entry, and a purge can then remove one strategy's inventory while the rest keeps quoting. For the connectivity and capital side of assembling a quoting operation, how to become a market maker covers the ground, and order modification and queue priority covers what a cancel and replace costs you in the book.
What pulling quotes looks like from outside
A basis point is one hundredth of one percent, the standard unit for quoted spreads. The panel below zooms into the first hour of the volatile session, minute by minute, measuring the distance between the best bid and the best offer on the consolidated feed.
| et_time | avg_spread_bps | p90_spread_bps |
|---|---|---|
| 09:30 | 6.56 | 10.78 |
| 09:31 | 5.09 | 7.97 |
| 09:32 | 5.68 | 9.05 |
| 09:33 | 4.58 | 7.36 |
| 09:34 | 4.24 | 6.78 |
| 09:35 | 3.18 | 5.09 |
| 09:36 | 3.23 | 4.53 |
| 09:37 | 2.89 | 4.51 |
| 09:38 | 2.89 | 4.51 |
| 09:39 | 2.72 | 3.96 |
| 09:40 | 3.05 | 4.51 |
| 09:41 | 3.38 | 5.09 |
| 09:42 | 3.5 | 5.12 |
| 09:43 | 2.94 | 4.55 |
| 09:44 | 3.13 | 5.09 |
| 09:45 | 2.63 | 3.97 |
| 09:46 | 2.93 | 4.5 |
| 09:47 | 2.9 | 4.49 |
| 09:48 | 3.2 | 5.05 |
| 09:49 | 2.83 | 3.92 |
The exact SQL behind every number
SELECT
formatDateTime(toStartOfMinute(toTimeZone(sip_timestamp, 'America/New_York')), '%H:%i') AS et_time,
round(avg(spread_bps), 2) AS avg_spread_bps,
round(quantileDeterministic(0.90)(spread_bps, toUInt64(sequence_number)), 2) AS p90_spread_bps
FROM
(
SELECT
sip_timestamp,
sequence_number,
20000 * (toFloat64(ask_price) - toFloat64(bid_price))
/ (toFloat64(ask_price) + toFloat64(bid_price)) AS spread_bps
FROM global_markets.cache_stocks_quotes
WHERE ticker = 'AAPL'
AND sip_timestamp >= '2025-04-07 13:30:00'
AND sip_timestamp < '2025-04-07 14:30:00'
AND bid_price > 0
AND ask_price > bid_price
)
GROUP BY et_time
ORDER BY et_timeThe minute beginning 09:30 ET carried an average quoted spread of 6.56 basis points, with the ninetieth percentile quote at 10.78. By 10:29 the average had settled to 4.23. Wide quoted spreads in the opening minutes are the state a book sits in while liquidity providers withdraw tight quotes and re-post further out. The cancel machinery in this post is what that withdrawal is made of.
The breadth problem in options market making
An equity market maker may hold a few hundred orders live. An options market maker quotes a two-sided market in every series it is assigned, across every expiry and strike on its underlyings, and the count of series is the entire problem.
| underlying_symbol | contracts_traded |
|---|---|
| NVDA | 3628 |
| MSFT | 1751 |
| AAPL | 1680 |
| KO | 433 |
| PG | 289 |
The exact SQL behind every number
SELECT
underlying_symbol AS underlying_symbol,
uniqExact(ticker) AS contracts_traded
FROM global_markets.options_trades
WHERE underlying_symbol IN ('AAPL', 'MSFT', 'NVDA', 'KO', 'PG')
AND sip_timestamp >= '2025-04-07 12:00:00'
AND sip_timestamp < '2025-04-07 21:00:00'
GROUP BY underlying_symbol
ORDER BY contracts_traded DESCOn that same session, NVDA printed trades in 3628 distinct option contracts, while PG printed 289. Contracts that traded are a floor on contracts that were quoted, since a market maker posts in many series that never print. One cancel message per contract turns a risk control into a long sequence of round trips, which is why the options purge is scoped by underlying or by assigned series, and why a quoting protocol such as SQF carries purge as a first-class message rather than as a loop over order IDs.
Cancel on disconnect is a safety net, not a tool
Cancel on disconnect (COD) is the last layer, and the one most often mistaken for a cancel button. COD fires on session loss: the exchange notices the connection has dropped and cancels the resting orders registered to that session. Nasdaq is blunt about the limits in its own documentation. The service is a best-effort attempt with no guarantee of operating without interruption, registration happens per session ID through the member portal, and a sequenced logoff on OUCH 3.1 or 4.0 does not initiate a cancel on disconnect at all. Configuration options let a member keep GTC orders or auction orders alive through a COD event.
Read the trigger condition again and the limitation is plain. COD answers the question "my connectivity died". It has nothing to say about "my pricing is stale while my session is perfectly healthy", which is the situation a purge port exists for. Dropping a session deliberately to invoke COD is possible, though it is the slowest cancel available and it blinds the desk to its own fills at the same time.
How this connects to Rule 15c3-5
SEC Rule 15c3-5, the market access rule, requires a broker-dealer with access to an exchange to maintain risk management controls under its direct and exclusive control, reasonably designed to limit financial and regulatory exposure, with an annual certification by the chief executive. Most of the attention on that rule goes to pre-trade checks that stop an order before it reaches the market: credit and capital thresholds, order size caps, price collars, and duplicate order detection.
Mass cancel tooling is the other half of the same obligation, the half that deals with orders already resting. Exchanges automate a version of it themselves. Nasdaq's SQF interface delivers risk protection triggers and purge notifications on the same port that accepts a manual purge, and Cboe pairs order-level risk profiles with purge functionality, giving a member a way to pull an entire RiskGroupID once a threshold is breached.
The shape of a market maker's worst day
A mispriced quote costs the width of the market. A quote that cannot be pulled while the tape runs costs the distance the tape travels, and the panels above put a number on that distance. That asymmetry is why cancel infrastructure draws more engineering attention on a quoting desk than pricing usually does. Why market makers lose money works through the loss arithmetic, and how market makers make money covers the revenue side these controls protect. A firm quoting the same name on several venues also has to keep its own orders from meeting each other during a pull, which is the subject of self match prevention and wash trades.
Data notes and method
Both sessions are pinned to fixed past dates, so these panels do not move: April 7, 2025 for the volatile session and June 10, 2025 for the quiet one. Quote counts are updates on the consolidated feed for AAPL between 08:00 and 17:00 New York time, divided by the 600 seconds in each ten-minute bucket, which is why premarket buckets read low on both days. Price travel is the high-to-low range within each bucket, taken from one-minute bars and expressed as a percentage of the bucket low. The spread panel covers one hour at one-minute resolution and drops any quote without a positive bid below its offer. The options panel counts distinct contracts that printed at least one trade, a floor on the number quoted. The first two panels share 54 clock buckets, aligned one to one.
FAQ
What is a purge port?
A purge port is a dedicated exchange connection that accepts cancel instructions and nothing else. Cboe accepts its US equities Purge Orders message only on purge ports, and Nasdaq's options markets offer purge-only ports on the SQF quoting interface. The value is an uncontended path, with no order-entry traffic able to sit in front of the cancel.
How is a mass cancel different from cancelling orders one at a time?
A single-order cancel names one order, so pulling 500 quotes takes 500 messages that queue in sequence. A mass cancel is one message the exchange expands on the firm's behalf across a scope such as an MPID, a symbol, a risk group, an instrument group, or every live quote on the session.
Does cancel on disconnect protect a market maker?
It covers one failure, the session dropping. Nasdaq describes the service as a best-effort attempt with no guarantee, and a clean sequenced logoff on OUCH does not invoke it. A firm whose connection is healthy and whose prices are stale gets nothing from it, and that gap is what purge ports fill.
Are mass cancels the same on futures exchanges?
The scopes differ. CME Globex separates a Quote Cancel, which removes resting mass quotes down to one instrument or up to every quote on the session, from an Order Mass Action Request, which cancels working orders by instrument, instrument group, or market segment. The mass order cancel does not remove mass quotes, which leaves a futures market maker relying on both messages.
Why do exchanges rate limit purge messages?
Cboe restricts identical purge requests to twenty per second per port. A purge port earns its latency from an empty queue, and a firm flooding it with repeated identical requests would be building the very queue it is paying to avoid.
Every panel here ships with the SQL that produced it, and every figure in the prose is read from those stored results. To run the same clock-bucket comparison on a symbol and a session of your choosing, ask the question in plain English on the Strasmore terminal.