{"slug":"how-monthly-returns-are-measured","qid":"compounding_check","label":"Sum vs compound: twelve SPY monthly returns linked three ways","post_title":"How Monthly Stock Returns Are Measured","post_url":"/blog/how-monthly-returns-are-measured#q-compounding_check","columns":["months_linked","simple_sum_pct","geometric_compound_pct","direct_twelve_month_pct","compounding_gap_points","sum_of_monthly_log_returns_pct","twelve_month_log_return_pct"],"rows":[{"months_linked":12,"simple_sum_pct":19.83,"geometric_compound_pct":20.79,"direct_twelve_month_pct":20.79,"compounding_gap_points":0.96,"sum_of_monthly_log_returns_pct":18.89,"twelve_month_log_return_pct":18.89}],"shape":"scalar","sql":"WITH daily AS (\n    SELECT toDate(toTimeZone(window_start, 'America/New_York')) AS day,\n           argMaxIf(toFloat64(close), window_start,\n               toTimeZone(window_start, 'America/New_York') >= toDateTime(concat(toString(toDate(toTimeZone(window_start, 'America/New_York'))), ' 09:30:00'), 'America/New_York')\n               AND toTimeZone(window_start, 'America/New_York') < toDateTime(concat(toString(toDate(toTimeZone(window_start, 'America/New_York'))), ' 16:00:00'), 'America/New_York')) AS cl\n    FROM global_markets.delayed_stocks_minute_aggs\n    WHERE ticker = 'SPY'\n      AND window_start >= '2025-06-24 04:00:00'\n      AND window_start < '2026-07-01 08:00:00'\n    GROUP BY day\n),\nmonthly AS (\n    SELECT toStartOfMonth(day) AS month_start,\n           argMax(cl, day) AS month_close\n    FROM daily\n    GROUP BY month_start\n),\nrets AS (\n    SELECT month_start,\n           100 * (month_close / lagInFrame(month_close) OVER (ORDER BY month_start) - 1) AS r\n    FROM monthly\n),\nends AS (\n    SELECT argMin(month_close, month_start) AS base_close,\n           argMax(month_close, month_start) AS final_close\n    FROM monthly\n)\nSELECT count() AS months_linked,\n       round(sum(r), 2) AS simple_sum_pct,\n       round(100 * (exp(sum(log(1 + r / 100))) - 1), 2) AS geometric_compound_pct,\n       round(100 * (any(final_close) / any(base_close) - 1), 2) AS direct_twelve_month_pct,\n       round(100 * (exp(sum(log(1 + r / 100))) - 1) - sum(r), 2) AS compounding_gap_points,\n       round(sum(100 * log(1 + r / 100)), 2) AS sum_of_monthly_log_returns_pct,\n       round(100 * log(any(final_close) / any(base_close)), 2) AS twelve_month_log_return_pct\nFROM rets CROSS JOIN ends\nWHERE isFinite(r)","computed_at":"2026-07-26T05:37:29.914616+00:00","elapsed":0.876604372}