Strasmore Research
Learn Matt ConnorBy Matt Connor

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.

QueryVIX futures final settlement dates, 2026 monthly contracts
settlement_datesettlement_labelvix_contractspx_anchor_fridaydays_from_equity_expiry
2026-01-21Wed Jan 21VX Jan 2026Feb 205
2026-02-18Wed Feb 18VX Feb 2026Mar 20-2
2026-03-18Wed Mar 18VX Mar 2026Apr 17-2
2026-04-15Wed Apr 15VX Apr 2026May 15-2
2026-05-19Tue May 19VX May 2026Jun 184
2026-06-17Wed Jun 17VX Jun 2026Jul 17-2
2026-07-22Wed Jul 22VX Jul 2026Aug 215
2026-08-19Wed Aug 19VX Aug 2026Sep 18-2
2026-09-16Wed Sep 16VX Sep 2026Oct 16-2
2026-10-21Wed Oct 21VX Oct 2026Nov 205
2026-11-18Wed Nov 18VX Nov 2026Dec 18-2
2026-12-16Wed Dec 16VX Dec 2026Jan 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 ASC
Run this yourself

All 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.

QueryThe walk-back arithmetic, step by step, for four contracts
vix_contractnext_month_third_fridayfriday_statuscounted_back_fromthirty_days_earlierthat_day_statusfinal_settlement_date
VX Jan 2026Fri Feb 20, 2026trading dayFri Feb 20, 2026Wed Jan 21, 2026trading dayWed Jan 21, 2026
VX Dec 2026Fri Jan 15, 2027trading dayFri Jan 15, 2027Wed Dec 16, 2026trading dayWed Dec 16, 2026
VX May 2026Fri Jun 19, 2026exchange holidayThu Jun 18, 2026Tue May 19, 2026trading dayTue May 19, 2026
VX Jun 2024Fri Jul 19, 2024trading dayFri Jul 19, 2024Wed Jun 19, 2024exchange holidayTue 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) ASC
Run this yourself

Read 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.

QueryContract months since 2015 that did not settle on a Wednesday
settlement_datesettlement_weekdayvix_contractclosed_day_in_the_way
2019-03-19TuesdayVX Mar 2019Fri Apr 19, 2019
2022-03-15TuesdayVX Mar 2022Fri Apr 15, 2022
2024-06-18TuesdayVX Jun 2024Wed Jun 19, 2024
2025-03-18TuesdayVX Mar 2025Fri Apr 18, 2025
2026-05-19TuesdayVX May 2026Fri 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 ASC
Run this yourself

5 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.

QueryDays between consecutive VIX futures settlements, 2026 cycle
settlement_datevix_contractdays_since_prior_settlement
2026-01-21VX Jan 202635
2026-02-18VX Feb 202628
2026-03-18VX Mar 202628
2026-04-15VX Apr 202628
2026-05-19VX May 202634
2026-06-17VX Jun 202629
2026-07-22VX Jul 202635
2026-08-19VX Aug 202628
2026-09-16VX Sep 202628
2026-10-21VX Oct 202635
2026-11-18VX Nov 202628
2026-12-16VX Dec 202628
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 ASC
Run this yourself

The 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.