When Do VIX Futures Expire? 2026 Dates
VIX futures expire on the Wednesday 30 days before the next month's third-Friday SPX expiry. Every 2026 settlement date, plus the walk-back arithmetic.
VIX futures expire on the Wednesday that falls exactly 30 calendar days before the third Friday of the following month. One rule generates the whole calendar: take next month's third Friday, count back 30 days, and the Wednesday you land on is the final settlement date for this month's contract. If either of those two days is an exchange holiday, the date steps back one business day to the Tuesday, which is how a VIX expiration ends up sitting a day off the pattern a reader expects.
When do VIX futures expire?
The anchor is not the equity monthly expiration in the contract's own month. It is the AM-settled S&P 500 index option expiration in the month after it. The 30-day offset is deliberate: the VIX measures the implied volatility priced into SPX options with 30 days left to run, and final settlement is scheduled for the morning when the option series feeding that calculation have exactly 30 days of life remaining. What the VIX actually measures covers the construction.
Two things follow from the rule. A VIX futures expiration is a Wednesday, not a Friday. And its distance from the equity monthly expiration in the same month shifts from month to month, since the two dates are pinned to third Fridays of different months.
VIX futures settlement dates for 2026
The panel below derives every 2026 date from the rule itself. No settlement date is typed in by hand: the query finds each third Friday and counts back 30 days, stepping over exchange holidays on the way.
| settlement_date | settlement_label | vix_contract | spx_anchor_friday | days_from_equity_expiry |
|---|---|---|---|---|
| 2026-01-21 | Wed Jan 21 | VX Jan 2026 | Feb 20 | 5 |
| 2026-02-18 | Wed Feb 18 | VX Feb 2026 | Mar 20 | -2 |
| 2026-03-18 | Wed Mar 18 | VX Mar 2026 | Apr 17 | -2 |
| 2026-04-15 | Wed Apr 15 | VX Apr 2026 | May 15 | -2 |
| 2026-05-19 | Tue May 19 | VX May 2026 | Jun 18 | 4 |
| 2026-06-17 | Wed Jun 17 | VX Jun 2026 | Jul 17 | -2 |
| 2026-07-22 | Wed Jul 22 | VX Jul 2026 | Aug 21 | 5 |
| 2026-08-19 | Wed Aug 19 | VX Aug 2026 | Sep 18 | -2 |
| 2026-09-16 | Wed Sep 16 | VX Sep 2026 | Oct 16 | -2 |
| 2026-10-21 | Wed Oct 21 | VX Oct 2026 | Nov 20 | 5 |
| 2026-11-18 | Wed Nov 18 | VX Nov 2026 | Dec 18 | -2 |
| 2026-12-16 | Wed Dec 16 | VX Dec 2026 | Jan 15 | -2 |
The exact SQL behind every number
WITH
traded AS
(
SELECT groupArray(session_day) AS session_days
FROM
(
SELECT date AS session_day
FROM global_markets.stocks_daily_aggs
WHERE ticker = 'SPY'
AND date >= toDate('2014-11-01')
GROUP BY session_day
)
),
closures_ahead AS
(
SELECT groupArray(date) AS closed_days
FROM global_markets.stocks_market_holidays
WHERE status = 'closed'
)
SELECT
toString(settlement) AS settlement_date,
formatDateTime(settlement, '%a %b %e') AS settlement_label,
concat('VX ', formatDateTime(contract_month, '%b %Y')) AS vix_contract,
formatDateTime(spx_anchor, '%b %e') AS spx_anchor_friday,
toInt32(dateDiff('day', equity_monthly, settlement)) AS days_from_equity_expiry
FROM
(
SELECT
a.contract_month AS contract_month,
a.equity_monthly AS equity_monthly,
if(((a.ref_friday <= toDate(arrayMax(t.session_days))) AND (NOT has(t.session_days, a.ref_friday)))
OR has(h.closed_days, a.ref_friday),
addDays(a.ref_friday, -1),
a.ref_friday) AS spx_anchor,
addDays(spx_anchor, -30) AS wednesday_target,
if(((wednesday_target <= toDate(arrayMax(t.session_days))) AND (NOT has(t.session_days, wednesday_target)))
OR has(h.closed_days, wednesday_target),
addDays(wednesday_target, -1),
wednesday_target) AS settlement
FROM
(
SELECT
contract_month,
addMonths(contract_month, 1) AS ref_month,
addDays(ref_month, ((5 - toInt32(toDayOfWeek(ref_month)) + 7) % 7) + 14) AS ref_friday,
addDays(contract_month, ((5 - toInt32(toDayOfWeek(contract_month)) + 7) % 7) + 14) AS equity_monthly
FROM
(
SELECT addMonths(toDate('2026-01-01'), toInt32(arrayJoin(range(12)))) AS contract_month
)
) AS a
CROSS JOIN traded AS t
CROSS JOIN closures_ahead AS h
)
ORDER BY settlement ASCAll 12 monthly contracts are there. VX Jan 2026 settles Wed Jan 21, which is 5 days later in the month than the equity expiration that shares its month. The next contract in the cycle flips the sign: its gap column reads -2, with the VIX date arriving first. The chart draws that gap. It sawtooths all year between a VIX date ahead of the equity monthly and one behind it, and it is never zero. The final 2026 cycle settles Wed Dec 16, anchored on a third Friday that sits in the next calendar year.
How to work out any month yourself
Four worked examples, with each step of the walk-back in its own column.
| vix_contract | next_month_third_friday | friday_status | counted_back_from | thirty_days_earlier | that_day_status | final_settlement_date |
|---|---|---|---|---|---|---|
| VX Jan 2026 | Fri Feb 20, 2026 | trading day | Fri Feb 20, 2026 | Wed Jan 21, 2026 | trading day | Wed Jan 21, 2026 |
| VX Dec 2026 | Fri Jan 15, 2027 | trading day | Fri Jan 15, 2027 | Wed Dec 16, 2026 | trading day | Wed Dec 16, 2026 |
| VX May 2026 | Fri Jun 19, 2026 | exchange holiday | Thu Jun 18, 2026 | Tue May 19, 2026 | trading day | Tue May 19, 2026 |
| VX Jun 2024 | Fri Jul 19, 2024 | trading day | Fri Jul 19, 2024 | Wed Jun 19, 2024 | exchange holiday | Tue Jun 18, 2024 |
The exact SQL behind every number
WITH
traded AS
(
SELECT groupArray(session_day) AS session_days
FROM
(
SELECT date AS session_day
FROM global_markets.stocks_daily_aggs
WHERE ticker = 'SPY'
AND date >= toDate('2014-11-01')
GROUP BY session_day
)
),
closures_ahead AS
(
SELECT groupArray(date) AS closed_days
FROM global_markets.stocks_market_holidays
WHERE status = 'closed'
)
SELECT
concat('VX ', formatDateTime(contract_month, '%b %Y')) AS vix_contract,
formatDateTime(ref_friday, '%a %b %e, %Y') AS next_month_third_friday,
if(friday_closed, 'exchange holiday', 'trading day') AS friday_status,
formatDateTime(spx_anchor, '%a %b %e, %Y') AS counted_back_from,
formatDateTime(wednesday_target, '%a %b %e, %Y') AS thirty_days_earlier,
if(target_closed, 'exchange holiday', 'trading day') AS that_day_status,
formatDateTime(settlement, '%a %b %e, %Y') AS final_settlement_date
FROM
(
SELECT
a.contract_month AS contract_month,
a.ref_friday AS ref_friday,
((a.ref_friday <= toDate(arrayMax(t.session_days))) AND (NOT has(t.session_days, a.ref_friday)))
OR has(h.closed_days, a.ref_friday) AS friday_closed,
if(friday_closed, addDays(a.ref_friday, -1), a.ref_friday) AS spx_anchor,
addDays(spx_anchor, -30) AS wednesday_target,
((wednesday_target <= toDate(arrayMax(t.session_days))) AND (NOT has(t.session_days, wednesday_target)))
OR has(h.closed_days, wednesday_target) AS target_closed,
if(target_closed, addDays(wednesday_target, -1), wednesday_target) AS settlement
FROM
(
SELECT
contract_month,
addMonths(contract_month, 1) AS ref_month,
addDays(ref_month, ((5 - toInt32(toDayOfWeek(ref_month)) + 7) % 7) + 14) AS ref_friday
FROM
(
SELECT toDate(arrayJoin(['2026-01-01', '2026-12-01', '2026-05-01', '2024-06-01'])) AS contract_month
)
) AS a
CROSS JOIN traded AS t
CROSS JOIN closures_ahead AS h
)
ORDER BY multiIf(contract_month = toDate('2026-01-01'), 1,
contract_month = toDate('2026-12-01'), 2,
contract_month = toDate('2026-05-01'), 3,
4) ASCRead the first row across. VX Jan 2026 is the plain case: next month's third Friday is Fri Feb 20, 2026, status trading day, 30 days earlier is Wed Jan 21, 2026, also trading day, and settlement is Wed Jan 21, 2026.
The second row crosses the year boundary. Its anchor Friday, Fri Jan 15, 2027, sits in the next calendar year while settlement, Wed Dec 16, 2026, stays in this one. Every December contract works that way.
The third row is where the holiday rule bites. The anchor Friday is Fri Jun 19, 2026 and its status reads exchange holiday: June 19 is Juneteenth, a full market closure since 2022. The count back starts from the business day before it, Thu Jun 18, 2026, which puts final settlement on Tue May 19, 2026, a Tuesday.
The fourth row shows the other branch of the same rule. Its anchor Friday was open, and the Wednesday 30 days earlier, Wed Jun 19, 2024, reads exchange holiday, so settlement moved one business day back to Tue Jun 18, 2024. Same holiday as the row above it, two years apart, hitting the other end of the arithmetic.
Why some VIX expirations land on a Tuesday
Two kinds of collision move the date, and both resolve the same way: step back one business day. The anchor Friday can be shut, which happens when Good Friday or Juneteenth lands on a third Friday. Or the counted-back Wednesday can be shut, which June 19 manages in years when it falls mid-week. The panel scans every monthly contract from 2015 forward and keeps the ones that did not settle on a Wednesday.
| settlement_date | settlement_weekday | vix_contract | closed_day_in_the_way |
|---|---|---|---|
| 2019-03-19 | Tuesday | VX Mar 2019 | Fri Apr 19, 2019 |
| 2022-03-15 | Tuesday | VX Mar 2022 | Fri Apr 15, 2022 |
| 2024-06-18 | Tuesday | VX Jun 2024 | Wed Jun 19, 2024 |
| 2025-03-18 | Tuesday | VX Mar 2025 | Fri Apr 18, 2025 |
| 2026-05-19 | Tuesday | VX May 2026 | Fri Jun 19, 2026 |
The exact SQL behind every number
WITH
traded AS
(
SELECT groupArray(session_day) AS session_days
FROM
(
SELECT date AS session_day
FROM global_markets.stocks_daily_aggs
WHERE ticker = 'SPY'
AND date >= toDate('2014-11-01')
GROUP BY session_day
)
),
closures_ahead AS
(
SELECT groupArray(date) AS closed_days
FROM global_markets.stocks_market_holidays
WHERE status = 'closed'
)
SELECT
toString(settlement) AS settlement_date,
formatDateTime(settlement, '%W') AS settlement_weekday,
concat('VX ', formatDateTime(contract_month, '%b %Y')) AS vix_contract,
formatDateTime(if(friday_closed, ref_friday, wednesday_target), '%a %b %e, %Y') AS closed_day_in_the_way
FROM
(
SELECT
a.contract_month AS contract_month,
a.ref_friday AS ref_friday,
((a.ref_friday <= toDate(arrayMax(t.session_days))) AND (NOT has(t.session_days, a.ref_friday)))
OR has(h.closed_days, a.ref_friday) AS friday_closed,
if(friday_closed, addDays(a.ref_friday, -1), a.ref_friday) AS spx_anchor,
addDays(spx_anchor, -30) AS wednesday_target,
((wednesday_target <= toDate(arrayMax(t.session_days))) AND (NOT has(t.session_days, wednesday_target)))
OR has(h.closed_days, wednesday_target) AS target_closed,
if(target_closed, addDays(wednesday_target, -1), wednesday_target) AS settlement
FROM
(
SELECT
contract_month,
addMonths(contract_month, 1) AS ref_month,
addDays(ref_month, ((5 - toInt32(toDayOfWeek(ref_month)) + 7) % 7) + 14) AS ref_friday
FROM
(
SELECT addMonths(toDate('2015-01-01'), toInt32(arrayJoin(range(144)))) AS contract_month
)
) AS a
CROSS JOIN traded AS t
CROSS JOIN closures_ahead AS h
)
WHERE toDayOfWeek(settlement) <> 3
ORDER BY settlement ASC5 of the 144 contract months in that window settled on some other weekday. The most recent is VX May 2026, which settled on a Tuesday, with Fri Jun 19, 2026 the date the exchange was closed. Twelve calendar years produced only a handful, which is why the arithmetic is worth doing per month instead of reaching for "the third Wednesday".
What actually settles that morning
VIX futures are cash settled against the Special Opening Quotation, or SOQ: one VIX value calculated from the opening prices of the SPX option series in the index calculation, printed in an opening auction on the settlement morning rather than taken from the index's continuous intraday prints. Nothing changes hands beyond cash. The difference between the trade price and the SOQ, multiplied by the contract multiplier, is the cash flow. How the VIX SOQ auction works covers how that print is assembled, and cash settled versus physical delivery covers the general mechanic.
The settlement date is an ordinary session for everything else. The equity market opens and closes on its normal schedule, and every VIX contract other than the expiring one trades its usual hours. The expiring contract is the exception: it stops trading that morning, ahead of the auction that fixes its final number, and the next contract in the cycle becomes the front month once that number prints. A position left open into the auction settles at the SOQ rather than at a price taken off the screen.
Do VIX futures and VIX options expire on the same day?
Yes. A VIX option in the same monthly cycle carries the same expiration date as the future and settles against the same SOQ print, so the two share one expiry morning. The options side adds its own detail about when the expiring series stops trading, which when VIX options expire sets out, and the morning settlement convention is the one described in AM versus PM settled options.
What the roll calendar looks like
Monthly does not mean evenly spaced. Each settlement is pinned to a third Friday in a different month, and consecutive third Fridays sit either 28 or 35 days apart. A holiday shift adds or removes a day on top of that.
| settlement_date | vix_contract | days_since_prior_settlement |
|---|---|---|
| 2026-01-21 | VX Jan 2026 | 35 |
| 2026-02-18 | VX Feb 2026 | 28 |
| 2026-03-18 | VX Mar 2026 | 28 |
| 2026-04-15 | VX Apr 2026 | 28 |
| 2026-05-19 | VX May 2026 | 34 |
| 2026-06-17 | VX Jun 2026 | 29 |
| 2026-07-22 | VX Jul 2026 | 35 |
| 2026-08-19 | VX Aug 2026 | 28 |
| 2026-09-16 | VX Sep 2026 | 28 |
| 2026-10-21 | VX Oct 2026 | 35 |
| 2026-11-18 | VX Nov 2026 | 28 |
| 2026-12-16 | VX Dec 2026 | 28 |
The exact SQL behind every number
WITH
traded AS
(
SELECT groupArray(session_day) AS session_days
FROM
(
SELECT date AS session_day
FROM global_markets.stocks_daily_aggs
WHERE ticker = 'SPY'
AND date >= toDate('2014-11-01')
GROUP BY session_day
)
),
closures_ahead AS
(
SELECT groupArray(date) AS closed_days
FROM global_markets.stocks_market_holidays
WHERE status = 'closed'
)
SELECT
toString(settlement) AS settlement_date,
concat('VX ', formatDateTime(contract_month, '%b %Y')) AS vix_contract,
toInt32(dateDiff('day', prior_settlement, settlement)) AS days_since_prior_settlement
FROM
(
SELECT
contract_month,
settlement,
lagInFrame(settlement) OVER (ORDER BY settlement ASC ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) AS prior_settlement
FROM
(
SELECT
a.contract_month AS contract_month,
if(((a.ref_friday <= toDate(arrayMax(t.session_days))) AND (NOT has(t.session_days, a.ref_friday)))
OR has(h.closed_days, a.ref_friday),
addDays(a.ref_friday, -1),
a.ref_friday) AS spx_anchor,
addDays(spx_anchor, -30) AS wednesday_target,
if(((wednesday_target <= toDate(arrayMax(t.session_days))) AND (NOT has(t.session_days, wednesday_target)))
OR has(h.closed_days, wednesday_target),
addDays(wednesday_target, -1),
wednesday_target) AS settlement
FROM
(
SELECT
contract_month,
addMonths(contract_month, 1) AS ref_month,
addDays(ref_month, ((5 - toInt32(toDayOfWeek(ref_month)) + 7) % 7) + 14) AS ref_friday
FROM
(
SELECT addMonths(toDate('2025-12-01'), toInt32(arrayJoin(range(13)))) AS contract_month
)
) AS a
CROSS JOIN traded AS t
CROSS JOIN closures_ahead AS h
)
)
WHERE prior_settlement >= toDate('2025-12-01')
ORDER BY settlement ASCThe first gap in the panel runs 35 days and the next one 28. A holding period sized for four weeks gets five in some cycles, and the front contract's remaining time to settlement at any moment depends on which side of that sawtooth the calendar is on.
On a broker screen the same arithmetic shows up as a dated line that does not match its neighbours. A VIX option expiring on the Wednesday sits in a dropdown beside equity monthlies expiring on the Friday two days later, and those lines have a different underlying and a different settlement procedure. Why VIX options do not track the VIX takes that mismatch further.
FAQ
When do VIX futures expire each month?
On the Wednesday 30 calendar days before the third Friday of the following month, which is the AM-settled SPX expiration each contract is anchored to. When that Wednesday or the anchor Friday is an exchange holiday, final settlement moves one business day earlier, to the Tuesday.
Why do VIX futures expire on a Wednesday instead of a Friday?
Counting 30 days back from a Friday lands on a Wednesday. The offset keeps the SPX option series used in the settlement calculation at exactly 30 days to expiration, matching the 30-day window the VIX itself measures.
Is the VIX futures expiration the third Wednesday of the month?
No, and the two line up only by coincidence. The date comes from the next month's third Friday, so it can fall anywhere between the 13th and the 22nd, which the 2026 calendar above shows landing on several different Wednesdays of the month.
What happens if I hold a VIX future to its settlement date?
Trading in that contract stops on the settlement morning and the open position is marked to the SOQ, then settled in cash. No index shares and no options are delivered.
Do published VIX settlement dates ever change?
The rule is fixed, so the dates are computable years ahead. They move only with the exchange holiday calendar, which is the Tuesday case shown above.
How the trading calendar in these panels is derived
The queries hardcode no holiday list. Past sessions come from whether SPY printed a daily bar on a date: a full market closure prints none, so a weekday missing from that set was a day the exchange was shut. Dates still in the future are checked against the exchange holiday calendar, which carries the scheduled closures ahead. The walk-back then steps one business day earlier whenever the anchor Friday or the counted-back Wednesday is not a session. Rerunning any panel rebuilds every date from scratch.
Every panel on this page ships with the SQL that produced it, so the arithmetic is auditable line by line. To pull a different year's dates, or check one specific contract month, ask for it in plain English on the Strasmore terminal.