{"slug":"when-do-vix-futures-expire","qid":"roll_cycle","label":"roll_cycle","post_title":"when-do-vix-futures-expire","post_url":"/blog/when-do-vix-futures-expire#q-roll_cycle","columns":["settlement_date","vix_contract","days_since_prior_settlement"],"rows":[{"settlement_date":"2026-01-21","vix_contract":"VX Jan 2026","days_since_prior_settlement":35},{"settlement_date":"2026-02-18","vix_contract":"VX Feb 2026","days_since_prior_settlement":28},{"settlement_date":"2026-03-18","vix_contract":"VX Mar 2026","days_since_prior_settlement":28},{"settlement_date":"2026-04-15","vix_contract":"VX Apr 2026","days_since_prior_settlement":28},{"settlement_date":"2026-05-19","vix_contract":"VX May 2026","days_since_prior_settlement":34},{"settlement_date":"2026-06-17","vix_contract":"VX Jun 2026","days_since_prior_settlement":29},{"settlement_date":"2026-07-22","vix_contract":"VX Jul 2026","days_since_prior_settlement":35},{"settlement_date":"2026-08-19","vix_contract":"VX Aug 2026","days_since_prior_settlement":28},{"settlement_date":"2026-09-16","vix_contract":"VX Sep 2026","days_since_prior_settlement":28},{"settlement_date":"2026-10-21","vix_contract":"VX Oct 2026","days_since_prior_settlement":35},{"settlement_date":"2026-11-18","vix_contract":"VX Nov 2026","days_since_prior_settlement":28},{"settlement_date":"2026-12-16","vix_contract":"VX Dec 2026","days_since_prior_settlement":28}],"shape":"series","sql":"WITH\n    traded AS\n    (\n        SELECT groupArray(session_day) AS session_days\n        FROM\n        (\n            SELECT date AS session_day\n            FROM global_markets.stocks_daily_aggs\n            WHERE ticker = 'SPY'\n              AND date >= toDate('2014-11-01')\n            GROUP BY session_day\n        )\n    ),\n    closures_ahead AS\n    (\n        SELECT groupArray(date) AS closed_days\n        FROM global_markets.stocks_market_holidays\n        WHERE status = 'closed'\n    )\nSELECT\n    toString(settlement)                                    AS settlement_date,\n    concat('VX ', formatDateTime(contract_month, '%b %Y'))  AS vix_contract,\n    toInt32(dateDiff('day', prior_settlement, settlement))  AS days_since_prior_settlement\nFROM\n(\n    SELECT\n        contract_month,\n        settlement,\n        lagInFrame(settlement) OVER (ORDER BY settlement ASC ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) AS prior_settlement\n    FROM\n    (\n        SELECT\n            a.contract_month AS contract_month,\n            if(((a.ref_friday <= toDate(arrayMax(t.session_days))) AND (NOT has(t.session_days, a.ref_friday)))\n                OR has(h.closed_days, a.ref_friday),\n               addDays(a.ref_friday, -1),\n               a.ref_friday)                                AS spx_anchor,\n            addDays(spx_anchor, -30)                        AS wednesday_target,\n            if(((wednesday_target <= toDate(arrayMax(t.session_days))) AND (NOT has(t.session_days, wednesday_target)))\n                OR has(h.closed_days, wednesday_target),\n               addDays(wednesday_target, -1),\n               wednesday_target)                            AS settlement\n        FROM\n        (\n            SELECT\n                contract_month,\n                addMonths(contract_month, 1)                                             AS ref_month,\n                addDays(ref_month, ((5 - toInt32(toDayOfWeek(ref_month)) + 7) % 7) + 14) AS ref_friday\n            FROM\n            (\n                SELECT addMonths(toDate('2025-12-01'), toInt32(arrayJoin(range(13)))) AS contract_month\n            )\n        ) AS a\n        CROSS JOIN traded AS t\n        CROSS JOIN closures_ahead AS h\n    )\n)\nWHERE prior_settlement >= toDate('2025-12-01')\nORDER BY settlement ASC","computed_at":"2026-10-01T15:14:22.574125+00:00","elapsed":0.197883998}