QTQuant Terminal
D1-06D1·intermediate·~21 min

Model selection and forecasting — the humbling benchmark

time-seriesmodel-selectionaicbicforecastingnaive-benchmarkoverfitting

▸ Pretest — guess, even if you don't know

You fit AR(1), AR(2), ... up to AR(10) to the same daily return series. Which model will have the lowest in-sample mean squared error?

The overfitting ratchet

AR(2) contains AR(1) as a special case (set the second coefficient to zero). So the best-fitting AR(2) is at least as good in-sample as the best AR(1) — and with noisy data, strictly better, because the extra coefficient bends toward whatever noise pattern this particular sample happens to contain. Same for AR(3) over AR(2), and for every ARMA extension from D1-05.

This is the time-series face of the disease you met in A2-05 and D4-01: maximizing in-sample fit selects for luck. Fit a rich enough model and you can reproduce history exactly — and forecast nothing. So we need scorekeeping that either charges for parameters or hides data from the fit.

AIC and BIC — paying for parameters

Information criteria charge for parameters directly. For a model with kk parameters and maximized likelihood LL fit on nn points:

AIC=2k2lnLBIC=klnn2lnL\text{AIC} = 2k - 2\ln L \qquad\qquad \text{BIC} = k\ln n - 2\ln L

Plain reading: the 2lnL-2\ln L term rewards fit (it falls as fit improves), the first term is the fine per parameter, and lower is better for both. The difference is the fine schedule: AIC charges a flat 2 per parameter; BIC charges lnn\ln n, which is already ~5.5 at 250 daily observations and grows with the sample. So BIC penalizes harder and picks smaller models. For daily financial data, where true signals are faint and extra parameters mostly harvest noise, the stingier BIC is usually the safer default. Statsmodels prints both for any ARMA fit; you just have to resist the urge to ignore them.

Information criteria are cheap and use all the data — but they still score in-sample likelihood, just with a handicap. The sterner test hides data.

Splitting time: train/test without shuffling

The machine-learning reflex is to shuffle the data and split randomly. For time series this is forbidden. Shuffling puts 2023 observations in the training set for "predicting" 2019 — look-ahead bias (D4-01) manufactured by your own evaluation harness. Autocorrelation makes it worse: a test point randomly sandwiched between two training points is partly memorized, not forecast.

The rule: temporal order is sacred. Train on the first chunk, test on the later chunk, and let information flow only forward:

split = int(len(x) * 0.7)
train, test = x[:split], x[split:]   # fit on train ONLY, forecast test

One split gives one verdict; rolling the split forward gives walk-forward analysis, the full treatment in Track D4.

The naive benchmark

Score forecasts on the test set with mean squared error or mean absolute error:

MSE=1nt(xtx^t)2MAE=1ntxtx^t\text{MSE} = \frac{1}{n}\sum_t (x_t - \hat{x}_t)^2 \qquad \text{MAE} = \frac{1}{n}\sum_t |x_t - \hat{x}_t|

MSE punishes big misses quadratically; MAE is more forgiving of outliers. But a raw error number means nothing alone — you need a baseline, and time series has a brutally effective one: the naive forecast, tomorrow equals today (x^t+1=xt\hat{x}_{t+1} = x_t). For a random walk (D1-02), the naive forecast is optimal. So "beat naive out-of-sample" is the minimum bar for claiming your model found structure — and for prices, most models fail it. For returns, the analogous humbling benchmark is the constant forecast (tomorrow's return equals the historical mean, or just zero): decades of academic horse races show sophisticated return forecasts struggling to beat even that.

The honest scoreboard

Where do linear time-series models actually land, out-of-sample, on financial data?

That asymmetry is the doorway to the rest of this track: the next two lessons are about the forecasts that actually work.

Try it

Race the two classic baselines. The naive forecast predicts each point with the previous point; the mean forecast predicts every point with the training mean (here: the mean of all points before the last step, a miniature train/test split):

▮ EXERCISE · d1-06-ex1

Implement naive_mse(x): the MSE of predicting x[t] with x[t-1], i.e. mean of (x[1:] - x[:-1]) squared. And mean_mse(x): the MSE of predicting every x[t] for t >= 1 with the mean of x[:-1]. Both return floats.

⧉ Review card
Why can in-sample fit never choose between nested models?
A bigger nested model can never fit worse in-sample — extra parameters always absorb at least some noise. In-sample error is a ratchet that only rewards complexity, so it selects for luck, not structure.
⧉ Review card
AIC vs BIC — formulas in words, and which penalizes harder?
Both are fit-reward minus parameter-fine, lower is better. AIC fines 2 per parameter; BIC fines ln(n) per parameter, which exceeds 2 once n is above about 8. BIC penalizes harder and picks smaller models — usually the safer default for noisy financial data.
⧉ Review card
Why must you never shuffle a time-series train/test split?
Shuffling puts future observations in the training set — look-ahead bias built into your own evaluation. Temporal order is sacred: train on the earlier chunk, test on the later one, information flows forward only.
⧉ Review card
What is the naive forecast and why is it the benchmark?
Tomorrow equals today. For a random walk it is the optimal forecast, so beating it out-of-sample is the minimum evidence of real structure. Most return forecasts fail this bar; volatility forecasts routinely clear it.
⧉ Review card
Where do linear time-series models honestly succeed and fail in markets?
Fail: forecasting returns — autocorrelations near 0.01-0.05 leave almost nothing to predict, and costs eat the rest. Succeed: forecasting volatility — squared returns are strongly autocorrelated, so risk is genuinely forecastable.

Predict before the next lesson

Next up is D1-07: volatility, ARCH and GARCH — the models built for the one thing that is forecastable. Before reading, predict:

◈ Calibration check

Could you explain to a colleague why AR(10) always beats AR(1) in-sample, how BIC arbitrates, and why the naive forecast is the bar to clear?

1 = guessing · 5 = could teach it

⏻ End of lesson

Mark it read to book its 5 review cards into your deck.

Sources & further reading