{"slug":"do-stocks-move-as-much-as-options-predict","qid":"by_year","label":"Under-implied share and median ratio, year by year","post_title":"Do Stocks Move as Much as Options Predict?","post_url":"/blog/do-stocks-move-as-much-as-options-predict#q-by_year","columns":["year","event_count","under_implied_pct","median_ratio"],"rows":[{"year":"2025","event_count":57,"under_implied_pct":61.4,"median_ratio":0.78},{"year":"2026","event_count":46,"under_implied_pct":43.5,"median_ratio":1.12}],"shape":"ranking","sql":"WITH\nreports AS (\n    SELECT\n        toString(ticker)    AS sym,\n        toDate(filing_date) AS report_date\n    FROM global_markets.stocks_8k_text\n    WHERE ticker IN ('AAPL', 'AMD', 'AMZN', 'AVGO', 'DIS', 'GOOGL', 'JPM', 'KO', 'META', 'MSFT', 'NFLX', 'NVDA', 'ORCL', 'QCOM', 'TSLA', 'WMT')\n      AND filing_date >= '2021-09-01'\n      AND filing_date <  '2026-09-20'\n      AND (items_text ILIKE '%results of operations and financial condition%'\n        OR items_text ILIKE '%item 2.02%')\n    GROUP BY sym, report_date\n),\nsessions AS (\n    SELECT\n        toString(ticker) AS sym,\n        toDate(date)     AS session_date,\n        toFloat64(close) AS px\n    FROM global_markets.stocks_daily_aggs\n    WHERE ticker IN ('AAPL', 'AMD', 'AMZN', 'AVGO', 'DIS', 'GOOGL', 'JPM', 'KO', 'META', 'MSFT', 'NFLX', 'NVDA', 'ORCL', 'QCOM', 'TSLA', 'WMT')\n      AND date >= '2021-08-01'\n      AND date <  '2026-09-27'\n      AND close > 0\n),\nspans AS (\n    SELECT\n        r.sym         AS sym,\n        r.report_date AS report_date,\n        maxIf(s.session_date, s.session_date < r.report_date) AS pre_date,\n        minIf(s.session_date, s.session_date > r.report_date) AS post_date\n    FROM reports AS r\n    INNER JOIN sessions AS s ON s.sym = r.sym\n    WHERE s.session_date >= r.report_date - 8\n      AND s.session_date <= r.report_date + 8\n    GROUP BY r.sym, r.report_date\n    HAVING countIf(s.session_date < r.report_date) > 0\n       AND countIf(s.session_date > r.report_date) > 0\n),\nmoves AS (\n    SELECT\n        sp.sym         AS sym,\n        sp.report_date AS report_date,\n        sp.pre_date    AS pre_date,\n        round(100 * abs(b.px / a.px - 1), 2) AS realized_pct\n    FROM spans AS sp\n    INNER JOIN sessions AS a ON a.sym = sp.sym AND a.session_date = sp.pre_date\n    INNER JOIN sessions AS b ON b.sym = sp.sym AND b.session_date = sp.post_date\n),\ngreeks AS (\n    SELECT\n        toString(underlying_symbol)   AS sym,\n        toDate(date)                  AS pre_date,\n        toDate(expiration_date)       AS expiry,\n        lower(option_type)            AS side,\n        toFloat64(strike_price)       AS strike,\n        toFloat64(option_close)       AS opt_px,\n        toFloat64(underlying_close)   AS spot,\n        toFloat64(implied_volatility) AS iv,\n        toUInt16(days_to_expiry)      AS dte\n    FROM global_markets.options_greeks\n    WHERE underlying_symbol IN ('AAPL', 'AMD', 'AMZN', 'AVGO', 'DIS', 'GOOGL', 'JPM', 'KO', 'META', 'MSFT', 'NFLX', 'NVDA', 'ORCL', 'QCOM', 'TSLA', 'WMT')\n      AND date >= '2021-09-01'\n      AND date <  '2026-09-20'\n      AND iv_converged = 1\n      AND volume > 0\n      AND days_to_expiry BETWEEN 1 AND 45\n      AND underlying_close > 0\n      AND option_close > 0\n      AND abs(toFloat64(strike_price) / toFloat64(underlying_close) - 1) < 0.05\n),\nchain AS (\n    SELECT\n        g.sym      AS sym,\n        g.pre_date AS pre_date,\n        g.expiry   AS expiry,\n        g.side     AS side,\n        g.strike   AS strike,\n        g.opt_px   AS opt_px,\n        g.spot     AS spot,\n        g.iv       AS iv,\n        g.dte      AS dte\n    FROM greeks AS g\n    INNER JOIN moves AS m ON m.sym = g.sym AND m.pre_date = g.pre_date\n    WHERE g.expiry > m.report_date\n),\nfront AS (\n    SELECT sym, pre_date, min(expiry) AS expiry\n    FROM chain\n    GROUP BY sym, pre_date\n),\nstraddles AS (\n    SELECT\n        c.sym       AS sym,\n        c.pre_date  AS pre_date,\n        c.strike    AS strike,\n        any(c.spot) AS spot,\n        max(c.dte)  AS dte,\n        avgIf(c.opt_px, c.side IN ('call', 'c')) AS call_px,\n        avgIf(c.opt_px, c.side IN ('put', 'p'))  AS put_px,\n        avg(c.iv)   AS atm_iv\n    FROM chain AS c\n    INNER JOIN front AS f\n        ON f.sym = c.sym AND f.pre_date = c.pre_date AND f.expiry = c.expiry\n    GROUP BY c.sym, c.pre_date, c.strike\n    HAVING countIf(c.side IN ('call', 'c')) > 0\n       AND countIf(c.side IN ('put', 'p')) > 0\n),\nimplied AS (\n    SELECT\n        sym,\n        pre_date,\n        argMin(round(100 * (call_px + put_px) / spot, 2), abs(strike / spot - 1)) AS straddle_pct,\n        argMin(round(100 * atm_iv * sqrt(dte / 365), 2), abs(strike / spot - 1))  AS iv_root_t_pct\n    FROM straddles\n    GROUP BY sym, pre_date\n)\nSELECT\n    toString(toYear(m.report_date))                                     AS year,\n    toUInt32(count())                                                   AS event_count,\n    round(100 * countIf(m.realized_pct <= i.straddle_pct) / count(), 1) AS under_implied_pct,\n    round(quantileDeterministic(0.5)(m.realized_pct / i.straddle_pct, cityHash64(m.sym, m.report_date)), 2) AS median_ratio\nFROM moves AS m\nINNER JOIN implied AS i ON i.sym = m.sym AND i.pre_date = m.pre_date\nWHERE i.straddle_pct > 0\nGROUP BY year\nORDER BY year","computed_at":"2026-09-27T16:03:10.761237+00:00","elapsed":0.005887638}