{"slug":"what-is-max-pain","qid":"pain_vs_close","label":"Minimising strike vs settlement close: SPY monthly expirations, Feb to Jul 2026","post_title":"What Is Max Pain in Options? The Real Math","post_url":"/blog/what-is-max-pain#q-pain_vs_close","columns":["expiry_label","min_payout_strike","settlement_close","max_pain_miss_pct","week_earlier_miss_pct"],"rows":[{"expiry_label":"Feb 20","min_payout_strike":687,"settlement_close":689.36,"max_pain_miss_pct":0.34,"week_earlier_miss_pct":1.12},{"expiry_label":"Mar 20","min_payout_strike":652,"settlement_close":648.54,"max_pain_miss_pct":0.53,"week_earlier_miss_pct":2.12},{"expiry_label":"Apr 17","min_payout_strike":707,"settlement_close":710.04,"max_pain_miss_pct":0.42,"week_earlier_miss_pct":4.32},{"expiry_label":"May 15","min_payout_strike":740,"settlement_close":739.09,"max_pain_miss_pct":0.12,"week_earlier_miss_pct":0.21},{"expiry_label":"Jun 18","min_payout_strike":746,"settlement_close":746.56,"max_pain_miss_pct":0.07,"week_earlier_miss_pct":1.19},{"expiry_label":"Jul 17","min_payout_strike":746,"settlement_close":743.2,"max_pain_miss_pct":0.37,"week_earlier_miss_pct":1.57}],"shape":"table","sql":"WITH expiries AS (\n    SELECT arrayJoin([toDate('2026-02-20'), toDate('2026-03-20'), toDate('2026-04-17'),\n                      toDate('2026-05-15'), toDate('2026-06-18'), toDate('2026-07-17')]) AS expiry\n),\nchain AS (\n    SELECT toDateOrNull(concat('20', substring(ticker, length(ticker) - 14, 6)))  AS expiry,\n           intDiv(toUInt32OrZero(substring(ticker, length(ticker) - 7, 8)), 1000) AS strike,\n           substring(ticker, length(ticker) - 8, 1)                              AS opt_type,\n           sum(toFloat64(volume))                                                AS contracts\n    FROM global_markets.options_minute_aggs\n    WHERE ((window_start >= toDateTime('2026-02-16 08:00:00') AND window_start < toDateTime('2026-02-21 04:00:00'))\n        OR (window_start >= toDateTime('2026-03-16 08:00:00') AND window_start < toDateTime('2026-03-21 04:00:00'))\n        OR (window_start >= toDateTime('2026-04-13 08:00:00') AND window_start < toDateTime('2026-04-18 04:00:00'))\n        OR (window_start >= toDateTime('2026-05-11 08:00:00') AND window_start < toDateTime('2026-05-16 04:00:00'))\n        OR (window_start >= toDateTime('2026-06-15 08:00:00') AND window_start < toDateTime('2026-06-19 04:00:00'))\n        OR (window_start >= toDateTime('2026-07-13 08:00:00') AND window_start < toDateTime('2026-07-18 04:00:00')))\n      AND startsWith(ticker, 'O:SPY26')\n      AND toDateOrNull(concat('20', substring(ticker, length(ticker) - 14, 6))) IN (SELECT expiry FROM expiries)\n    GROUP BY expiry, strike, opt_type\n),\ncurve AS (\n    SELECT c1.expiry AS expiry,\n           c1.strike AS settle,\n           sum(multiIf(c2.opt_type = 'C' AND c2.strike < c1.strike, c2.contracts * (c1.strike - c2.strike),\n                       c2.opt_type = 'P' AND c2.strike > c1.strike, c2.contracts * (c2.strike - c1.strike),\n                       0)) AS payout\n    FROM chain AS c1 INNER JOIN chain AS c2 ON c1.expiry = c2.expiry\n    GROUP BY c1.expiry, c1.strike\n),\npain AS (\n    SELECT expiry, argMin(settle, payout) AS pain_strike FROM curve GROUP BY expiry\n),\npx AS (\n    SELECT toDate(toTimeZone(window_start, 'America/New_York')) AS d,\n           argMax(close, window_start)                          AS close_px\n    FROM global_markets.delayed_stocks_minute_aggs\n    WHERE ticker = 'SPY'\n      AND toDate(toTimeZone(window_start, 'America/New_York')) >= toDate('2026-02-05')\n      AND toDate(toTimeZone(window_start, 'America/New_York')) <= toDate('2026-07-17')\n      AND (toHour(toTimeZone(window_start, 'America/New_York')) * 60\n           + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959\n    GROUP BY d\n),\nprior AS (\n    SELECT expiries.expiry AS expiry, argMax(px.close_px, px.d) AS week_earlier_px\n    FROM expiries CROSS JOIN px\n    WHERE px.d <= expiries.expiry - 7\n    GROUP BY expiries.expiry\n)\nSELECT formatDateTime(pain.expiry, '%b %e')                                   AS expiry_label,\n       pain.pain_strike                                                       AS min_payout_strike,\n       round(px.close_px, 2)                                                  AS settlement_close,\n       round(abs(px.close_px - pain.pain_strike) / px.close_px * 100, 2)      AS max_pain_miss_pct,\n       round(abs(px.close_px - prior.week_earlier_px) / px.close_px * 100, 2) AS week_earlier_miss_pct\nFROM pain\nINNER JOIN px ON pain.expiry = px.d\nINNER JOIN prior ON pain.expiry = prior.expiry\nORDER BY pain.expiry","computed_at":"2026-07-31T08:40:56.667735+00:00","elapsed":3.733405248}