{"slug":"backtest-vs-forward-test","qid":"risk_sweep","label":"risk_sweep","post_title":"backtest-vs-forward-test","post_url":"/blog/lang/th/backtest-vs-forward-test#q-risk_sweep","columns":["label","max_dd_in_sample_pct","max_dd_forward_pct","hit_rate_in_sample_pct","hit_rate_forward_pct"],"rows":[{"label":"MA 20","max_dd_in_sample_pct":14.19,"max_dd_forward_pct":19.06,"hit_rate_in_sample_pct":53.03,"hit_rate_forward_pct":53.71},{"label":"MA 150","max_dd_in_sample_pct":16.2,"max_dd_forward_pct":16.94,"hit_rate_in_sample_pct":53.5,"hit_rate_forward_pct":53.36},{"label":"MA 200","max_dd_in_sample_pct":17.84,"max_dd_forward_pct":16.92,"hit_rate_in_sample_pct":53.51,"hit_rate_forward_pct":52.99},{"label":"MA 100","max_dd_in_sample_pct":15.41,"max_dd_forward_pct":16.64,"hit_rate_in_sample_pct":53.84,"hit_rate_forward_pct":52.62},{"label":"MA 50","max_dd_in_sample_pct":12.27,"max_dd_forward_pct":11.05,"hit_rate_in_sample_pct":53.6,"hit_rate_forward_pct":53.16}],"shape":"table","sql":"WITH px AS (\n    SELECT\n        ticker,\n        date,\n        toFloat64(close) AS c\n    FROM global_markets.stocks_daily_aggs\n    WHERE ticker IN ('SPY', 'MSFT', 'JPM', 'HD', 'PEP')\n      AND date >= '2008-12-01'\n      AND date <  '2026-10-01'\n),\nma AS (\n    SELECT\n        ticker,\n        date,\n        c,\n        avg(c) OVER (PARTITION BY ticker ORDER BY date ROWS BETWEEN 19  PRECEDING AND CURRENT ROW) AS ma_20,\n        avg(c) OVER (PARTITION BY ticker ORDER BY date ROWS BETWEEN 49  PRECEDING AND CURRENT ROW) AS ma_50,\n        avg(c) OVER (PARTITION BY ticker ORDER BY date ROWS BETWEEN 99  PRECEDING AND CURRENT ROW) AS ma_100,\n        avg(c) OVER (PARTITION BY ticker ORDER BY date ROWS BETWEEN 149 PRECEDING AND CURRENT ROW) AS ma_150,\n        avg(c) OVER (PARTITION BY ticker ORDER BY date ROWS BETWEEN 199 PRECEDING AND CURRENT ROW) AS ma_200\n    FROM px\n),\ngrid AS (\n    SELECT\n        ticker,\n        date,\n        c,\n        g.1 AS label,\n        g.2 AS ma_value\n    FROM ma\n    ARRAY JOIN [('MA 20', ma_20), ('MA 50', ma_50), ('MA 100', ma_100), ('MA 150', ma_150), ('MA 200', ma_200)] AS g\n),\nsig AS (\n    SELECT\n        label,\n        ticker,\n        date,\n        c,\n        any(c)                      OVER (PARTITION BY label, ticker ORDER BY date ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING) AS prev_c,\n        any(if(c > ma_value, 1, 0)) OVER (PARTITION BY label, ticker ORDER BY date ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING) AS held\n    FROM grid\n),\nport AS (\n    SELECT\n        label,\n        if(date < '2020-01-01', 'in_sample', 'forward') AS window_name,\n        date,\n        avg(held * (c / prev_c - 1)) AS r,\n        avg(held)                    AS exposure\n    FROM sig\n    WHERE prev_c > 0\n      AND ((date >= '2010-01-01' AND date < '2020-01-01')\n        OR (date >= '2020-09-01' AND date < '2026-10-01'))\n    GROUP BY label, window_name, date\n),\ncurve AS (\n    SELECT\n        label,\n        window_name,\n        date,\n        r,\n        exposure,\n        exp(sum(log(1 + r)) OVER (PARTITION BY label, window_name ORDER BY date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)) AS equity\n    FROM port\n),\ndd AS (\n    SELECT\n        label,\n        window_name,\n        r,\n        exposure,\n        equity / greatest(max(equity) OVER (PARTITION BY label, window_name ORDER BY date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 1.0) - 1 AS drawdown\n    FROM curve\n),\nmetrics AS (\n    SELECT\n        label,\n        window_name,\n        round(abs(min(drawdown)) * 100, 2)                                                        AS max_dd_pct,\n        round(100.0 * countIf(r > 0 AND exposure > 0) / greatest(countIf(exposure > 0), 1), 2)     AS hit_rate_pct\n    FROM dd\n    GROUP BY label, window_name\n)\nSELECT\n    label,\n    anyIf(max_dd_pct, window_name = 'in_sample')   AS max_dd_in_sample_pct,\n    anyIf(max_dd_pct, window_name = 'forward')     AS max_dd_forward_pct,\n    anyIf(hit_rate_pct, window_name = 'in_sample') AS hit_rate_in_sample_pct,\n    anyIf(hit_rate_pct, window_name = 'forward')   AS hit_rate_forward_pct\nFROM metrics\nGROUP BY label\nHAVING countIf(window_name = 'in_sample') > 0\n   AND countIf(window_name = 'forward') > 0\nORDER BY max_dd_forward_pct DESC","computed_at":"2026-10-01T16:05:19.412304+00:00","elapsed":0.009162136}