{"slug":"how-risky-is-options-trading","qid":"expiry_outcomes","label":"AAPL contracts trading on their own expiration day: share finishing out of the money, six monthly cycles","post_title":"How Risky Is Options Trading? The Mechanics","post_url":"/blog/how-risky-is-options-trading#q-expiry_outcomes","columns":["cycle","contract_count","calls_otm_pct","puts_otm_pct"],"rows":[{"cycle":"Dec 2025","contract_count":102,"calls_otm_pct":23.9,"puts_otm_pct":68.6},{"cycle":"Jan 2026","contract_count":93,"calls_otm_pct":39,"puts_otm_pct":58.8},{"cycle":"Feb 2026","contract_count":87,"calls_otm_pct":40.7,"puts_otm_pct":63.6},{"cycle":"Mar 2026","contract_count":89,"calls_otm_pct":49.1,"puts_otm_pct":62.5},{"cycle":"Apr 2026","contract_count":101,"calls_otm_pct":29.5,"puts_otm_pct":75},{"cycle":"May 2026","contract_count":136,"calls_otm_pct":20,"puts_otm_pct":70.6}],"shape":"ranking","sql":"WITH traded AS (\n    SELECT toDate(sip_timestamp) AS session_date,\n           ticker\n    FROM global_markets.options_trades\n    WHERE ticker LIKE 'O:AAPL2%'\n      AND toDate(sip_timestamp) IN ('2025-12-19', '2026-01-16', '2026-02-20', '2026-03-20', '2026-04-17', '2026-05-15')\n    GROUP BY session_date, ticker\n),\nexpiring AS (\n    SELECT session_date AS expiration_date,\n           substring(ticker, 13, 1) AS right_code,\n           toFloat64OrZero(substring(ticker, 14, 8)) / 1000 AS strike_usd\n    FROM traded\n    WHERE toDate(concat('20', substring(ticker, 7, 2), '-', substring(ticker, 9, 2), '-', substring(ticker, 11, 2))) = session_date\n),\nspot AS (\n    SELECT toDate(toTimeZone(window_start, 'America/New_York')) AS expiration_date,\n           argMax(toFloat64(close), window_start) AS closing_price\n    FROM global_markets.delayed_stocks_minute_aggs\n    WHERE ticker = 'AAPL'\n      AND toDate(toTimeZone(window_start, 'America/New_York')) IN ('2025-12-19', '2026-01-16', '2026-02-20', '2026-03-20', '2026-04-17', '2026-05-15')\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 expiration_date\n)\nSELECT formatDateTimeInJodaSyntax(e.expiration_date, 'MMM yyyy') AS cycle,\n       count() AS contract_count,\n       round(100 * countIf(e.right_code = 'C' AND e.strike_usd > s.closing_price) / countIf(e.right_code = 'C'), 1) AS calls_otm_pct,\n       round(100 * countIf(e.right_code = 'P' AND e.strike_usd < s.closing_price) / countIf(e.right_code = 'P'), 1) AS puts_otm_pct\nFROM expiring AS e\nINNER JOIN spot AS s ON e.expiration_date = s.expiration_date\nGROUP BY e.expiration_date\nHAVING countIf(e.right_code = 'C') > 0 AND countIf(e.right_code = 'P') > 0\nORDER BY e.expiration_date","computed_at":"2026-08-01T09:06:08.620539+00:00","elapsed":0.004512759}