{"slug":"bootstrapping-backtest-confidence-intervals","qid":"sharpe_by_year","label":"One position, one year at a time: SPY annualized Sharpe by calendar year","post_title":"Bootstrapping Backtest Confidence Bands","post_url":"/blog/bootstrapping-backtest-confidence-intervals#q-sharpe_by_year","columns":["year","obs_count","ann_return_pct","ann_vol_pct","sharpe_ratio"],"rows":[{"year":"2012","obs_count":250,"ann_return_pct":13.56,"ann_vol_pct":12.79,"sharpe_ratio":1.06},{"year":"2013","obs_count":252,"ann_return_pct":26.63,"ann_vol_pct":11.13,"sharpe_ratio":2.39},{"year":"2014","obs_count":252,"ann_return_pct":11.33,"ann_vol_pct":11.28,"sharpe_ratio":1},{"year":"2015","obs_count":252,"ann_return_pct":0.39,"ann_vol_pct":15.58,"sharpe_ratio":0.03},{"year":"2016","obs_count":252,"ann_return_pct":10.07,"ann_vol_pct":13.14,"sharpe_ratio":0.77},{"year":"2017","obs_count":251,"ann_return_pct":18.02,"ann_vol_pct":6.77,"sharpe_ratio":2.66},{"year":"2018","obs_count":251,"ann_return_pct":-5.12,"ann_vol_pct":17.13,"sharpe_ratio":-0.3},{"year":"2019","obs_count":252,"ann_return_pct":26.1,"ann_vol_pct":12.55,"sharpe_ratio":2.08},{"year":"2020","obs_count":253,"ann_return_pct":20.58,"ann_vol_pct":33.51,"sharpe_ratio":0.61},{"year":"2021","obs_count":252,"ann_return_pct":24.8,"ann_vol_pct":13.11,"sharpe_ratio":1.89},{"year":"2022","obs_count":251,"ann_return_pct":-18.81,"ann_vol_pct":24.27,"sharpe_ratio":-0.78},{"year":"2023","obs_count":250,"ann_return_pct":22.79,"ann_vol_pct":13.19,"sharpe_ratio":1.73},{"year":"2024","obs_count":252,"ann_return_pct":21.75,"ann_vol_pct":12.59,"sharpe_ratio":1.73},{"year":"2025","obs_count":250,"ann_return_pct":17.14,"ann_vol_pct":19.48,"sharpe_ratio":0.88}],"shape":"table","sql":"WITH daily AS\n(\n    SELECT\n        date,\n        toFloat64(close) / nullIf(lagInFrame(toFloat64(close), 1)\n            OVER (ORDER BY date ROWS BETWEEN 1 PRECEDING AND CURRENT ROW), 0) - 1 AS ret\n    FROM global_markets.stocks_daily_aggs\n    WHERE ticker = 'SPY'\n      AND date >= '2011-12-01'\n      AND date <  '2026-01-01'\n)\nSELECT\n    toString(toYear(date))                            AS year,\n    count()                                           AS obs_count,\n    round(avg(ret) * 252 * 100, 2)                    AS ann_return_pct,\n    round(stddevSamp(ret) * sqrt(252) * 100, 2)       AS ann_vol_pct,\n    round(avg(ret) / stddevSamp(ret) * sqrt(252), 2)  AS sharpe_ratio\nFROM daily\nWHERE date >= '2012-01-01'\n  AND ret IS NOT NULL\nGROUP BY year\nORDER BY year","computed_at":"2026-08-14T16:02:32.790277+00:00","elapsed":0.002703633}