Quant Memo
Core

Sample Weights and Recency Decay

Telling a model that some training examples matter more than others — typically that recent data should count more than old data — by attaching a weight to each row instead of treating every historical observation as equally informative.

Prerequisites: Label Construction and Prediction Horizon

Most model-fitting routines silently assume every training row is equally trustworthy and equally relevant — a trade from five years ago, back when the market's microstructure and volatility regime were completely different, counts exactly as much as a trade from last week. That assumption is rarely true in markets, where regimes shift, spreads compress, and relationships between features and returns can decay or flip entirely. Sample weighting is the tool for saying, explicitly, "trust this row more" or "trust this row less" without throwing old data away entirely.

The idea: attach a weight to every row, not just a label

Instead of an unweighted loss that treats every observation the same, a weighted loss multiplies each row's contribution to the loss function by a weight wiw_i:

wi=eλ(Tti)w_i = e^{-\lambda (T - t_i)}

In plain English: for a row observed at time tit_i, with TT the most recent time in the dataset, the weight decays exponentially the further back in time the observation sits, controlled by a decay rate λ\lambda. A large λ\lambda makes old data almost worthless to the fit very quickly; a small λ\lambda keeps most history nearly as influential as the newest rows. Setting λ=0\lambda = 0 recovers ordinary unweighted training. Weights need not only encode recency — they can also downweight periods of unusually thin liquidity, or upweight rare-but-important regime types, but recency decay is the most common financial use case.

Worked example: half-life weighting on five years of data

Suppose a modeler wants observations to lose half their weight every 2 years — a half-life of 2 years. The decay rate satisfying eλ2=0.5e^{-\lambda \cdot 2} = 0.5 is λ=ln(2)/20.347\lambda = \ln(2)/2 \approx 0.347 per year. A row from exactly today (Tti=0T - t_i = 0) gets weight e0=1e^0 = 1. A row from 2 years ago gets weight e0.347×2=0.5e^{-0.347 \times 2} = 0.5, exactly half. A row from 5 years ago gets weight e0.347×50.177e^{-0.347 \times 5} \approx 0.177 — under a fifth of a fresh observation's influence on the fit, even though it's still included in the training set rather than being hard-cut and discarded entirely. This is the practical middle ground between "use all history equally" and "only use last month's data": all the history is present, but its influence fades smoothly.

1.0 0 t=0 2 yrs (w=0.5) 5 yrs (w≈0.18)
With a 2-year half-life, a row's weight halves every two years further into the past — old data is never discarded outright, just steadily discounted.

What this means in practice

Recency-weighted training is a common way to keep a model responsive to regime change without the instability of retraining only on a short recent window, which can starve the model of enough data to fit reliably. The decay rate itself becomes a hyperparameter to tune, usually via time-series cross-validation, trading off responsiveness to new regimes against noise from too small an effective sample size. It's easy to double-count recency, though: if the label horizon or feature construction already implicitly favors recent behavior (for example, a feature that's itself a recent rolling average), stacking an aggressive weight decay on top can overcorrect and starve the model of the older regimes it still needs to generalize across.

Sample weights let recent — or otherwise more trustworthy — observations count more in model fitting than older or noisier ones, most commonly via exponential recency decay controlled by a half-life, without discarding any historical data outright.

Related concepts

Practice in interviews

Further reading

  • López de Prado, Advances in Financial Machine Learning, ch. 4
ShareTwitterLinkedIn