Every feature has a timestamp: leakage, scaling and twin features

ml basics

On this page
  1. The leak
  2. Scaling: units, not information
  3. Twin features: ridge shares, lasso chooses
  4. A rolling z-score hides a crash
  5. Traps
  6. The code
  7. Summary
  8. Next
  9. References

A model is only as honest as its inputs. This post builds a dishonest input on purpose, shows how it looks in the score and how to catch it, and then covers the two quieter feature problems: units and twins.

a coefficient or a chosen penaltyerrorthe leak

The leak

The series’ seven features are all computed from a ticker’s prices up to the day of the forecast: recent returns, recent volatility, distance from the moving average. I added an eighth on purpose: SPY’s volatility over the same month the model is trying to predict. It is a perfectly reasonable-looking column, the kind that appears when a market-wide table is joined onto a per-ticker table by month without thinking about which month.

FeaturesTest RMSE
Seven honest features7.50
Plus SPY’s volatility over the same month (not knowable yet)6.81\bad{6.81}
Plus SPY’s volatility over the last month (knowable)7.56

The leak looks like the best model in the table. The honest version of the same idea, last month’s market volatility, adds nothing. Nothing in the score distinguishes the two; the only difference is a timestamp.

{"panels":[{"x0":52.28,"x1":564.92,"y0":29.16,"y1":177.33,"title":"1. The leak test: lag the suspicious feature and watch the score","xlabel":"Feature set","ylabel":"Test RMSE (volatility points)","xlog":false},{"x0":52.28,"x1":564.92,"y0":352.09,"y1":500.27,"title":"2. Ridge shrinks every coefficient toward zero; lasso drives some exactly to zero","xlabel":"Penalty strength alpha (log scale)","ylabel":"Standardised coefficient","xlog":true},{"x0":52.28,"x1":564.92,"y0":675.03,"y1":823.2,"title":"3. A rolling z-score hides a crash: the window's spread balloons with the fall","xlabel":"Date","ylabel":"z-score","xlog":false}],"series":[{"panel":1,"label":"ret_1d (lasso)","color":"var(--chart-maroon, #b6262c)","x":[0.001,0.0013434,0.00180472,0.00242446,0.00325702,0.00437548,0.00587802,0.00789652,0.0106082,0.014251,0.0191448,0.0257191,0.0345511,0.0464159,0.0623551,0.0837678,0.112534,0.151178,0.203092,0.272833,0.366524,0.492388,0.661474,0.888624,1.19378,1.60372,2.15443,2.89427,3.88816,5.22335,7.01704,9.42668,12.6638,17.0125,22.8546,30.7029,41.2463,55.4102,74.438,100.0],"y":[-0.4717,-0.471578,-0.471415,-0.471195,-0.470902,-0.470505,-0.469972,-0.469262,-0.468298,-0.467001,-0.465271,-0.46292,-0.459783,-0.455488,-0.449724,-0.441949,-0.431554,-0.3981,-0.344558,-0.291775,-0.222313,-0.128997,-0.00366332,-0.0,-0.0,-0.0,-0.0,-0.0,-0.0,-0.0,-0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0],"sx":[75.58,87.53,99.48,111.43,123.38,135.33,147.28,159.23,171.18,183.13,195.08,207.03,218.98,230.93,242.88,254.83,266.78,278.73,290.68,302.63,314.58,326.53,338.48,350.43,362.38,374.33,386.28,398.23,410.18,422.13,434.08,446.02,457.97,469.92,481.87,493.82,505.77,517.72,529.67,541.62],"sy":[454.23,454.23,454.23,454.23,454.22,454.22,454.21,454.2,454.19,454.17,454.14,454.11,454.07,454.01,453.93,453.82,453.68,453.21,452.47,451.74,450.77,449.48,447.74,447.69,447.69,447.69,447.69,447.69,447.69,447.69,447.69,447.69,447.69,447.69,447.69,447.69,447.69,447.69,447.69,447.69]},{"panel":1,"label":"ret_5d (lasso)","color":"var(--chart-blue, #3b6ea5)","x":[0.001,0.0013434,0.00180472,0.00242446,0.00325702,0.00437548,0.00587802,0.00789652,0.0106082,0.014251,0.0191448,0.0257191,0.0345511,0.0464159,0.0623551,0.0837678,0.112534,0.151178,0.203092,0.272833,0.366524,0.492388,0.661474,0.888624,1.19378,1.60372,2.15443,2.89427,3.88816,5.22335,7.01704,9.42668,12.6638,17.0125,22.8546,30.7029,41.2463,55.4102,74.438,100.0],"y":[-1.35619,-1.35678,-1.35757,-1.35862,-1.36007,-1.36198,-1.36454,-1.36803,-1.37265,-1.37884,-1.38725,-1.39835,-1.41341,-1.43307,-1.45952,-1.49483,-1.54261,-1.51294,-1.43248,-1.38704,-1.3311,-1.25594,-1.15502,-0.972233,-0.725298,-0.39358,-0.0,-0.0,-0.0,-0.0,-0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0],"sx":[75.58,87.53,99.48,111.43,123.38,135.33,147.28,159.23,171.18,183.13,195.08,207.03,218.98,230.93,242.88,254.83,266.78,278.73,290.68,302.63,314.58,326.53,338.48,350.43,362.38,374.33,386.28,398.23,410.18,422.13,434.08,446.02,457.97,469.92,481.87,493.82,505.77,517.72,529.67,541.62],"sy":[466.5,466.51,466.52,466.54,466.56,466.58,466.62,466.67,466.73,466.82,466.93,467.09,467.3,467.57,467.94,468.43,469.09,468.68,467.56,466.93,466.15,465.11,463.71,461.18,457.75,453.15,447.69,447.69,447.69,447.69,447.69,447.69,447.69,447.69,447.69,447.69,447.69,447.69,447.69,447.69]},{"panel":1,"label":"ret_20d (lasso)","color":"var(--chart-grey, #a3a19c)","x":[0.001,0.0013434,0.00180472,0.00242446,0.00325702,0.00437548,0.00587802,0.00789652,0.0106082,0.014251,0.0191448,0.0257191,0.0345511,0.0464159,0.0623551,0.0837678,0.112534,0.151178,0.203092,0.272833,0.366524,0.492388,0.661474,0.888624,1.19378,1.60372,2.15443,2.89427,3.88816,5.22335,7.01704,9.42668,12.6638,17.0125,22.8546,30.7029,41.2463,55.4102,74.438,100.0],"y":[1.15187,1.14857,1.14415,1.1382,1.13018,1.11945,1.10505,1.08561,1.05962,1.02475,0.977715,0.914916,0.830262,0.717633,0.566258,0.36333,0.0900389,0.0,0.0,0.0,-0.0,-0.0,-0.0,-0.0,-0.0,-0.0,-0.0,-0.0,-0.0,-0.0,-0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0],"sx":[75.58,87.53,99.48,111.43,123.38,135.33,147.28,159.23,171.18,183.13,195.08,207.03,218.98,230.93,242.88,254.83,266.78,278.73,290.68,302.63,314.58,326.53,338.48,350.43,362.38,374.33,386.28,398.23,410.18,422.13,434.08,446.02,457.97,469.92,481.87,493.82,505.77,517.72,529.67,541.62],"sy":[431.71,431.76,431.82,431.9,432.01,432.16,432.36,432.63,432.99,433.47,434.13,435.0,436.17,437.73,439.83,442.65,446.44,447.69,447.69,447.69,447.69,447.69,447.69,447.69,447.69,447.69,447.69,447.69,447.69,447.69,447.69,447.69,447.69,447.69,447.69,447.69,447.69,447.69,447.69,447.69]},{"panel":1,"label":"vol_5d (lasso)","color":"var(--chart-purple, #7c5fd3)","x":[0.001,0.0013434,0.00180472,0.00242446,0.00325702,0.00437548,0.00587802,0.00789652,0.0106082,0.014251,0.0191448,0.0257191,0.0345511,0.0464159,0.0623551,0.0837678,0.112534,0.151178,0.203092,0.272833,0.366524,0.492388,0.661474,0.888624,1.19378,1.60372,2.15443,2.89427,3.88816,5.22335,7.01704,9.42668,12.6638,17.0125,22.8546,30.7029,41.2463,55.4102,74.438,100.0],"y":[5.15392,5.15448,5.15523,5.15624,5.1576,5.15942,5.16185,5.16516,5.16956,5.17545,5.18343,5.19401,5.20833,5.22718,5.25253,5.28643,5.33222,5.36375,5.39197,5.37986,5.35982,5.33287,5.2971,5.27452,5.24501,5.20544,5.1549,5.10938,4.63255,3.90134,2.9192,1.58015,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0],"sx":[75.58,87.53,99.48,111.43,123.38,135.33,147.28,159.23,171.18,183.13,195.08,207.03,218.98,230.93,242.88,254.83,266.78,278.73,290.68,302.63,314.58,326.53,338.48,350.43,362.38,374.33,386.28,398.23,410.18,422.13,434.08,446.02,457.97,469.92,481.87,493.82,505.77,517.72,529.67,541.62],"sy":[376.19,376.18,376.17,376.16,376.14,376.12,376.08,376.04,375.98,375.89,375.78,375.64,375.44,375.18,374.82,374.35,373.72,373.28,372.89,373.06,373.34,373.71,374.21,374.52,374.93,375.48,376.18,376.81,383.42,393.57,407.19,425.77,447.69,447.69,447.69,447.69,447.69,447.69,447.69,447.69]},{"panel":1,"label":"vol_20d (lasso)","color":"var(--chart-amber, #c0781a)","x":[0.001,0.0013434,0.00180472,0.00242446,0.00325702,0.00437548,0.00587802,0.00789652,0.0106082,0.014251,0.0191448,0.0257191,0.0345511,0.0464159,0.0623551,0.0837678,0.112534,0.151178,0.203092,0.272833,0.366524,0.492388,0.661474,0.888624,1.19378,1.60372,2.15443,2.89427,3.88816,5.22335,7.01704,9.42668,12.6638,17.0125,22.8546,30.7029,41.2463,55.4102,74.438,100.0],"y":[6.40282,6.40182,6.40049,6.3987,6.39628,6.39304,6.3887,6.38284,6.37501,6.36449,6.35031,6.33137,6.30585,6.27186,6.22619,6.16494,6.08248,6.0067,5.92114,5.86003,5.78206,5.67734,5.53633,5.33423,5.06231,4.69696,4.20951,3.59225,3.00835,2.2775,1.29555,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0],"sx":[75.58,87.53,99.48,111.43,123.38,135.33,147.28,159.23,171.18,183.13,195.08,207.03,218.98,230.93,242.88,254.83,266.78,278.73,290.68,302.63,314.58,326.53,338.48,350.43,362.38,374.33,386.28,398.23,410.18,422.13,434.08,446.02,457.97,469.92,481.87,493.82,505.77,517.72,529.67,541.62],"sy":[358.87,358.88,358.9,358.92,358.96,359.0,359.06,359.14,359.25,359.4,359.6,359.86,360.21,360.68,361.32,362.17,363.31,364.36,365.55,366.4,367.48,368.93,370.89,373.69,377.46,382.53,389.29,397.86,405.96,416.09,429.72,447.69,447.69,447.69,447.69,447.69,447.69,447.69,447.69,447.69]},{"panel":1,"label":"sma_ratio (lasso)","color":"var(--chart-green, #3f8f45)","x":[0.001,0.0013434,0.00180472,0.00242446,0.00325702,0.00437548,0.00587802,0.00789652,0.0106082,0.014251,0.0191448,0.0257191,0.0345511,0.0464159,0.0623551,0.0837678,0.112534,0.151178,0.203092,0.272833,0.366524,0.492388,0.661474,0.888624,1.19378,1.60372,2.15443,2.89427,3.88816,5.22335,7.01704,9.42668,12.6638,17.0125,22.8546,30.7029,41.2463,55.4102,74.438,100.0],"y":[-3.28969,-3.28459,-3.27774,-3.26855,-3.25614,-3.23955,-3.21727,-3.18721,-3.14702,-3.09306,-3.02032,-2.92314,-2.79218,-2.61775,-2.38331,-2.06897,-1.64575,-1.40304,-1.21958,-1.20011,-1.19097,-1.17871,-1.1621,-1.10875,-1.03611,-0.93849,-0.766472,-0.12085,-0.0,-0.0,-0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0],"sx":[75.58,87.53,99.48,111.43,123.38,135.33,147.28,159.23,171.18,183.13,195.08,207.03,218.98,230.93,242.88,254.83,266.78,278.73,290.68,302.63,314.58,326.53,338.48,350.43,362.38,374.33,386.28,398.23,410.18,422.13,434.08,446.02,457.97,469.92,481.87,493.82,505.77,517.72,529.67,541.62],"sy":[493.32,493.25,493.16,493.03,492.86,492.63,492.32,491.9,491.35,490.6,489.59,488.24,486.42,484.0,480.75,476.39,470.52,467.15,464.61,464.34,464.21,464.04,463.81,463.07,462.06,460.71,458.32,449.37,447.69,447.69,447.69,447.69,447.69,447.69,447.69,447.69,447.69,447.69,447.69,447.69]},{"panel":1,"label":"bollinger (lasso)","color":"var(--chart-maroon, #b6262c)","x":[0.001,0.0013434,0.00180472,0.00242446,0.00325702,0.00437548,0.00587802,0.00789652,0.0106082,0.014251,0.0191448,0.0257191,0.0345511,0.0464159,0.0623551,0.0837678,0.112534,0.151178,0.203092,0.272833,0.366524,0.492388,0.661474,0.888624,1.19378,1.60372,2.15443,2.89427,3.88816,5.22335,7.01704,9.42668,12.6638,17.0125,22.8546,30.7029,41.2463,55.4102,74.438,100.0],"y":[1.11357,1.11169,1.10916,1.10576,1.10119,1.09505,1.08681,1.07573,1.06086,1.04087,1.01402,0.977958,0.929506,0.864425,0.776995,0.659545,0.501756,0.294992,0.0198962,0.0,0.0,-0.0,-0.0,-0.0,-0.0,-0.0,-0.0,-0.0,-0.0,-0.0,-0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0],"sx":[75.58,87.53,99.48,111.43,123.38,135.33,147.28,159.23,171.18,183.13,195.08,207.03,218.98,230.93,242.88,254.83,266.78,278.73,290.68,302.63,314.58,326.53,338.48,350.43,362.38,374.33,386.28,398.23,410.18,422.13,434.08,446.02,457.97,469.92,481.87,493.82,505.77,517.72,529.67,541.62],"sy":[432.24,432.27,432.3,432.35,432.41,432.5,432.61,432.77,432.97,433.25,433.62,434.12,434.79,435.7,436.91,438.54,440.73,443.6,447.41,447.69,447.69,447.69,447.69,447.69,447.69,447.69,447.69,447.69,447.69,447.69,447.69,447.69,447.69,447.69,447.69,447.69,447.69,447.69,447.69,447.69]},{"panel":2,"label":"SPY price z-score, 60-day window","color":"var(--chart-maroon, #b6262c)","x":["2019-10-01","2019-10-02","2019-10-03","2019-10-04","2019-10-07","2019-10-08","2019-10-09","2019-10-10","2019-10-11","2019-10-14","2019-10-15","2019-10-16","2019-10-17","2019-10-18","2019-10-21","2019-10-22","2019-10-23","2019-10-24","2019-10-25","2019-10-28","2019-10-29","2019-10-30","2019-10-31","2019-11-01","2019-11-04","2019-11-05","2019-11-06","2019-11-07","2019-11-08","2019-11-11","2019-11-12","2019-11-13","2019-11-14","2019-11-15","2019-11-18","2019-11-19","2019-11-20","2019-11-21","2019-11-22","2019-11-25","2019-11-26","2019-11-27","2019-11-29","2019-12-02","2019-12-03","2019-12-04","2019-12-05","2019-12-06","2019-12-09","2019-12-10","2019-12-11","2019-12-12","2019-12-13","2019-12-16","2019-12-17","2019-12-18","2019-12-19","2019-12-20","2019-12-23","2019-12-24","2019-12-26","2019-12-27","2019-12-30","2019-12-31","2020-01-02","2020-01-03","2020-01-06","2020-01-07","2020-01-08","2020-01-09","2020-01-10","2020-01-13","2020-01-14","2020-01-15","2020-01-16","2020-01-17","2020-01-21","2020-01-22","2020-01-23","2020-01-24","2020-01-27","2020-01-28","2020-01-29","2020-01-30","2020-01-31","2020-02-03","2020-02-04","2020-02-05","2020-02-06","2020-02-07","2020-02-10","2020-02-11","2020-02-12","2020-02-13","2020-02-14","2020-02-18","2020-02-19","2020-02-20","2020-02-21","2020-02-24","2020-02-25","2020-02-26","2020-02-27","2020-02-28","2020-03-02","2020-03-03","2020-03-04","2020-03-05","2020-03-06","2020-03-09","2020-03-10","2020-03-11","2020-03-12","2020-03-13","2020-03-16","2020-03-17","2020-03-18","2020-03-19","2020-03-20","2020-03-23","2020-03-24","2020-03-25","2020-03-26","2020-03-27","2020-03-30","2020-03-31","2020-04-01","2020-04-02","2020-04-03","2020-04-06","2020-04-07","2020-04-08","2020-04-09","2020-04-13","2020-04-14","2020-04-15","2020-04-16","2020-04-17","2020-04-20","2020-04-21","2020-04-22","2020-04-23","2020-04-24","2020-04-27","2020-04-28","2020-04-29","2020-04-30","2020-05-01","2020-05-04","2020-05-05","2020-05-06","2020-05-07","2020-05-08","2020-05-11","2020-05-12","2020-05-13","2020-05-14","2020-05-15","2020-05-18","2020-05-19","2020-05-20","2020-05-21","2020-05-22","2020-05-26","2020-05-27","2020-05-28","2020-05-29","2020-06-01","2020-06-02","2020-06-03","2020-06-04","2020-06-05","2020-06-08","2020-06-09","2020-06-10","2020-06-11","2020-06-12","2020-06-15","2020-06-16","2020-06-17","2020-06-18","2020-06-19","2020-06-22","2020-06-23","2020-06-24","2020-06-25","2020-06-26","2020-06-29","2020-06-30"],"y":[-0.167221,-1.12068,-0.652607,0.102078,-0.120694,-0.960641,-0.413773,-0.0228508,0.571945,0.507191,1.06217,0.971076,1.14721,0.897141,1.2953,1.11257,1.2782,1.34185,1.52145,1.75853,1.69983,1.79825,1.59063,1.9949,2.09141,1.94499,1.86255,1.98412,2.04605,1.86974,1.88324,1.82097,1.8076,2.04525,2.02453,1.93992,1.72052,1.59792,1.64119,1.92488,1.95384,2.05713,1.79817,1.36404,1.03344,1.25701,1.28857,1.61203,1.42334,1.3275,1.39108,1.66629,1.61943,1.80167,1.72985,1.66145,1.73469,1.81009,1.78604,1.71223,1.82437,1.77103,1.53694,1.57042,1.83878,1.54495,1.64994,1.51039,1.66073,1.85871,1.6873,1.87987,1.75768,1.78679,2.02063,2.05786,1.91267,1.85206,1.82874,1.44654,0.816984,1.17741,1.11575,1.20573,0.46334,0.729777,1.30543,1.71656,1.78372,1.51399,1.75342,1.75824,1.93916,1.81691,1.80387,1.639,1.76026,1.54994,1.11373,-0.217176,-1.39247,-1.51553,-2.95655,-2.89971,-1.55056,-2.3909,-1.10655,-2.09653,-2.46022,-3.98081,-2.62244,-3.30694,-4.22534,-2.74054,-3.73058,-2.84123,-3.0654,-2.78463,-2.88679,-2.82167,-2.04542,-1.83869,-1.34886,-1.52082,-1.23402,-1.30073,-1.56887,-1.35698,-1.40425,-0.914906,-0.878516,-0.612288,-0.47786,-0.526611,-0.282919,-0.429709,-0.371506,-0.139451,-0.263243,-0.487441,-0.29283,-0.273479,-0.139587,0.00117912,-0.0216347,0.222489,0.159648,-0.0555789,-0.00542694,0.104954,0.0692838,0.216123,0.417835,0.459595,0.28541,0.130866,0.31328,0.422509,0.870409,0.777762,1.03947,0.968052,0.995969,1.16558,1.39171,1.35436,1.43825,1.48644,1.57907,1.70115,1.60974,1.88173,1.96134,1.77056,1.64202,0.82529,0.95488,1.0545,1.31118,1.261,1.24567,1.13136,1.20013,1.2485,0.783288,0.940473,0.498949,0.751759,1.00631],"sx":[75.58,77.29,79.0,80.71,85.83,87.53,89.24,90.95,92.66,97.78,99.48,101.19,102.9,104.6,109.73,111.43,113.14,114.85,116.55,121.68,123.38,125.09,126.8,128.5,133.63,135.33,137.04,138.75,140.45,145.58,147.28,148.99,150.7,152.4,157.53,159.23,160.94,162.65,164.35,169.47,171.18,172.89,176.3,181.42,183.13,184.84,186.55,188.25,193.37,195.08,196.79,198.5,200.2,205.32,207.03,208.74,210.45,212.15,217.27,218.98,222.39,224.1,229.22,230.93,234.34,236.05,241.17,242.88,244.59,246.29,248.0,253.12,254.83,256.54,258.24,259.95,266.78,268.49,270.19,271.9,277.02,278.73,280.44,282.14,283.85,288.97,290.68,292.39,294.09,295.8,300.92,302.63,304.34,306.04,307.75,314.58,316.29,317.99,319.7,324.82,326.53,328.24,329.94,331.65,336.77,338.48,340.18,341.89,343.6,348.72,350.43,352.13,353.84,355.55,360.67,362.38,364.08,365.79,367.5,372.62,374.33,376.03,377.74,379.45,384.57,386.28,387.98,389.69,391.4,396.52,398.23,399.93,401.64,408.47,410.18,411.88,413.59,415.3,420.42,422.13,423.83,425.54,427.25,432.37,434.08,435.78,437.49,439.2,444.32,446.02,447.73,449.44,451.15,456.27,457.97,459.68,461.39,463.1,468.22,469.92,471.63,473.34,475.05,481.87,483.58,485.29,487.0,492.12,493.82,495.53,497.24,498.94,504.07,505.77,507.48,509.19,510.89,516.02,517.72,519.43,521.14,522.84,527.97,529.67,531.38,533.09,534.79,539.92,541.62],"sy":[729.93,750.26,740.28,724.18,728.93,746.85,735.18,726.85,714.16,715.54,703.71,705.65,701.9,707.23,698.74,702.63,699.1,697.75,693.92,688.86,690.11,688.01,692.44,683.82,681.76,684.88,686.64,684.05,682.73,686.49,686.2,687.53,687.81,682.75,683.19,684.99,689.67,692.28,691.36,685.31,684.69,682.49,688.01,697.27,704.32,699.55,698.88,691.98,696.01,698.05,696.7,690.83,691.83,687.94,689.47,690.93,689.37,687.76,688.27,689.85,687.46,688.59,693.59,692.87,687.15,693.41,691.18,694.15,690.95,686.72,690.38,686.27,688.88,688.26,683.27,682.48,685.57,686.87,687.36,695.51,708.94,701.25,702.57,700.65,716.48,710.8,698.52,689.75,688.32,694.07,688.97,688.87,685.01,687.61,687.89,691.41,688.82,693.31,702.61,730.99,756.05,758.68,789.41,788.2,759.43,777.35,749.96,771.07,778.82,811.25,782.28,796.88,816.46,784.8,805.91,786.95,791.73,785.74,787.92,786.53,769.98,765.57,755.12,758.79,752.68,754.1,759.82,755.3,756.31,745.87,745.09,739.42,736.55,737.59,732.39,735.52,734.28,729.33,731.97,736.75,732.6,732.19,729.34,726.33,726.82,721.62,722.96,727.55,726.48,724.12,724.88,721.75,717.45,716.56,720.27,723.57,719.68,717.35,707.8,709.77,704.19,705.72,705.12,701.5,696.68,697.48,695.69,694.66,692.69,690.08,692.03,686.23,684.53,688.6,691.34,708.76,706.0,703.87,698.4,699.47,699.8,702.23,700.77,699.74,709.66,706.3,715.72,710.33,704.9]},{"panel":2,"label":"z = \u22122","color":"var(--chart-blue, #3b6ea5)","x":["1970-01-01","1970-01-02"],"y":[-2.0,-2.0],"sx":[-30942.42,-30940.71],"sy":[769.01,769.01]}]} 2026-10-15T23:57:22.429867 image/svg+xml Matplotlib v3.11.2, https://matplotlib.org/ seven honest features plus SPY's same-month volatility (leak) the leak, lagged 20 days plus SPY's last-month volatility (honest) Feature set 6.00 6.25 6.50 6.75 7.00 7.25 7.50 7.75 8.00 Test RMSE (volatility points) 7.50 6.81 7.55 7.56 1. The leak test: lag the suspicious feature and watch the score 1 0 − 3 1 0 − 2 1 0 − 1 1 0 0 1 0 1 1 0 2 Penalty strength alpha (log scale) −2 0 2 4 6 Standardised coefficient 2. Ridge shrinks every coefficient toward zero; lasso drives some exactly to zero ret_1d (lasso) ret_5d (lasso) ret_20d (lasso) vol_5d (lasso) vol_20d (lasso) sma_ratio (lasso) bollinger (lasso) dotted: the same feature under ridge 2019-10 2019-11 2019-12 2020-01 2020-02 2020-03 2020-04 2020-05 2020-06 2020-07 Date −4 −3 −2 −1 0 1 2 z-score 3. A rolling z-score hides a crash: the window's spread balloons with the fall SPY price z-score, 60-day window z = −2

How to catch it. Lag the suspicious feature by one period and rerun. The leaked column shifted back by twenty trading days scores 7.55: the “signal” vanished, which means it was never a signal about the future, only a copy of it. Removing the feature is the same test. Run either on any result that looks too good.

Leakage comes in two shapes. Time leakage uses the future: a same-month statistic, a revised figure, a label that overlaps the test period. Pooling leakage uses the test rows: a mean, a scaler or a feature-selection step computed over the whole table. Regularisation does not stop either, and a correct split cannot repair a feature that was built looking forward.

Scaling: units, not information

Some models measure distances between rows (nearest neighbours), or penalise the size of coefficients (ridge, lasso, logistic regression). Both depend on the units of each feature, so features are standardised: each column rescaled to a mean of zero and a spread of one. Scaling changes a feature’s influence, not its information. Trees do not need it, because a split only asks whether a value is above or below a threshold.

The rule that matters is where the scaler is fitted. Fit it on the training rows and apply it to the test rows. Fitted on all rows together, it carries the test set’s mean into the model: in this data the pooled mean of 20-day volatility is 23.84 points and the training-only mean is 24.30, because the pooled version already knows 2022 was calmer than 2020. The difference is small here; the habit is the point, and scikit-learn’s Pipeline exists so that the habit is automatic.

Twin features: ridge shares, lasso chooses

Add a column that is 1.02 times the 20-day volatility plus a hair of noise, a twin of a feature already there. Fit three models:

ModelCoefficient on vol_20dOn its twinTheir sumTest RMSE
Ordinary least squares−33.89\risk{-33.89}+40.31\risk{+40.31}+6.417.50
Ridge, penalty 1−1.37+7.78+6.417.50
Lasso, penalty 0.050.00\key{0.00}+6.27+6.277.51

Least squares gives the twins wildly opposite weights that cancel; the predictions are fine, the coefficients are meaningless, and a tiny change in the data would swap them. Ridge pulls both towards zero and shares the weight. Lasso drops one exactly. The sum is stable in every row; the split between the twins is not. Correlated features break the interpretation of a linear model before they break its predictions, and in a random forest they split the importance score between them without breaking anything.

The two penalties:

ItemRidge (L2)Lasso (L1)
Adds to the lossλ∑βj2\lambda \sum \key{\beta_j^2}λ∑∣βj∣\lambda \sum \lvert \key{\beta_j} \rvert
Effect as λ\lambda growsevery coefficient shrinks, none reaches zerosome reach exactly zero
With twinsshares the weightpicks one
Use whenmany small effectsa few real effects among many candidates

The second panel of the chart draws both paths on the seven honest features as the penalty grows: lasso’s lines hit zero one by one (seven features left at a penalty of 0.1, four at 1), ridge’s lines approach zero and never arrive. The penalty is chosen on validation data. At zero it is plain least squares; large enough, it predicts the mean.

A rolling z-score hides a crash

A common feature is a rolling z-score: how far today’s price sits from its recent average, in recent standard deviations. It has a flaw that shows up exactly when it matters.

Window meanWindow sdPricez
10021063.0
100101060.6

The same six-point gap reads as extreme in a calm window and as nothing in a wild one, because the wild window’s standard deviation has ballooned. SPY in 2020: a week into the fall, on 28 February, the 60-day z-score read −2.90. At the bottom on 23 March, with the price 79 points below the window’s mean, it read −2.82, because the window’s standard deviation had tripled from 8.6 to 28.1. The third panel of the chart shows the z-score barely moving through the worst month in a decade. A feature built to flag extremes went quiet in the biggest one.

Traps

TrapTruth
Leakage is only about timePooling the test rows leaks too
Future returns as the label are leakageThat is the target; leakage is about the inputs
Trees need scalingNo
Correlated features break a random forestThey dilute its importance scores; they break least-squares coefficients
Lasso picks the truly important twinIt picks one, arbitrarily
Fewer features lowers biasIt raises bias and lowers variance
A z-score is capped at ±3It is not, and in a crash it shrinks

The code

from sklearn.linear_model import Lasso, LinearRegression, Ridge
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler

# the leak test: lag the suspect by one period and see whether the score moves
X_lagged = X.copy(); X_lagged["suspect"] = X_lagged.groupby("ticker")["suspect"].shift(20)

# scaling inside a pipeline: the scaler is fitted on the training rows only
ridge = make_pipeline(StandardScaler(), Ridge(alpha=1.0)).fit(X_train, y_train)
lasso = make_pipeline(StandardScaler(), Lasso(alpha=0.05)).fit(X_train, y_train)
lasso[-1].coef_                                          # some entries exactly 0.0

z = (price - price.rolling(60).mean()) / price.rolling(60).std()   # the rolling z-score

Summary

IdeaRuleRemember it as
Timestampwhen was this knowable?lag everything to the last close
The leak testlag or remove the suspect, reruna real signal survives a lag
Scalingstandardise, fitted on training rowsunits, not information
Ridgeλ∑β2\lambda \sum \beta^2shrinks all, shares twins
Lassoλ∑∣β∣\lambda \sum \lvert \beta \rvertzeroes some, picks a twin
Rolling z-score(price − mean) / sdgoes quiet in a crash

Next

Decision trees: a model that needs no scaling, ignores the features it does not use, and finds thresholds on its own.

References