{"slug":"verifiable-ai-trading-track-records","qid":"outcome_band","label":"SPY holding-period outcomes since 2015: 5th percentile, median, 95th percentile","post_title":"Can an AI Trading Track Record Be Verified?","post_url":"/blog/verifiable-ai-trading-track-records#q-outcome_band","columns":["holding_period","window_count","p05_return_pct","median_return_pct","p95_return_pct","spread_pct","stdev_pct"],"rows":[{"holding_period":"1 month","window_count":2868,"p05_return_pct":-6.6,"median_return_pct":1.6,"p95_return_pct":7.1,"spread_pct":13.7,"stdev_pct":4.5},{"holding_period":"3 months","window_count":2826,"p05_return_pct":-8.8,"median_return_pct":3.9,"p95_return_pct":12.2,"spread_pct":21.1,"stdev_pct":6.9},{"holding_period":"6 months","window_count":2763,"p05_return_pct":-9.4,"median_return_pct":6.7,"p95_return_pct":19.4,"spread_pct":28.8,"stdev_pct":9},{"holding_period":"12 months","window_count":2637,"p05_return_pct":-10.9,"median_return_pct":14.1,"p95_return_pct":34.2,"spread_pct":45,"stdev_pct":13.6}],"shape":"table","sql":"WITH daily AS (\n    SELECT toDate(toTimeZone(window_start, 'America/New_York')) AS session_date,\n           toFloat64(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('2015-01-02')\n      AND toDate(toTimeZone(window_start, 'America/New_York')) <= toDate('2026-06-30')\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 session_date\n),\nseries AS (\n    SELECT groupArray(close_px) AS px\n    FROM (SELECT close_px FROM daily ORDER BY session_date)\n),\nhorizons AS (\n    SELECT arrayJoin([21, 63, 126, 252]) AS sessions, px\n    FROM series\n),\nwindows AS (\n    SELECT sessions,\n           arrayJoin(arrayMap(i -> (i, 100 * (px[i + sessions] / px[i] - 1)),\n                              range(1, length(px) - sessions + 1))) AS w\n    FROM horizons\n),\nmeasured AS (\n    SELECT sessions,\n           w.1 AS window_index,\n           w.2 AS window_return_pct\n    FROM windows\n)\nSELECT multiIf(sessions = 21, '1 month',\n               sessions = 63, '3 months',\n               sessions = 126, '6 months',\n               '12 months') AS holding_period,\n       count() AS window_count,\n       round(quantileDeterministic(0.05)(window_return_pct,\n                                         cityHash64(window_index * 1000 + sessions)), 1) AS p05_return_pct,\n       round(quantileDeterministic(0.50)(window_return_pct,\n                                         cityHash64(window_index * 1000 + sessions)), 1) AS median_return_pct,\n       round(quantileDeterministic(0.95)(window_return_pct,\n                                         cityHash64(window_index * 1000 + sessions)), 1) AS p95_return_pct,\n       round(quantileDeterministic(0.95)(window_return_pct,\n                                         cityHash64(window_index * 1000 + sessions))\n             - quantileDeterministic(0.05)(window_return_pct,\n                                           cityHash64(window_index * 1000 + sessions)), 1) AS spread_pct,\n       round(stddevSamp(window_return_pct), 1) AS stdev_pct\nFROM measured\nGROUP BY sessions\nORDER BY sessions","computed_at":"2026-08-03T11:22:51.191744+00:00","elapsed":5.032632493}