Locked and Crossed Markets, Explained With Data
A locked market quotes the bid equal to the ask; a crossed market quotes the bid above it. See why Reg NMS bans both and how often each hits the tape.
A locked market is a moment when a stock's best bid equals its best ask — a bid-ask spread of exactly zero. A crossed market goes further: the best bid stands above the best ask. US market rules ban exchanges from displaying either, yet both appear routinely in the consolidated quote record. This page defines both, explains the rule, and measures how often each shows up.
What is a locked market?
Every US stock carries two prices at once: the bid, the highest price any buyer will pay, and the ask, the lowest price any seller will accept. The ask normally sits above the bid; the gap between them is the bid-ask spread, the built-in cost of trading immediately.
A locked market is the moment that gap closes to zero: a stock quoted $10.00 bid / $10.05 ask carries a five-cent spread, and if the best ask drops to $10.00 while the bid holds, the quote is locked at $10.00 / $10.00.
On a single exchange that state cannot stand — a buy at $10.00 meeting a sell at $10.00 executes on the spot. A locked quote survives only between venues. US stocks trade on over a dozen exchanges at once, and the NBBO — the national best bid and offer, the quote your brokerage app shows — stitches the best bid and ask across all of them. A $10.00 bid on one venue and a $10.00 ask on another lock the national quote while neither venue shows a locked book.
What is a crossed market?
A crossed market inverts the ordinary arrangement: the best bid stands above the best ask — say a $10.02 bid against a $10.00 ask. On paper anyone could buy at $10.00, sell at $10.02, and pocket two cents a share risk-free. That free money is exactly why a crossed quote cannot last: professional firms — among them the market makers quoting both sides of most stocks — take the arbitrage the instant it is real, and taking it consumes both quotes. What remains in the data is the flicker: a crossed record caught between one venue's update and another's.
Why Reg NMS bans locked and crossed quotes
The ban lives in Regulation NMS, the SEC rulebook (adopted 2005) for trading across US stock exchanges. Rule 610(d) requires each exchange to prevent its members from displaying quotes that lock or cross protected quotes — the automated best bids and offers on other exchanges — during regular trading hours. The intent: a bid equal to someone else's ask can trade right now, and the rulebook wants it routed and executed, not displayed as a standing contradiction.
One boundary in the rule matters for the data below: it regulates displaying, not existing. Each venue controls what its own book shows; the consolidated record is assembled across venues in real time, from feeds racing one another. A locked NBBO can appear there without any single participant breaking the rule.
Why locked and crossed quotes still hit the tape
Speed. Exchanges update quotes in microseconds, and the consolidated feed rebuilds on every update. Two ordinary events put locked and crossed records into it:
- Races. One exchange raises its bid to $10.00 while another still displays a $10.00 ask. Stitched together, the NBBO reads locked until the slower side updates; the same race overshooting by a cent reads crossed.
- Stale and one-sided records. Some records carry only one side — a bid with no ask, or the reverse. Thin names spend far more of the day there, as the census below shows.
The counting rules on this page: locked = bid equals ask, both sides positive; crossed = bid above a positive ask; one-sided = either side zero or missing. Nothing is silently dropped — one-sided records get their own column.
How often do locked and crossed markets happen?
Start with the whole tape: every NBBO update, for every listed symbol, on 2026-07-02 — the most recent completed session in our census window.
The exact SQL behind every number
WITH (
SELECT max(toDate(toTimeZone(window_start, 'America/New_York')))
FROM global_markets.delayed_stocks_minute_aggs
WHERE ticker = 'SPY' AND window_start < toDateTime(today() - 3)
) AS census_day
SELECT
formatDateTime(census_day, '%Y-%m-%d') AS session_date,
round(count() / 1e6, 0) AS updates_m,
round(countIf(bid_price = ask_price AND bid_price > 0) / 1e6, 2) AS locked_m,
round(countIf(bid_price > ask_price AND ask_price > 0) / 1e3, 1) AS crossed_k,
round(countIf(bid_price = ask_price AND bid_price > 0) / toFloat64(greatest(countIf(bid_price > ask_price AND ask_price > 0), 1)), 0) AS locked_per_crossed,
round(countIf(bid_price >= ask_price AND ask_price > 0 AND bid_price > 0) / toFloat64(count()) * 1e4, 1) AS locked_or_crossed_per_10k,
round(countIf(bid_price <= 0 OR ask_price <= 0) / toFloat64(count()) * 1e4, 1) AS one_sided_per_10k
FROM global_markets.cache_stocks_quotes
WHERE sip_timestamp >= toDateTime(census_day)
AND sip_timestamp < toDateTime(census_day + 1)That session carried 597 million quote updates: 10.96 million locked and 68.2 thousand crossed — together 184.7 of every 10,000 updates, locked outnumbering crossed 161-to-one. Records missing a side ran 3.8 per 10,000. The July 7 market recap discloses the same counts beside every spread it measures.
The market-wide rate blends thousands of very different names. The table below reruns the census on a checked set of ten — eight household tickers plus two genuinely thin small caps, Nathan's Famous (NATH) and Seneca Foods (SENEA) — over recent completed sessions, extended hours included, with each name's typical quoted spread alongside.
The exact SQL behind every number
SELECT ticker,
round(count() / 1e3, 1) AS updates_k,
round(quantileExactIf(0.5)(toFloat64(ask_price - bid_price), bid_price > 0 AND ask_price >= bid_price) * 100, 1) AS median_spread_cents,
countIf(bid_price = ask_price AND bid_price > 0) AS locked_records,
countIf(bid_price > ask_price AND ask_price > 0) AS crossed_records,
round(countIf(bid_price >= ask_price AND ask_price > 0 AND bid_price > 0) / toFloat64(count()) * 1e4, 1) AS locked_or_crossed_per_10k,
round(countIf(bid_price <= 0 OR ask_price <= 0) / toFloat64(count()) * 1e4, 1) AS one_sided_per_10k
FROM global_markets.cache_stocks_quotes
WHERE ticker IN ('SPY', 'QQQ', 'AAPL', 'NVDA', 'MSFT', 'TSLA', 'KO', 'AMD', 'NATH', 'SENEA')
AND sip_timestamp >= toDateTime(today() - 10)
AND sip_timestamp < toDateTime(today() - 3)
GROUP BY ticker
ORDER BY tickerRead the spread column against the rate. Coca-Cola, typically quoted 1¢ wide, showed a locked or crossed quote on 244.9 of every 10,000 updates; Nvidia (2¢) ran 115.3 per 10,000 and SPY (3¢) 49.5. AMD, whose typical extended-day quote sat 41¢ wide, came in at just 6.4 per 10,000. A lock takes two venues arriving at the same penny at the same instant — the wider the typical quote, the longer that trip.
The thin names barely register: Nathan's Famous printed 0 locked and 0 crossed records across 4.6 thousand updates, Seneca Foods 0 and 1 across 20.7 thousand. Their column is the other one: one-sided records ran 92.8 per 10,000 in Nathan's Famous and 52.7 in Seneca Foods, against 0 in SPY. Locked and crossed records live where quoting is fast and tight; one-sided records live where quoting is scarce.
When do locked and crossed quotes cluster?
The name census says where; the clock says when. Here is the checked set's locked-or-crossed rate by 30-minute Eastern Time bucket, 4:00 a.m. to 8:00 p.m., with each bucket's update traffic alongside.
The exact SQL behind every number
SELECT formatDateTime(toStartOfInterval(toTimeZone(sip_timestamp, 'America/New_York'), INTERVAL 30 MINUTE), '%H:%i') AS et_time,
round(countIf(bid_price >= ask_price AND ask_price > 0 AND bid_price > 0) / toFloat64(count()) * 1e4, 1) AS locked_or_crossed_per_10k,
round(count() / 1e6, 2) AS updates_m
FROM global_markets.cache_stocks_quotes
WHERE ticker IN ('SPY', 'QQQ', 'AAPL', 'NVDA', 'MSFT', 'TSLA', 'KO', 'AMD', 'NATH', 'SENEA')
AND sip_timestamp >= toDateTime(today() - 10)
AND sip_timestamp < toDateTime(today() - 3)
GROUP BY et_time
HAVING et_time >= '04:00' AND et_time < '20:00'
ORDER BY et_timeThe rate climbs across the session. The 05:00 premarket bucket measured 2.1 per 10,000 on 0.49 million updates; the 09:30 bucket — regular trading's first half hour, opened by the opening auction — ran 31.9 per 10,000 on 8.7 million updates. From there the rate builds into the close, reaching 131.8 per 10,000 in the 15:30 bucket, the run-up to the closing auction. One receipt row pins the shape:
The exact SQL behind every number
SELECT
round(countIf(is_locked) / 1e3, 1) AS locked_k,
round(countIf(is_crossed) / 1e3, 1) AS crossed_k,
round(countIf(is_locked) / toFloat64(greatest(countIf(is_crossed), 1)), 1) AS locked_per_crossed,
round(countIf((is_locked OR is_crossed) AND et_minute BETWEEN 240 AND 569) / toFloat64(countIf(et_minute BETWEEN 240 AND 569)) * 1e4, 1) AS premarket_per_10k,
round(countIf((is_locked OR is_crossed) AND et_minute BETWEEN 570 AND 599) / toFloat64(countIf(et_minute BETWEEN 570 AND 599)) * 1e4, 1) AS opening_half_hour_per_10k,
round(countIf((is_locked OR is_crossed) AND et_minute BETWEEN 720 AND 779) / toFloat64(countIf(et_minute BETWEEN 720 AND 779)) * 1e4, 1) AS midday_hour_per_10k,
round(countIf((is_locked OR is_crossed) AND et_minute BETWEEN 930 AND 959) / toFloat64(countIf(et_minute BETWEEN 930 AND 959)) * 1e4, 1) AS closing_half_hour_per_10k,
round((countIf((is_locked OR is_crossed) AND et_minute BETWEEN 930 AND 959) / toFloat64(countIf(et_minute BETWEEN 930 AND 959)))
/ (countIf((is_locked OR is_crossed) AND et_minute BETWEEN 570 AND 599) / toFloat64(countIf(et_minute BETWEEN 570 AND 599))), 1) AS close_to_open_ratio,
round((countIf((is_locked OR is_crossed) AND et_minute BETWEEN 930 AND 959) / toFloat64(countIf(et_minute BETWEEN 930 AND 959)))
/ (countIf((is_locked OR is_crossed) AND et_minute BETWEEN 720 AND 779) / toFloat64(countIf(et_minute BETWEEN 720 AND 779))), 1) AS close_to_midday_ratio,
round((countIf((is_locked OR is_crossed) AND et_minute BETWEEN 930 AND 959) / toFloat64(countIf(et_minute BETWEEN 930 AND 959)))
/ (countIf((is_locked OR is_crossed) AND et_minute BETWEEN 240 AND 569) / toFloat64(countIf(et_minute BETWEEN 240 AND 569))), 1) AS close_to_premarket_ratio
FROM (
SELECT
bid_price = ask_price AND bid_price > 0 AS is_locked,
bid_price > ask_price AND ask_price > 0 AS is_crossed,
toHour(toTimeZone(sip_timestamp, 'America/New_York')) * 60 + toMinute(toTimeZone(sip_timestamp, 'America/New_York')) AS et_minute
FROM global_markets.cache_stocks_quotes
WHERE ticker IN ('SPY', 'QQQ', 'AAPL', 'NVDA', 'MSFT', 'TSLA', 'KO', 'AMD', 'NATH', 'SENEA')
AND sip_timestamp >= toDateTime(today() - 10)
AND sip_timestamp < toDateTime(today() - 3)
)Across the whole window the checked set printed 262.8 thousand locked records against 22.6 thousand crossed — 11.6 locked for every crossed. The closing half hour's rate, 131.8 per 10,000, ran 4.1 times the opening half hour's 31.9 and 2.9 times the noon hour's 45.9.
The display ban does not even apply before 9:30 a.m., yet the whole 4:00–9:30 stretch — premarket trading, quoting on a fraction of regular-hours traffic — showed just 10.4 per 10,000, and the closing half hour ran 12.7 times that rate. These records collect where quoting runs fast and tight, not where the rulebook is loosest.
Data notes: window and scope
- The whole-tape census covers one full UTC calendar day, every listed symbol included; the checked-set panels cover a rolling window of recent completed sessions, extended hours (4:00 a.m.–8:00 p.m. ET) included. Every window ends three days behind the refresh date — the quote warehouse carries a one-to-two-day ingest lag at its front edge.
- The ten-name table is a checked set, not a market-wide ranking; clock buckets after 4:00 p.m. run on a small fraction of regular-hours traffic (the update-count column is charted for that reason).
- These are counts of records, not durations. Per-update forensics — nanosecond gaps, plus crossed and zero-bid receipts on a single session's SPY tape — live in the June 29 microstructure deep-dive.
FAQ
What is a locked market in stocks?
A locked market is a quote where the best bid equals the best ask — a spread of zero. It arises between exchanges — one venue's bid matching another's ask — and Regulation NMS bars exchanges from displaying quotes that create it during regular hours.
What does it mean when the bid is higher than the ask?
A bid above the ask is a crossed market. A single exchange's book cannot stand that way — overlapping orders execute immediately — and a crossed NBBO is almost always two venues' quotes caught mid-update.
Are locked and crossed markets illegal?
Not for a regular investor. Rule 610(d) of Regulation NMS obliges exchanges to prevent their members from displaying locking or crossing quotes during regular hours. The transient records in the consolidated feed are artifacts of venues updating at slightly different instants.
How often do locked and crossed markets happen?
Routinely: 10.96 million locked and 68.2 thousand crossed records hit the consolidated tape on 2026-07-02 alone — 184.7 of every 10,000 updates. The rate runs far higher in tightly quoted names than in thin ones, and higher into the close than at the open.
Every number on this page renders from a stored query — expand the SQL under any panel to see exactly how a lock or a cross is counted. To run the same census on the names you trade, ask for it in plain English on the Strasmore terminal.