Futures Price Limits and Price Banding
Futures price limits, price banding and Velocity Logic are three different controls. What each one stops, and how to tell a reject from a locked market.
Futures price limits and price banding are two different controls, and they look identical on a screen when one of them stops your trade. A price band rejects a single order before it reaches the book, and the market keeps trading around you. A daily price limit stops the whole contract from printing beyond a fixed distance from the prior session's settlement, for the rest of that session. A third control, Velocity Logic, pauses one instrument for a short published interval after it covers too much ground in too little time, then reopens it.
What is a futures price limit?
A futures price limit is a price ceiling and floor for a single session, measured from the prior session's settlement price. Three structural facts outrank whatever the current percentages happen to be.
The limit is per product. Equity index futures carry different limits from grain or energy contracts, and the exchange revises the percentages over time, so the live figure belongs to the exchange's current price limit table.
The limit resets each session. A contract pinned at a limit through the overnight hours does not stay pinned: the next session brings a fresh settlement reference and, on many products, a wider or differently tiered band.
The limit bounds price, not trading. Inside the band the book works normally, and orders rest, trade and cancel as usual. What vanishes is any print beyond the limit price. A book with bids stacked at the limit-up price and no offers is what traders call limit locked.
For equity index futures, the downside limits in force during US cash hours are coordinated with the stock market's own halt levels, which is why the two markets stop together on a deep decline. The equity side of that machinery sits in market-wide circuit breakers, and the single-name version is limit up limit down halts in stocks.
What price banding does, and why an order gets rejected
Banding is a rejection, not a halt, and it is the control a retail trader meets most often. The exchange publishes, per product, a band width around a reference price the matching engine maintains from recent trading, and an order priced outside that band never enters the book.
Band width is quoted in the contract's own price increments, which is why band width and tick value read together. Futures tick size and tick value covers those units.
A banded reject has no duration: resubmit inside the band and it works at once. And a band is not a limit, so an order can be banded while the market sits nowhere near its daily limit. Mistyped prices and stale quotes left behind by a fast move are the ordinary sources.
What Velocity Logic does
Velocity Logic measures distance over time. When price covers more than the configured distance inside a short rolling interval, the instrument enters a reserved state for a published, product-specific duration. Resting orders stay in the book, new orders can be entered and cancelled, and the instrument reopens on its own.
This is the control most visible overnight, when the book is thinnest and one large order can walk through several price levels at once. Equity index futures trade nearly around the clock with one short daily break, and ES futures trading hours has the session clock.
How far do index products move between sessions?
A daily limit is measured from a settlement price, and the move that approaches one usually happens while the US cash market is shut. The panel below measures that between-session distance on four equity index ETFs, cash proxies for the indexes the large futures track. Each session's gap is its opening print against the previous session's close, in percent.
| symbol | sample_size | median_gap_pct | p99_gap_pct | largest_gap_pct |
|---|---|---|---|---|
| IWM | 2934 | 0.381 | 3.52 | 9.08 |
| QQQ | 2940 | 0.376 | 3.12 | 9.46 |
| DIA | 2940 | 0.287 | 2.67 | 11.03 |
| SPY | 2945 | 0.291 | 2.6 | 10.45 |
The exact SQL behind every number
WITH
daily AS
(
SELECT
ticker,
date,
any(open) AS session_open,
any(close) AS session_close
FROM global_markets.stocks_daily_aggs
WHERE ticker IN ('SPY', 'QQQ', 'DIA', 'IWM')
AND date >= '2015-01-01'
AND date < '2026-10-01'
GROUP BY ticker, date
),
gaps AS
(
SELECT
ticker,
date,
abs(toFloat64(session_open)
/ any(toFloat64(session_close)) OVER (PARTITION BY ticker ORDER BY date ASC
ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING)
- 1) AS gap
FROM daily
)
SELECT
ticker AS symbol,
count() AS sample_size,
round(100 * quantileDeterministic(0.5)(gap, toUInt64(toYYYYMMDD(date))), 3) AS median_gap_pct,
round(100 * quantileDeterministic(0.99)(gap, toUInt64(toYYYYMMDD(date))), 2) AS p99_gap_pct,
round(100 * max(gap), 2) AS largest_gap_pct
FROM gaps
WHERE isFinite(gap)
AND gap > 0
GROUP BY ticker
ORDER BY p99_gap_pct DESCOf the 4 names, IWM carried the widest 99th-percentile gap at 3.52%, against a median session gap of 0.381% across 2934 sessions. Its single largest gap in the window measured 9.08%. That is the shape a limit band lives with: a typical night moves the reference a fraction of a percent, and the tail is an order of magnitude bigger.
The next panel counts, for the S&P 500 ETF, the share of sessions that opened at least one percent and at least two percent away from the prior close.
| year | sample_size | pct_sessions_over_1pct | pct_sessions_over_2pct | largest_gap_pct |
|---|---|---|---|---|
| 2015 | 251 | 7.6 | 2 | 5.23 |
| 2016 | 252 | 6 | 0.8 | 3.41 |
| 2017 | 251 | 0.4 | 0 | 1.1 |
| 2018 | 251 | 6.4 | 0 | 1.69 |
| 2019 | 252 | 4.8 | 0 | 1.97 |
| 2020 | 253 | 25.7 | 10.7 | 10.45 |
| 2021 | 252 | 5.6 | 0 | 1.51 |
| 2022 | 251 | 22.3 | 3.6 | 3.72 |
| 2023 | 250 | 4 | 0 | 1.49 |
| 2024 | 252 | 6 | 0.8 | 3.99 |
| 2025 | 250 | 12.4 | 3.2 | 3.47 |
| 2026 | 187 | 7.5 | 0.5 | 2.6 |
The exact SQL behind every number
WITH
daily AS
(
SELECT
date,
any(open) AS session_open,
any(close) AS session_close
FROM global_markets.stocks_daily_aggs
WHERE ticker = 'SPY'
AND date >= '2015-01-01'
AND date < '2026-10-01'
GROUP BY date
),
gaps AS
(
SELECT
date,
abs(toFloat64(session_open)
/ any(toFloat64(session_close)) OVER (ORDER BY date ASC
ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING)
- 1) AS gap
FROM daily
)
SELECT
toYear(date) AS year,
count() AS sample_size,
round(100 * countIf(gap >= 0.01) / count(), 1) AS pct_sessions_over_1pct,
round(100 * countIf(gap >= 0.02) / count(), 1) AS pct_sessions_over_2pct,
round(100 * max(gap), 2) AS largest_gap_pct
FROM gaps
WHERE isFinite(gap)
GROUP BY year
ORDER BY year ASCIn 2026 so far, 7.5% of the 187 sessions opened at least one percent from the prior close and 0.5% opened at least two percent away, with the year's largest gap at 2.6%. The clustering is the point: limit-scale moves arrive in bunches rather than evenly.
How fast is too fast inside one minute?
A velocity control cares about distance divided by time, so the useful reference is how far price normally travels inside a very short window. The panel below takes every regular-session minute in the S&P 500 ETF since the start of 2024 and buckets the open-to-close move by half hour of the trading day.
| et_time | median_minute_move_pct | p99_minute_move_pct |
|---|---|---|
| 09:30 | 0.025 | 0.171 |
| 10:00 | 0.022 | 0.158 |
| 10:30 | 0.019 | 0.138 |
| 11:00 | 0.016 | 0.123 |
| 11:30 | 0.015 | 0.113 |
| 12:00 | 0.014 | 0.116 |
| 12:30 | 0.014 | 0.114 |
| 13:00 | 0.014 | 0.102 |
| 13:30 | 0.013 | 0.121 |
| 14:00 | 0.013 | 0.114 |
| 14:30 | 0.013 | 0.113 |
| 15:00 | 0.013 | 0.114 |
| 15:30 | 0.017 | 0.137 |
The exact SQL behind every number
SELECT
formatDateTime(toStartOfInterval(toTimeZone(window_start, 'America/New_York'), INTERVAL 30 MINUTE), '%H:%i') AS et_time,
round(100 * quantileDeterministic(0.5)(abs(toFloat64(close) / toFloat64(open) - 1),
toUInt64(toUnixTimestamp(window_start))), 3) AS median_minute_move_pct,
round(100 * quantileDeterministic(0.99)(abs(toFloat64(close) / toFloat64(open) - 1),
toUInt64(toUnixTimestamp(window_start))), 3) AS p99_minute_move_pct
FROM global_markets.delayed_stocks_minute_aggs
WHERE ticker = 'SPY'
AND window_start >= '2024-01-01 00:00:00'
AND window_start < '2026-10-01 00:00:00'
AND volume > 0
AND open > 0
AND (toHour(toTimeZone(window_start, 'America/New_York')) * 60
+ toMinute(toTimeZone(window_start, 'America/New_York'))) >= 570
AND (toHour(toTimeZone(window_start, 'America/New_York')) * 60
+ toMinute(toTimeZone(window_start, 'America/New_York'))) < 960
GROUP BY et_time
ORDER BY et_time ASCIn the half hour starting 09:30 ET, the 99th-percentile minute moved 0.171% while the median minute moved 0.025%. In the 15:30 ET bucket the same two readings measured 0.137% and 0.017%. A speed check is calibrated against exactly this gap between the body and the tail of the distribution.
A pinned example: the morning of August 5, 2024
Index futures reprice through the night, and the cash market meets that repricing as an opening gap. The trace below pins one real morning, August 5, 2024, in ten-minute buckets measured against the prior session's close.
| et_time | pct_vs_prior_close | bucket_range_pct |
|---|---|---|
| 09:30 | -3.22 | 1.07 |
| 09:40 | -3 | 0.76 |
| 09:50 | -3.32 | 0.53 |
| 10:00 | -2.9 | 1.07 |
| 10:10 | -2.75 | 0.5 |
| 10:20 | -3.43 | 1.55 |
| 10:30 | -2.96 | 0.67 |
| 10:40 | -3.08 | 0.37 |
| 10:50 | -3.09 | 0.63 |
| 11:00 | -2.54 | 0.86 |
| 11:10 | -2.49 | 0.36 |
| 11:20 | -2.48 | 0.31 |
| 11:30 | -2.19 | 0.48 |
| 11:40 | -2.44 | 0.31 |
| 11:50 | -2.34 | 0.3 |
| 12:00 | -2.17 | 0.32 |
| 12:10 | -1.99 | 0.29 |
| 12:20 | -1.75 | 0.35 |
| 12:30 | -2.23 | 0.57 |
| 12:40 | -2.41 | 0.35 |
The exact SQL behind every number
WITH
reference AS
(
SELECT any(toFloat64(close)) AS prior_close
FROM global_markets.stocks_daily_aggs
WHERE ticker = 'SPY'
AND date = '2024-08-02'
)
SELECT
formatDateTime(toStartOfInterval(toTimeZone(m.window_start, 'America/New_York'), INTERVAL 10 MINUTE), '%H:%i') AS et_time,
round(100 * (toFloat64(argMax(m.close, m.window_start)) / any(r.prior_close) - 1), 2) AS pct_vs_prior_close,
round(100 * (toFloat64(max(m.high)) - toFloat64(min(m.low))) / any(r.prior_close), 2) AS bucket_range_pct
FROM global_markets.delayed_stocks_minute_aggs AS m
CROSS JOIN reference AS r
WHERE m.ticker = 'SPY'
AND m.window_start >= '2024-08-05 13:30:00'
AND m.window_start < '2024-08-05 20:00:00'
AND m.volume > 0
GROUP BY et_time
ORDER BY et_time ASCThe first bucket of the session, 09:30 ET, finished at -3.22% versus the prior close, with a high-to-low range inside those ten minutes of 1.07% of the same reference. By the 15:50 bucket the reading was -2.91%, across 39 buckets in all. Most of that distance was covered before the cash open, when overnight bands and velocity pauses are the only controls on the futures market.
A reject and a locked market need opposite responses
Both situations show the same thing on a trading screen: an order that has not filled, and the correct next move is opposite in each case.
A banded reject means the order never existed. There is nothing in the queue, nothing to cancel, and no state to wait out. Reprice inside the band and submit again.
A limit-locked market means the order is live and queued. Repricing it further through the limit earns another reject, while cancelling it gives up queue position in a market where fills are scarce. A banded reject leaves a two-sided book that keeps printing normally and carries an order-level reject reason; a locked or paused instrument carries a market state and prints stop at one price.
Neither situation suspends the obligations attached to the position. Mark-to-market and margin calls continue while a contract sits limit locked, which is covered in how futures margin works.
FAQ
Do futures have circuit breakers like stocks?
They have close cousins. Futures use daily price limits plus velocity pauses rather than the single-stock limit up limit down bands used in equities.
What does limit down mean in a futures contract?
It means the contract has reached the lowest price it is allowed to print in that session, measured from the prior settlement. Trading continues at or above the limit price, and no trade prints below it until the limit changes.
Why was my futures order rejected instead of filled?
The ordinary source is price banding: the order was priced outside the published band around the contract's reference price, so the matching engine refused it on arrival. The market itself was never halted.
How long does a Velocity Logic pause last?
The duration is set per product and published by the exchange, and it is short, which is why the state is described as a momentary pause rather than a halt.
Data notes and limitations
- The panels use cash equity ETFs, not futures prices. They stand in for the index a futures contract tracks.
- Opening prints here are raw and unadjusted, so each ex-dividend morning adds a small mechanical gap to the first two panels.
- The one-minute panel covers the regular session, 09:30 to 16:00 ET, where ETF prints are dense; extended-hours minutes are thin and would distort a percentile.
- Exchange band widths, limit percentages and pause durations are published by the exchange per product and revised over time. None are quoted here on purpose.
Every panel above opens to the exact SQL that produced it. To measure between-session gaps or one-minute moves on a name you follow, ask the question in plain English on the Strasmore terminal.