Regression Analysis for Financial Relationships › Diagnosing Regression Problems

Michael BrenndoerferDecember 11, 20259 min read
Quantitative FinanceAll chapters
Part 8 of 15Econometrics and Statistical Modeling
Chapter 43Regression Analysis for Financial Relationships › Diagnosing Regression Problems
Reading section 5 of 12 · Diagnosing Regression Problems · 9 minSection 5 of 12 · 9 min

Book contents

Section 5 of 12 · 9 min read

Regression Analysis for Financial Relationships › Diagnosing Regression ProblemsOne section · Sections in this chapter5 / 12 ↓

The validity of OLS inference depends on several assumptions about the error term. With financial time series data, these assumptions are frequently violated, leading to incorrect standard errors and misleading hypothesis tests. Understanding these assumptions and knowing how to detect their violations is essential for producing reliable results. Failing to address these issues can lead to false confidence in spurious relationships or obscure effects that are present.

The Classical Assumptions

The Gauss-Markov assumptions for OLS include:

  • Linearity: The relationship between yy and xx is linear
  • Exogeneity: E[εi∣xi]=0E[\varepsilon_i | x_i] = 0 (errors are uncorrelated with explanatory variables)
  • Homoskedasticity: Var(εi)=σ2\text{Var}(\varepsilon_i) = \sigma^2 (constant error variance)
  • No autocorrelation: Cov(εi,εj)=0\text{Cov}(\varepsilon_i, \varepsilon_j) = 0 for i≠ji \neq j (errors are uncorrelated across observations)
  • Normality: εi∼N(0,σ2)\varepsilon_i \sim N(0, \sigma^2) (required for exact inference in small samples)

When these assumptions hold, OLS is the Best Linear Unbiased Estimator (BLUE); no other linear unbiased estimator has smaller variance. This optimality result justifies the widespread use of OLS. However, when assumptions are violated, the BLUE property fails, and either the estimates themselves become biased or the standard errors become unreliable.

Heteroskedasticity

Heteroskedasticity occurs when the error variance changes across observations. In finance, this is common: volatility varies over time, as we explored in Chapter 18 on GARCH models. Periods of market stress have higher return volatility than calm periods. The variance of returns during the 2008 financial crisis was many times higher than during the calm period of 2005-2006. This time-varying volatility directly translates into heteroskedastic errors in regression models.

Heteroskedasticity doesn't bias the coefficient estimates, but it makes the standard errors incorrect, invalidating hypothesis tests. The OLS formula for standard errors assumes constant variance, so when variance varies, these standard errors are wrong. They might be too small (leading to false positives) or too large (leading to false negatives), depending on the pattern of heteroskedasticity.

In[16]:
Code
# Create data with heteroskedasticity (volatility increases with market magnitude)
np.random.seed(123)
market_het = np.random.normal(0.0004, 0.012, 500)

# Error variance increases with absolute market return
heterosked_errors = np.random.normal(0, 0.01 + 0.5 * np.abs(market_het), 500)
stock_het = 0.0001 + 1.2 * market_het + heterosked_errors

# Fit regression
X_het = sm.add_constant(market_het)
model_het = sm.OLS(stock_het, X_het).fit()
residuals_het = model_het.resid

The Breusch-Pagan test formally tests for heteroskedasticity by regressing squared residuals on the explanatory variables:

In[17]:
Code
from statsmodels.stats.diagnostic import het_breuschpagan

# Breusch-Pagan test
bp_stat, bp_pval, _, _ = het_breuschpagan(model_het.resid, model_het.model.exog)
Out[18]:
Console
Breusch-Pagan Test for Heteroskedasticity:
  Test statistic: 5.5053
  p-value:        0.0190

The low p-value indicates we reject the null hypothesis of homoskedasticity, confirming the presence of time-varying volatility in our synthetic data.

Let's visualize heteroskedasticity by plotting residuals against fitted values:

Out[19]:
Visualization
Scatter plot of residuals versus fitted values showing a funnel-shaped pattern, indicating heteroskedasticity with increasing variance.
Residuals versus fitted values for heteroskedastic data. The funnel shape shows variance increasing with the magnitude of fitted values.
Scatter plot of residuals versus fitted values showing uniform spread around zero, indicating constant error variance.
Residuals versus fitted values for homoskedastic data. The consistent spread around zero indicates constant error variance.

Autocorrelation

Autocorrelation occurs when errors are correlated across time periods. This is particularly problematic in financial time series because returns often exhibit short-term momentum or mean-reversion patterns in their volatility. When today's error is correlated with yesterday's error, the observations are not truly independent. This reduces the effective sample size: 500 correlated observations contain less information than 500 independent observations.

The Durbin-Watson statistic tests for first-order autocorrelation:

DW=∑t=2n(ε^t−ε^t−1)2∑t=1nε^t2DW = \frac{\sum_{t=2}^{n}(\hat{\varepsilon}_t - \hat{\varepsilon}_{t-1})^2}{\sum_{t=1}^{n}\hat{\varepsilon}_t^2}

where:

  • DWDW: Durbin-Watson statistic
  • ε^t\hat{\varepsilon}_t: residual at time tt
  • nn: number of observations

The numerator sums squared differences between adjacent residuals. If errors are positively correlated, ε^t≈ε^t−1\hat{\varepsilon}_t \approx \hat{\varepsilon}_{t-1}, making the differences small and DWDW low (closer to 0). If errors are uncorrelated, the differences are as large as the errors themselves, and the expected value is 2. If errors are negatively correlated, consecutive residuals tend to have opposite signs, making the differences large and DWDW high (closer to 4).

Values near 2 indicate no autocorrelation, values below 2 indicate positive autocorrelation, and values above 2 indicate negative autocorrelation.

In[20]:
Code
from statsmodels.stats.stattools import durbin_watson

# Generate data with autocorrelated errors
np.random.seed(456)
market_ac = np.random.normal(0.0004, 0.012, 500)

# Create AR(1) errors with persistence parameter 0.3
ar_coef = 0.3
errors_ac = np.zeros(500)
errors_ac[0] = np.random.normal(0, 0.015)
for t in range(1, 500):
    errors_ac[t] = ar_coef * errors_ac[t - 1] + np.random.normal(0, 0.015)

true_beta_ac = 1.2
stock_ac = 0.0001 + true_beta_ac * market_ac + errors_ac

# Fit regression
X_ac = sm.add_constant(market_ac)
model_ac = sm.OLS(stock_ac, X_ac).fit()

# Durbin-Watson test
dw_stat = durbin_watson(model_ac.resid)
dw_original = durbin_watson(model.resid)
Out[21]:
Console
Durbin-Watson Test for Autocorrelation:

Autocorrelated data:
  DW statistic: 1.4305

Original data (no autocorrelation):
  DW statistic: 1.9979

The Durbin-Watson statistic differs significantly from 2 in the autocorrelated sample, signaling the presence of serial correlation.

We can also examine the autocorrelation function of the residuals:

Out[22]:
Visualization
Residual ACF stem plot through lag 20 with a lag-one correlation near 0.28 extending above the shaded 95% confidence band.
Autocorrelation function (ACF) of residuals with serial correlation. The lag-one spike lies clearly outside the 95% confidence band, showing that adjacent errors are positively related.
Residual ACF stem plot through lag 20 with every nonzero-lag correlation inside the shaded 95% confidence band.
Autocorrelation function (ACF) of well-behaved residuals. Correlations fall within the shaded confidence bands, indicating independence.

Multicollinearity

Multicollinearity arises when explanatory variables are highly correlated with each other. While not strictly a violation of OLS assumptions, it inflates standard errors and makes coefficient estimates unstable. The fundamental problem is identification: when two variables move together, it becomes difficult to determine which one is driving the effect. Mathematically, the matrix X′X\mathbf{X}'\mathbf{X} approaches singularity, making its inverse unstable.

The Variance Inflation Factor (VIF) quantifies multicollinearity:

VIFj=11−Rj2VIF_j = \frac{1}{1 - R_j^2}

where:

  • VIFjVIF_j: Variance Inflation Factor for variable jj
  • Rj2R_j^2: R-squared from regressing variable jj on all other explanatory variables

If variable jj is highly correlated with other predictors, Rj2R_j^2 approaches 1, causing the denominator to approach zero and VIFjVIF_j to become very large. This mathematically quantifies how much the variance of the estimated coefficient is inflated due to collinearity. A VIF of 5 means the variance is five times what it would be if the variable were uncorrelated with other predictors, making the standard error about 2.2 times larger.

A VIF above 5 or 10 suggests problematic multicollinearity.

In[23]:
Code
from statsmodels.stats.outliers_influence import variance_inflation_factor

# Create correlated factors (simulating market, size, and value factors)
np.random.seed(789)
n = 500

# Market factor
factor_market = np.random.normal(0.0004, 0.012, n)

# Size factor (moderately correlated with market)
factor_size = 0.5 * factor_market + np.random.normal(0, 0.008, n)

# Value factor (slightly correlated with both)
factor_value = (
    0.3 * factor_market + 0.2 * factor_size + np.random.normal(0, 0.006, n)
)

# Create DataFrame
factors_df = pd.DataFrame(
    {"market": factor_market, "size": factor_size, "value": factor_value}
)

# Add constant
X_multi = sm.add_constant(factors_df)

# Calculate VIF for each variable
vif_data = pd.DataFrame()
vif_data["Variable"] = X_multi.columns
vif_data["VIF"] = [
    variance_inflation_factor(X_multi.values, i)
    for i in range(X_multi.shape[1])
]
Out[24]:
Console
Variance Inflation Factors:
------------------------------
  market    : 2.02
  size      : 1.63
  value     : 1.82

Correlation matrix of factors:
        market   size  value
market   1.000  0.593  0.646
size     0.593  1.000  0.528
value    0.646  0.528  1.000

The VIF values quantify the inflation in variance due to correlation among predictors. Values above 5 would suggest problematic multicollinearity requiring attention.

Out[25]:
Visualization
Annotated three-by-three correlation heatmap for market, size, and value factors. Off-diagonal correlations are 0.59 for market-size, 0.65 for market-value, and 0.53 for size-value.
Correlation heatmap for the synthetic market, size, and value factors. Pairwise correlations range from 0.53 to 0.65, indicating moderate co-movement without the near-linear dependence associated with severe multicollinearity.