If you learn one formula by heart for quant interviews, make it this one. Linear regression is the workhorse of quant research: factor models, hedge ratios, signal testing and most first-pass alpha models are regressions.
The closed form
Stack the observations into a matrix (, with a column of ones for the intercept) and a vector . Minimising means setting its gradient to zero:
The fitted values are . The hat matrix “puts the hat on ”: it is the orthogonal projection of onto the column space of . Least squares finds the point in the space your features can reach that is closest to the data, and the residual is perpendicular to every feature.
With one input, the slope and intercept are just covariances:
When isn’t invertible
If one column is an exact linear combination of others (say , or a full set of dummy variables plus an intercept), is singular. The fitted values are still the unique projection; only the coefficients stop being unique, because there is more than one way to write the same projection.
Is a coefficient real? The t-test
Estimate the noise variance with degrees of freedom (one is used up by each coefficient and the intercept):
If errors are uncorrelated with constant variance, . To test , standardise:
which follows a distribution under the null. is the usual rule of thumb for “significant”, and is an approximate 95% interval (1.96 is the exact normal quantile).
For a single input, : precision improves with more data, less noise, and more spread in the input.
Testing groups: the F-statistic
Three correlated features can each look insignificant while together they matter. Compare the full model (RSS₁, coefficients) with one that drops the group (RSS₀, ):
A large says the dropped group carried real information. A typical workflow: run t-tests, collect everything with , then F-test that group before deleting it.
Multiple regression is many simple ones
Regression by successive orthogonalisation (Gram–Schmidt) shows what a multiple-regression coefficient means. Regress on all the other inputs and keep the residual , the part of nothing else explains. Then
So is the effect of after adjusting for every other input. Econometricians call this regression anatomy (the Frisch–Waugh–Lovell theorem). It also explains multicollinearity: if is almost explained by the others, is tiny, is small and explodes.
The brainteasers interviewers ask
What does linear regression assume?
- The relationship is linear in the parameters (you can still use or as features).
- Errors average zero given the inputs, so there is no systematic bias. Normal errors are convenient for exact t-tests but not required.
- No perfect multicollinearity, and ideally not much of the imperfect kind.
- Homoskedasticity: the error variance doesn’t depend on the inputs. If it does, use heteroskedasticity-robust standard errors.
- Errors are uncorrelated with each other (no autocorrelation) and with the inputs.
Regress Y on X, then X on Y. Is β₁ = 1/β₂?
No. The first minimises vertical distances, the second horizontal ones. In fact , so they are reciprocals only if the fit is perfect. Turn on “Also regress x on y” in the figure.
You duplicate every data point. What happens?
doesn’t change ( and both double, and the factors of two cancel). But the standard errors shrink by about , so t-statistics grow. Duplicated or overlapping observations fake significance, a real danger with overlapping return windows.
Three highly correlated features: two look fine and the third has a huge standard error. What do you do?
That is multicollinearity. Options: drop the third (but it may carry unique information), combine the features (an average or a principal component), centre or transform them, or switch to ridge, lasso or principal-components regression (next two lessons).
In code
import numpy as np
rng = np.random.default_rng(8)
N, p = 200, 3
X = np.c_[np.ones(N), rng.standard_normal((N, p))]
beta_true = np.array([1.0, 2.0, 0.0, -1.0])
y = X @ beta_true + rng.standard_normal(N)
beta = np.linalg.solve(X.T @ X, X.T @ y) # (X'X)^-1 X'y, solved stably
resid = y - X @ beta
sigma2 = resid @ resid / (N - p - 1)
se = np.sqrt(np.diag(np.linalg.inv(X.T @ X)) * sigma2)
print(np.round(beta, 3))
print(np.round(beta / se, 1)) # t-stats: the zero coefficient has |t| < 2
# Duplicating the data: same beta, smaller standard errors
X2, y2 = np.r_[X, X], np.r_[y, y]
print(np.allclose(np.linalg.solve(X2.T @ X2, X2.T @ y2), beta)) # True
Check yourself
In a regression of y on x₁ and x₂, x₂ is almost perfectly correlated with x₁. What happens to se(β₁)?