Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

3. The Fama–French Three-Factor Model

Question: Can market, size and value exposures explain why diversified equity funds behave differently?

We now combine the regression algebra of Chapter 1 with the time-series reasoning of Chapter 2. SPY, IWN and VTV provide contrasting US equity portfolios: a broad large-cap benchmark, small-cap value, and large-cap value. We construct the factor definitions, align returns, estimate coefficients by hand and in Python, and interpret uncertainty and residuals.

By the end you should be able to explain an estimated factor loading, distinguish alpha from realised outperformance, and say what a time-series regression does—and does not—establish about expected returns across assets.

Prerequisites: OLS, standard errors and the diagnostics in Chapter 2. The code uses complete monthly USD returns; sample dates are printed below and conventions are collected in Data and methods.

Previous: Financial time series · Next: Beyond three factors

Source
All libraries imported successfully!

Section 1: Understanding the Fama-French 3-Factor Model

The Theory Behind Factor Models

The Fama-French 3-Factor Model is a framework developed by economists Eugene Fama and Kenneth French to explain stock returns. It extends the simpler Capital Asset Pricing Model (CAPM) by including two additional factors beyond just the market risk premium.

The model answers this question: Why do some stocks or portfolios outperform others?

The Mathematical Model

Ri(t)−Rf(t)=αi+βMKT[Rm(t)−Rf(t)]+βSMB⋅SMB(t)+βHML⋅HML(t)+ϵi(t)R_i(t) - R_f(t) = \alpha_i + \beta_{MKT} \left[R_m(t) - R_f(t)\right] + \beta_{SMB} \cdot SMB(t) + \beta_{HML} \cdot HML(t) + \epsilon_i(t)

Where:

  • Ri(t)R_i(t) = Return of the asset (stock, ETF, or portfolio) at time tt

  • Rf(t)R_f(t) = Risk-free rate (typically the 1-month U.S. Treasury bill rate)

  • Rm(t)R_m(t) = Return of the overall market portfolio

  • α\alpha = Alpha (Jensen’s alpha): The model-relative return not explained by the three factors. A positive alpha suggests the asset outperformed this specific model, but it can also reflect omitted risk factors, misspecification, or mispricing.

  • βMKT\beta_{MKT} = Market Beta: Sensitivity to market risk. A beta of 1 means the asset moves with the market; >1 means stronger conditional market sensitivity; total volatility also depends on other exposures and residual risk.

  • βSMB\beta_{SMB} = SMB Beta: Sensitivity to the small-minus-big factor, which captures the size effect (small firms tend to outperform large firms).

  • βHML\beta_{HML} = HML Beta: Sensitivity to the high-minus-low factor, which captures the value effect (value stocks tend to outperform growth stocks).

  • SMB(t)SMB(t) = Small Minus Big factor: The return difference between portfolios of small-cap and large-cap stocks

  • HML(t)HML(t) = High Minus Low factor: The return difference between portfolios of high book-to-market (value) and low book-to-market (growth) stocks

  • ϵi(t)\epsilon_i(t) = Error term (random, unexplained variation)

From CAPM to multiple sources of variation

CAPM links expected excess return to market beta. Fama and French’s empirical work adds size- and book-to-market-sorted portfolios to describe common variation and differences in average stock returns. A risk-based interpretation treats the additional factors as proxies for priced risks; behavioural and mispricing explanations offer alternatives. Regression fit alone does not choose between these mechanisms. Factor definitions and the original research.

Three questions that require different evidence

  1. Exposure: in the time-series regression, does a fund covary with HML after controlling for the other factors? This concerns βHML\beta_{HML}.

  2. Premium: is the expected HML return positive? This concerns E[HML]E[HML], estimated with considerable uncertainty from its historical mean.

  3. Pricing: do exposures account for differences in expected returns across many assets? For traded factors, a zero-alpha pricing restriction is E[Ri−Rf]=βi⊤E[f]E[R_i-R_f]=\boldsymbol\beta_i^\top E[\mathbf f]. Examining that restriction requires evidence across assets and joint pricing errors, not just one high R2R^2.

In this chapter we estimate time-series exposures and model-relative intercepts. With an intercept, OLS gives the sample identity Rˉie=α^i+β^i⊤fˉ\bar R_i^e=\hat\alpha_i+\hat{\boldsymbol\beta}_i^\top\bar{\mathbf f}. That arithmetic is useful attribution, but it is not an independent test that the estimated factor means will persist. Cross-sectional pricing tests are a further step beyond the ETF regressions developed here.

What We’ll Do (Step-by-Step)

In this tutorial, we will:

  1. Obtain historical data on ETF returns and the three factors

  2. Run a regression to estimate α\alpha, βMKT\beta_{MKT}, βSMB\beta_{SMB}, and βHML\beta_{HML}

  3. Test the statistical significance of these coefficients

  4. Interpret what they tell us about the ETF’s factor exposures


Section 2: Load and Explore the Data

We’ll use real, actual market data from two sources:

  1. Fama-French Factors: Downloaded from Kenneth French’s official data library at https://mba.tuck.dartmouth.edu/pages/faculty/ken.french/data_library.html

    • Market risk premium (Mkt-RF)

    • Size effect factor (SMB)

    • Value effect factor (HML)

    • Risk-free rate (RF)

  2. ETF Returns: Downloaded from Yahoo Finance using real historical prices for three US-listed ETFs with very different expected factor exposures:

    • SPY (SPDR S&P 500 ETF Trust) — a broad US market ETF tracking the S&P 500. We expect βMKT≈1\beta_{MKT} \approx 1 and low SMB/HML loadings.

    • IWN (iShares Russell 2000 Value ETF) — targets US small-cap value stocks. We expect positive SMB and HML loadings.

    • VTV (Vanguard Value ETF) — targets US large-cap value stocks. We expect a positive HML loading but near-zero or negative SMB loading.

Why These Three ETFs?

These ETFs are chosen deliberately because they let us see the Fama-French factors at work:

  • A broad US market ETF should be mostly explained by the market factor alone, with βMKT≈1\beta_{MKT} \approx 1.

  • A US small-cap value ETF should load positively on both SMB and HML, indicating exposure to the size and value spread portfolios; the signs do not establish a future premium.

  • A US large-cap value ETF lets us isolate the value effect—it should load on HML but not on SMB.

Why these US equity funds? Their underlying holdings are US equities and their measured returns are in USD, matching the universe and currency of the US factors. A US listing alone would not ensure that match: a US-listed international ETF requires an appropriate international comparison. Chapter 5 repeats this reasoning for developed and emerging markets.

How Are the Fama-French Factors Constructed?

This is an important question for understanding where empirical factor data comes from. The construction is actually quite clever:

Market Risk Premium (Mkt-RF)

  • Construct a value-weighted portfolio of all stocks in the market

  • Calculate its monthly return

  • Subtract the risk-free rate (typically 1-month Treasury bill yield)

  • Result: The excess return investors earn for taking market risk

SMB and HML use six intersecting portfolios, not independent single sorts. At each June formation date, split eligible stocks into Small/Big using the NYSE median market equity, and independently into Low/Neutral/High book-to-market using NYSE 30th/70th percentiles. Returns within each of the six intersections are value weighted.

SMB=13(SL+SN+SH)−13(BL+BN+BH)SMB=\tfrac13(SL+SN+SH)-\tfrac13(BL+BN+BH)
HML=12(SH+BH)−12(SL+BL)HML=\tfrac12(SH+BH)-\tfrac12(SL+BL)

The SMB of FF5 averages size spreads across book-to-market, profitability and investment sorts, so it differs from FF3 SMB. These long-short spreads are not automatically market neutral or costlessly investable: borrowing, financing, turnover and collateral matter. Size sorts use market equity; log size is often used in cross-sectional regressions but is not required for sorting.

See the official factor definitions and FF5 construction.

Sample and units. We start in January 2010 and stop at the latest complete month shared by prices and the released factors. The cells print the exact dates and counts. French percentages are converted to decimals once. ETF returns use consecutive adjusted month-end closing prices, so dividends and splits are accounted for by the vendor and return intervals match the calendar-month factors. We fetch a preceding price month and exclude incomplete months.

Data vintage. French’s US series switched from CRSP FIZ to CIZ beginning with the January 2025 release. CIZ monthly returns compound daily returns with dividends reinvested on ex-dates; historical series can be revised. These are current-vintage analyses, not point-in-time backtests. Data Library notes.

A Note on Sample Size

Professional empirical research typically uses 10+ years of monthly data (120+ observations) to obtain reliable estimates. A larger sample:

  • Increases statistical power (more likely to detect true effects)

  • Reduces standard errors (more data = less uncertainty)

  • Makes diagnostic tests more reliable (e.g., the Jarque-Bera test has low power with fewer than ~50 observations)

US FF3 (decimal monthly returns): 201 observations, 2010-01-01 to 2026-09-01; as of 2026-10-31
            Mkt_RF     SMB     HML      RF
Date                                      
2026-05-01  0.0491 -0.0159 -0.0231  0.0031
2026-06-01 -0.0107  0.0339  0.0358  0.0029
2026-07-01 -0.0061 -0.0192  0.0211  0.0033
2026-08-01  0.0256  0.0034 -0.0354  0.0029
2026-09-01 -0.0107 -0.0316 -0.0155  0.0031
SPY adjusted daily prices: 6,770 observations, 1999-12-01 to 2026-10-30; as of 2026-10-31
IWN adjusted daily prices: 6,604 observations, 2000-07-28 to 2026-10-30; as of 2026-10-31
VTV adjusted daily prices: 5,725 observations, 2004-01-30 to 2026-10-30; as of 2026-10-31
Common monthly regression sample: 201 observations, 2010-01-01 to 2026-09-01; as of 2026-10-31
            Mkt_RF     SMB     HML      RF  ETF_Return  Stock_Excess_Return
Date                                                                       
2010-01-01 -0.0335  0.0043  0.0033  0.0000   -0.036343            -0.036343
2010-02-01  0.0339  0.0118  0.0318  0.0000    0.031195             0.031195
2010-03-01  0.0630  0.0146  0.0219  0.0001    0.060880             0.060780
2010-04-01  0.0199  0.0484  0.0296  0.0001    0.015470             0.015370
2010-05-01 -0.0790  0.0013 -0.0248  0.0001   -0.079455            -0.079555

Section 3: Descriptive Statistics — Understanding Your Data

Before building a regression model, we need to understand the basic properties of our data. Descriptive statistics summarize large datasets into a few key numbers — mean, standard deviation, percentiles, and correlations.

Prerequisite recap: If any of the formulas below are unfamiliar (e.g., Bessel’s correction, the difference between nn and n−1n-1, or how sample covariance relates to population covariance), see the Statistical Foundations, Sections 1–2, where every formula is derived and verified on a 5-observation dataset.

Quick Reference

StatisticFormulaFinancial Meaning
Meanxˉ=1n∑xi\bar{x} = \frac{1}{n}\sum x_iAverage monthly return
Std Devs=1n−1∑(xi−xˉ)2s = \sqrt{\frac{1}{n-1}\sum(x_i - \bar{x})^2}Volatility / risk
Correlationrxy=Cov(x,y)/(sxsy)r_{xy} = \text{Cov}(x,y) / (s_x s_y)Co-movement (−1 to +1)

Key things to look for:

  • Mkt_RF mean × 12 gives a rough annualized market premium

  • Std Dev × 12\sqrt{12} gives an annualized volatility estimate (typical US equity: 15–20%)

  • Low correlations between factors (SMB, HML, Mkt-RF) confirm they capture different dimensions of risk — this is by construction

Now let’s examine our data:


DESCRIPTIVE STATISTICS (All returns in decimal form)

           Mkt_RF         SMB         HML          RF  ETF_Return  \
count  201.000000  201.000000  201.000000  201.000000  201.000000   
mean     0.010822   -0.000877   -0.000558    0.001184    0.011950   
std      0.042944    0.025254    0.032421    0.001536    0.041402   
min     -0.133700   -0.059300   -0.138300    0.000000   -0.124871   
25%     -0.012000   -0.019300   -0.018800    0.000000   -0.011215   
50%      0.013700    0.000600   -0.003800    0.000100    0.017011   
75%      0.034500    0.013100    0.016600    0.002100    0.036838   
max      0.136000    0.071400    0.128600    0.004800    0.126984   

       Stock_Excess_Return  
count           201.000000  
mean              0.010766  
std               0.041385  
min              -0.126071  
25%              -0.013193  
50%               0.016200  
75%               0.035446  
max               0.126984  


CORRELATION MATRIX

                     Mkt_RF     SMB     HML      RF  ETF_Return  \
Mkt_RF               1.0000  0.3279  0.0284 -0.0107      0.9942   
SMB                  0.3279  1.0000  0.1041 -0.1117      0.2485   
HML                  0.0284  0.1041  1.0000 -0.0369      0.0293   
RF                  -0.0107 -0.1117 -0.0369  1.0000      0.0297   
ETF_Return           0.9942  0.2485  0.0293  0.0297      1.0000   
Stock_Excess_Return  0.9950  0.2528  0.0307 -0.0074      0.9993   

                     Stock_Excess_Return  
Mkt_RF                            0.9950  
SMB                               0.2528  
HML                               0.0307  
RF                               -0.0074  
ETF_Return                        0.9993  
Stock_Excess_Return               1.0000  
Source
<Figure size 1600x550 with 3 Axes>

Interpreting the Descriptive Statistics

The exact numbers you see will depend on the data downloaded from Yahoo Finance. Here are the key things to look for:

Key Observations:

Count (n): Check how many months of data we have. The loading cell prints the current matched sample; more months do not by themselves guarantee valid inference.

Mean (Average Returns):

  • Mkt_RF: The average monthly market excess return. Multiply by 12 for a rough annualized figure (an arithmetic annualization, not realized CAGR; realized total-return CAGR uses the product of gross returns).

  • Stock_Excess_Return: Since SPY tracks the broad US market, its mean excess return should be close to the Mkt_RF mean.

Standard Deviation (Volatility):

  • Mkt_RF and Stock_Excess_Return: For a broad US market ETF like SPY, these should be similar. Multiply monthly std dev by 12\sqrt{12} for an annualized approximation.

  • A typical annualized US equity volatility is around 15–20%.

Range (Min to Max):

  • Look for extreme months (e.g., the COVID crash in March 2020 will appear as a large negative).

  • Even diversified ETFs experience significant monthly swings.

The Correlation Matrix shows how variables move together (scale: -1 to +1):

Between Factors:

  • Mkt-RF vs SMB: Typically weak to moderate (small and large caps move somewhat together)

  • Mkt-RF vs HML: Typically near zero (market and value effects are largely independent)

  • SMB vs HML: Typically near zero (confirming they capture different risks)

SPY’s Correlations:

  • Mkt-RF vs SPY: Should be very high (close to 1), since SPY tracks the US market that the Mkt-RF factor is derived from. This is exactly what we expect!

  • SMB vs SPY: Should be near zero or slightly negative — a large-cap-heavy index has limited small-cap exposure.

  • HML vs SPY: Should be near zero — a market-cap-weighted index has no particular value tilt.

Manual Calculation Results (Mkt-RF):
Count: 201
Mean: 0.010822 (1.0822% monthly, 12.9863% arithmetic annualized)
Std Dev: 0.042944 (4.2944% monthly, 14.88% annualized)
Min: -0.133700 (-13.37%)
Q1: -0.012000 (-1.20%)
Median: 0.013700 (1.37%)
Q3: 0.034500 (3.45%)
Max: 0.136000 (13.60%)
IQR: 0.046500

Manual Correlation (Mkt-RF vs Stock_Excess_Return):
Covariance: 0.001768
Correlation: 0.995049
Verification (pandas): 0.995049

Understanding These Manual Calculations

The code above recalculates statistics “from scratch” using the raw formulas: mean = sum/count, standard deviation via ∑(xi−xˉ)2/(n−1)\sqrt{\sum(x_i - \bar{x})^2 / (n-1)}, covariance, and correlation. This is the same approach used on 5 data points in the Statistical Foundations notebook (Sections 1–2) — now applied at scale to real market data. The pandas .describe() and .corr() methods compute exactly the same quantities.

Why These Matter for the Fama-French Model:

  1. Mean — expected excess returns, i.e., compensation for taking risk

  2. Standard deviation — volatility / risk level of each factor

  3. Correlation — whether factors capture different risks (low correlation = good: the factors are not redundant)

For SPY vs Mkt-RF specifically, we expect correlation close to 1.0, since SPY tracks the very market that the Mkt-RF factor is derived from.

Let’s now use these factors in a regression model to explain stock returns...

Source
<Figure size 1400x1200 with 6 Axes>
Time series visualization created.

Section 4: Understanding Excess Returns and Regression Concepts

What are Excess Returns?

In the Fama-French model, we don’t use raw returns directly. Instead, we use excess returns, which are the returns on an investment minus the risk-free rate.

The formula is:

Excess Return=Actual Return−Risk-Free Rate\text{Excess Return} = \text{Actual Return} - \text{Risk-Free Rate}

Why do this? The risk-free rate represents the return you could get with a short-horizon USD risk-free proxy (a one-month Treasury bill return). By subtracting it, we’re asking: “How much extra return does this investment provide beyond what a safe investment would give?”

In our data:

  • Stock_Excess_Return = The excess return on our ETF (already calculated)

  • Mkt_RF = The market excess return (Market return minus risk-free rate)

  • SMB and HML = Long-short return spreads; do not subtract RF again

Multiple Linear Regression: The Concept

We want to estimate a relationship like this:

y=β0+β1x1+β2x2+β3x3+ϵy = \beta_0 + \beta_1 x_1 + \beta_2 x_2 + \beta_3 x_3 + \epsilon

In our case:

Ri−Rf=α+β1(Rm−Rf)+β2⋅SMB+β3⋅HML+ϵR_i - R_f = \alpha + \beta_1 (R_m - R_f) + \beta_2 \cdot SMB + \beta_3 \cdot HML + \epsilon

What this means:

  • The ETF’s excess return (yy) depends on three factors (x1,x2,x3x_1, x_2, x_3)

  • We want to find the coefficients (β0,β1,β2,β3\beta_0, \beta_1, \beta_2, \beta_3) that best fit the data

  • ϵ\epsilon is the “leftover” part we can’t explain—the error term


Section 5: Ordinary Least Squares (OLS) Regression

Applying the Normal Equations to Real Factor Data

OLS finds the coefficients that minimize the sum of squared errors. In matrix form:

β^=(XTX)−1XTy\hat{\boldsymbol{\beta}} = (\mathbf{X}^T \mathbf{X})^{-1} \mathbf{X}^T \mathbf{y}

Full derivation: See Statistical Foundations (Section 5) for the scalar and matrix derivations of the normal equations with hand-checkable examples. Here we apply the same formula to real Fama-French data.

For our 3-factor model, the design matrix is:

X=(1(Rm−Rf)1SMB1HML1⋮⋮⋮⋮1(Rm−Rf)nSMBnHMLn)\mathbf{X} = \begin{pmatrix} 1 & (R_m - R_f)_1 & SMB_1 & HML_1 \\ \vdots & \vdots & \vdots & \vdots \\ 1 & (R_m - R_f)_n & SMB_n & HML_n \end{pmatrix}

The first column of 1’s gives us the intercept (α\alpha). The code below walks through each matrix operation step by step on the actual data.

======================================================================
MANUAL OLS CALCULATION USING NORMAL EQUATIONS
======================================================================

Design matrix X shape: (201, 4)
Dependent variable y shape: (201,)

First 5 rows of X:
   [Intercept]  [Mkt_RF]  [SMB]    [HML]
[[ 1.     -0.0335  0.0043  0.0033]
 [ 1.      0.0339  0.0118  0.0318]
 [ 1.      0.063   0.0146  0.0219]
 [ 1.      0.0199  0.0484  0.0296]
 [ 1.     -0.079   0.0013 -0.0248]]

X^T shape: (4, 201)

X^T * X (shape (4, 4)):
[[ 2.0100000e+02  2.1752000e+00 -1.7630000e-01 -1.1210000e-01]
 [ 2.1752000e+00  3.9238282e-01  6.9221980e-02  6.7043500e-03]
 [-1.7630000e-01  6.9221980e-02  1.2771227e-01  1.7137480e-02]
 [-1.1210000e-01  6.7043500e-03  1.7137480e-02  2.1028735e-01]]

(X^T * X)^-1:
[[ 5.37053236e-03 -3.43523011e-02  2.57840326e-02  1.85685532e-03]
 [-3.43523011e-02  3.03797281e+00 -1.69715404e+00  2.31417509e-02]
 [ 2.57840326e-02 -1.69715404e+00  8.87351111e+00 -6.55298165e-01]
 [ 1.85685532e-03  2.31417509e-02 -6.55298165e-01  4.80905372e+00]]

X^T * y:
[2.16399701 0.37710663 0.0509441  0.0070265 ]

======================================================================
ESTIMATED COEFFICIENTS (β̂)
======================================================================
Alpha (α̂):          -0.000006 (intercept)
Beta MKT (β̂_MKT):   0.985004 (market sensitivity)
Beta SMB (β̂_SMB):   -0.136763 (size sensitivity)
Beta HML (β̂_HML):   0.013152 (value sensitivity)

Note: We will compare these manual estimates with the statsmodels
output in the next section to confirm they are identical.

Section 6: Fitting the Model with statsmodels

In Section 5 we computed the OLS coefficients manually using the normal equations. In practice, we use a statistical library — statsmodels — which does the same calculation and also computes many additional diagnostics automatically.

What Does statsmodels Do?

When we call sm.OLS(y, X).fit(), the library:

  1. Computes the OLS estimates β^=(XTX)−1XTy\hat{\boldsymbol{\beta}} = (\mathbf{X}^T\mathbf{X})^{-1}\mathbf{X}^T\mathbf{y} — exactly what we did by hand.

  2. Estimates the error variance σ^2=RSSn−k\hat{\sigma}^2 = \frac{RSS}{n - k} from the residuals.

  3. Calculates many additional statistics — standard errors, t-statistics, p-values, R2R^2, and more.

For now, we’ll only verify that statsmodels produces the same coefficients as our manual calculation. This is an important sanity check: if both methods agree, we know our math is correct.

The additional statistics (R-squared, standard errors, t-statistics, p-values, etc.) will be introduced one at a time in the sections that follow, so you can understand each one properly before moving on.

======================================================================
VERIFICATION: MANUAL vs STATSMODELS COEFFICIENTS
======================================================================

Coefficient                   Manual   statsmodels    Match?
------------------------------------------------------------
Alpha (intercept)          -0.000006     -0.000006       Yes
Beta_MKT                    0.985004      0.985004       Yes
Beta_SMB                   -0.136763     -0.136763       Yes
Beta_HML                    0.013152      0.013152       Yes

All coefficients match: True

The statsmodels results object also contains standard errors,
t-statistics, p-values, R-squared, and many other diagnostics.
We will explore each of these in the sections that follow.
Source
<Figure size 1700x500 with 3 Axes>
Left:   Points near the 45° line → model captures most variation.
Center: The steep slope confirms SPY moves nearly 1-for-1 with the market.
Right:  No visible pattern in residuals → model specification looks reasonable.

Section 7: Model Performance Metrics

Understanding R-squared and Other Fit Statistics

Before diving into the individual coefficients, let’s assess how well the model fits the data overall. R² and residual sums of squares describe in-sample fit. F-test p-values depend on error assumptions; we contrast classical and HAC-based tests below.

The R-squared Formula

R2=1−RSSTSSR^2 = 1 - \frac{RSS}{TSS}

Where:

  • RSSRSS (Residual Sum of Squares) = ∑i=1n(yi−y^i)2\sum_{i=1}^{n} (y_i - \hat{y}_i)^2 = Sum of squared residuals

  • TSSTSS (Total Sum of Squares) = ∑i=1n(yi−yˉ)2\sum_{i=1}^{n} (y_i - \bar{y})^2 = Total variation in yy

  • yˉ\bar{y} = Mean of the dependent variable

Intuition:

  • TSSTSS measures how much yy varies around its mean (total variation to explain)

  • RSSRSS measures how much unexplained variation remains after fitting the model

  • R2R^2 tells us what fraction of the total variation is explained by the model

  • R2R^2 ranges from 0 to 1; higher is better

Adjusted R-squared

The problem with R2R^2 is that adding regressors to a nested OLS model on the same sample cannot decrease it (even irrelevant variables). Adjusted R2R^2 penalizes for the number of variables:

Rˉ2=1−RSS/(n−k)TSS/(n−1)=1−(1−R2)(n−1)n−k\bar{R}^2 = 1 - \frac{RSS/(n-k)}{TSS/(n-1)} = 1 - \frac{(1-R^2)(n-1)}{n-k}

Where:

  • nn = number of observations

  • kk = number of parameters (including intercept)

  • The denominator (n−k)(n-k) is called “degrees of freedom”

======================================================================
MODEL PERFORMANCE METRICS - MANUAL CALCULATIONS
======================================================================

NOTE: TSS = RSS + ESS (in-sample OLS with an intercept)
TSS = 0.34253926
RSS = 0.00127362
ESS = 0.34126564
TSS = RSS + ESS? True

1. R-SQUARED (R²)
   Formula: R² = 1 - (RSS / TSS)
   Calculation: R² = 1 - (0.001274 / 0.342539)
   R² = 0.996282
   Interpretation: 99.63% of the variation in stock returns is
   explained by the three Fama-French factors.

2. ADJUSTED R-SQUARED (R̄²)
   Formula: R̄² = 1 - ((1 - R²) × (n-1) / (n-k))
   Where n = 201 observations, k = 4 parameters
   Calculation: R̄² = 1 - ((1 - 0.9963) × 200 / 197)
   R̄² = 0.996225
   Interpretation: After adjusting for the number of variables,
   99.62% of variation is explained.

3. RESIDUAL STANDARD ERROR (σ̂)
   Formula: σ̂² = RSS / (n - k)
   Calculation: σ̂² = 0.001274 / 197
   σ̂² = 0.00000647
   σ̂ = 0.002543
   Interpretation: Residual SD (not mean absolute prediction error) is 0.002543
   (0.2543% per month in this case).

4. F-STATISTIC
   Formula: F = (ESS / (k-1)) / (RSS / (n-k)) = MSR / MSE
   MSR = 0.11375521, MSE = 0.00000647
   F-statistic = 17595.3389
   p-value = 5.34e-239
   Interpretation: This tests the null hypothesis that ALL coefficients
   (except intercept) equal zero. If p-value < 0.05, we reject this,
   meaning the model IS statistically significant.
   The model IS statistically significant (p < 0.05)

======================================================================
COMPARISON WITH STATSMODELS OUTPUT
======================================================================
Our R²:               0.996282
Statsmodels R²:       0.996282
Match? True

Our Adjusted R²:      0.996225
Statsmodels Adj R²:   0.996225
Match? True

Our F-statistic:      17595.3389
Statsmodels F-stat:   17595.3389
Match? True
The classical F reference needs its classical error assumptions; use the HAC Wald test below for robust joint inference.
Source
<Figure size 1400x500 with 2 Axes>

Section 8: Validating Regression Assumptions and Diagnostics

Why This Section Matters

The regression in Section 6 gave us coefficient estimates (α^\hat{\alpha}, β^MKT\hat{\beta}_{MKT}, etc.) and the performance metrics in Section 7 told us how well the model fits. But those numbers come with a hidden catch: the standard errors, t-statistics, and p-values printed alongside the coefficients are only trustworthy if certain assumptions about the error term hold.

If those assumptions fail — and they often do for financial data — then:

  • The coefficient estimates themselves are typically still fine (unbiased and consistent).

  • But the uncertainty measures are wrong: standard errors tend to be too small, t-statistics too large, and p-values too optimistic. In other words, you might conclude a factor is statistically significant when it really isn’t.

Connection to earlier notebooks: The Time-Series Foundations notebook examined evidence about whether financial return data is compatible with stationarity (Section 2), has weak autocorrelation but strong volatility clustering (Sections 3–4), and explores dependence diagnostics without proving mixing (Section 5). The summary “assumption stack” (Section 6) distinguished diagnostics from assumptions that remain unverified. Here we check the same assumptions directly on the Fama-French regression residuals.

This section checks the assumptions; the next section (Section 9) will show how to fix things when they fail.

Source
<Figure size 1600x1200 with 6 Axes>
Diagnostic plots created.

Formal Statistical Tests for Assumptions

Visual inspection gives us intuition, but formal statistical tests provide rigorous evidence. Each test below has a null hypothesis (H0H_0) representing the “ideal” scenario and an alternative that flags a violation.

======================================================================
FORMAL TESTS FOR REGRESSION ASSUMPTIONS
======================================================================

1. JARQUE-BERA TEST FOR NORMALITY
   Null Hypothesis (H0): Residuals are normally distributed
   Alternative Hypothesis (H1): Residuals are NOT normally distributed
   Caveat: With ~100+ observations, JB can detect departures in financial
   data that has mild fat tails (excess kurtosis), even when the
   departure from normality is too small to meaningfully affect OLS.
   This is one reason we use robust standard errors regardless.
   Test Statistic: 87.952962
   p-value: 0.000000
   CONCLUSION: We reject H0 (p < 0.05)
     Residuals appear to NOT be normally distributed.

2. BREUSCH-PAGAN TEST FOR HOMOSCEDASTICITY
   Null Hypothesis (H0): Constant variance of residuals (homoscedasticity)
   Alternative Hypothesis (H1): Variance is NOT constant (heteroscedasticity)
   Test Statistic: 9.673128
   p-value: 0.021559
   CONCLUSION: We reject H0 (p < 0.05)
     There appears to be heteroscedasticity.

3. DURBIN-WATSON TEST FOR AUTOCORRELATION
   Tests for correlation between consecutive residuals
   Durbin-Watson Statistic: 1.738673
   Interpretation:
      DW ~ 2: No autocorrelation (ideal)
      DW < 2: Positive autocorrelation
      DW > 2: Negative autocorrelation
   CONCLUSION: DW is near 2; this heuristic is not a significance test of independence.

4. VARIANCE INFLATION FACTOR (VIF) FOR MULTICOLLINEARITY
   VIF measures how much the variance of a coefficient is
   inflated due to correlation with other variables.
   Interpretation: VIF > 5 suggests multicollinearity problem

   Mkt_RF    : VIF = 1.1205 (Below the heuristic VIF threshold)
   SMB       : VIF = 1.1319 (Below the heuristic VIF threshold)
   HML       : VIF = 1.0110 (Below the heuristic VIF threshold)

======================================================================
SUMMARY OF ASSUMPTIONS
======================================================================
Normality (Jarque-Bera):      Rejected
Homoscedasticity (BP test):   Rejected
Lag-1 dependence (DW heuristic): PASS
Multicollinearity (VIF):      PASS

Note: Even if all tests pass, robust standard errors are still
recommended for financial time-series data. Financial returns exhibit
time-varying volatility (conditional heteroscedasticity) that the
Breusch-Pagan test may not detect. This motivates the next section.
Residual Ljung-Box diagnostics (approximate reference):
     lb_stat  lb_pvalue
6   7.090766   0.312535
12  9.848872   0.629217
A non-rejection is not proof that an assumption holds.
Source
<Figure size 1700x500 with 3 Axes>

Section 9: Robust Standard Errors — The Standard in Empirical Finance

Why This Matters

The diagnostic tests above may or may not have flagged violations. But here’s the key insight: in financial time-series, we almost always use robust standard errors regardless of what the tests say.

Why? Because:

  • Financial returns exhibit stochastic volatility — variance changes over time. This often carries over to regression residuals, and simple tests may miss it.

  • Monthly returns can show mild autocorrelation, so treating a time-series regression as i.i.d. is risky.

  • Classic OLS standard errors assume constant variance and no autocorrelation. If those assumptions fail, your coefficient estimates (β^\hat{\beta}) are still unbiased, but your standard errors, t-statistics, p-values, and confidence intervals are wrong.

Full derivation and intuition: The sandwich formula (HC0), its small-sample variants (HC1–HC3), and the Newey-West extension are derived step-by-step — with hand calculations on a 5-observation dataset — in the Statistical Foundations (Sections 6–9). The Monte Carlo evidence there demonstrates that classic OLS over-rejects under H0H_0 while Newey-West maintains the correct 5 % size.

An Intuitive Picture: Why Autocorrelation Inflates False Positives

Imagine 100 monthly residuals. If each were truly independent, you’d have 100 independent pieces of information. But with positive autocorrelation, consecutive residuals are partially redundant: your 100 observations effectively contain fewer than 100 independent pieces. Classic OLS doesn’t know this — it treats all 100 as fully independent, producing standard errors that are too small, t-statistics that are too large, and p-values that are too optimistic.

Section 5 of the Time-Series Foundations notebook studied the dependence structure of the raw return series. That evidence motivates HAC inference, but the direct evidence for the present regression comes from the residual diagnostics in Section 8 above.

The Solution: Let the Data Determine the Variance

Instead of assuming a rigid error structure, robust methods let the residuals themselves determine the variance estimate:

MethodCorrects forWhen to use
HC1 / HC3Heteroscedasticity onlyCross-sectional data
Newey-West HACHeteroscedasticity + autocorrelationTime-series data (our case)

We use Newey–West as a working covariance specification and check sensitivity. The code below compares all four methods side-by-side.

================================================================================
COMPARING STANDARD ERROR ESTIMATES: CLASSIC VS. ROBUST METHODS
================================================================================

1. CLASSIC OLS STANDARD ERRORS (assumes homoscedastic, independent errors)
--------------------------------------------------------------------------------
==============================================================================
                 coef    std err          t      P>|t|      [0.025      0.975]
------------------------------------------------------------------------------
const      -6.073e-06      0.000     -0.033      0.974      -0.000       0.000
x1             0.9850      0.004    222.259      0.000       0.976       0.994
x2            -0.1368      0.008    -18.057      0.000      -0.152      -0.122
x3             0.0132      0.006      2.359      0.019       0.002       0.024
==============================================================================

================================================================================
2. HC1 STANDARD ERRORS (corrects for heteroscedasticity only)
--------------------------------------------------------------------------------
==============================================================================
                 coef    std err          z      P>|z|      [0.025      0.975]
------------------------------------------------------------------------------
const      -6.073e-06      0.000     -0.035      0.972      -0.000       0.000
x1             0.9850      0.005    210.044      0.000       0.976       0.994
x2            -0.1368      0.009    -16.008      0.000      -0.154      -0.120
x3             0.0132      0.007      2.016      0.044       0.000       0.026
==============================================================================

================================================================================
3. HC3 STANDARD ERRORS (alternative HC correction)
--------------------------------------------------------------------------------
==============================================================================
                 coef    std err          z      P>|z|      [0.025      0.975]
------------------------------------------------------------------------------
const      -6.073e-06      0.000     -0.035      0.972      -0.000       0.000
x1             0.9850      0.005    204.999      0.000       0.976       0.994
x2            -0.1368      0.009    -15.619      0.000      -0.154      -0.120
x3             0.0132      0.007      1.954      0.051   -4.23e-05       0.026
==============================================================================

================================================================================
4. NEWEY-WEST STANDARD ERRORS (corrects for heteroscedasticity + autocorrelation)
--------------------------------------------------------------------------------
Sample size: 201 observations
Heuristic lag length: 4 (using sample-size bandwidth heuristic)
==============================================================================
                 coef    std err          z      P>|z|      [0.025      0.975]
------------------------------------------------------------------------------
const      -6.073e-06      0.000     -0.035      0.972      -0.000       0.000
x1             0.9850      0.005    184.512      0.000       0.975       0.995
x2            -0.1368      0.010    -14.323      0.000      -0.155      -0.118
x3             0.0132      0.007      1.952      0.051   -5.11e-05       0.026
==============================================================================

================================================================================
DETAILED COMPARISON OF STANDARD ERRORS
================================================================================
      Variable  Classic SE   HC1 SE   HC3 SE  Newey-West SE  HC1 % Change  NW % Change
 Intercept (a)    0.000186 0.000172 0.000174       0.000174     -7.950544    -6.470338
Mkt_RF (B_MKT)    0.004432 0.004690 0.004805       0.005338      5.815530    20.457891
   SMB (B_SMB)    0.007574 0.008543 0.008756       0.009549     12.794545    26.069101
   HML (B_HML)    0.005576 0.006524 0.006732       0.006737     17.009315    20.815533

================================================================================
INTERPRETING THE COMPARISON
================================================================================

% Change Interpretation:
- Positive: Robust method gives LARGER standard errors
- Negative: Robust method gives SMALLER standard errors
- |Change| > 10%: Notable difference — using classic SE could mislead

Residual autocorrelation structure (explains WHY some NW SEs differ):
  Durbin-Watson = 1.739  (2 = none, <2 = positive, >2 = negative)
  Lag-1 autocorrelation = +0.1280

The covariance estimator depends on the scores x_t * residual_t, not just
residual autocorrelation. Robust SEs may be larger or smaller than classical
SEs; neither sign is a guarantee of accuracy. Compare the actual table above.
HAC remains an asymptotic estimate under exogeneity and weak dependence.

Quick-Reference: The Sandwich Formula

All robust standard errors share the same sandwich structure:

Var^(β^)=(XTX)−1⏟bread    M⏟meat    (XTX)−1⏟bread\widehat{\text{Var}}(\hat{\boldsymbol{\beta}}) = \underbrace{(\mathbf{X}^T \mathbf{X})^{-1}}_{\text{bread}} \;\; \underbrace{\mathbf{M}}_{\text{meat}} \;\; \underbrace{(\mathbf{X}^T \mathbf{X})^{-1}}_{\text{bread}}

What differs between methods is the meat M\mathbf{M}:

MethodMeatKey feature
HC0XTdiag(ϵ^i2)X\mathbf{X}^T \text{diag}(\hat\epsilon_i^2) \mathbf{X}Uses squared residuals; no autocorrelation correction
Newey-West∑j=0LwjΓ^j\sum_{j=0}^{L} w_j \hat\Gamma_jAdds Bartlett-weighted cross-lag terms up to lag LL

Lag selection (critical for Newey-West): L=⌊4(n/100)2/9⌋L = \lfloor 4(n/100)^{2/9}\rfloor — the sample-size bandwidth heuristic. Too few lags misses correlation; too many introduces noise.

For the complete hand calculation of each formula — HC0, HC1, HC2, HC3, and Newey-West with Bartlett weights — see the Statistical Foundations, Sections 6–7.

Decision Guide — Which Standard Errors?

Is this TIME-SERIES data?
  ├─ NO  (cross-sectional)  →  HC1 or HC3
  └─ YES (monthly returns, etc.)  →  Newey-West HAC  ✓

For the Fama-French model: Our data is monthly returns (time-series), so Newey-West is the correct choice. Many finance journals now require robust standard errors. Let’s see how much the choice matters:

Source

================================================================================
IMPACT ON HYPOTHESIS TESTING: HOW P-VALUES CHANGE
================================================================================

P-value Comparison (Two-tailed test, a = 0.05)
--------------------------------------------------------------------------------
      Variable  Coefficient  Classic p-val    HC1 p-val  Newey-West p-val Significance Change?
 Intercept (a)    -0.000006   9.740323e-01 9.717544e-01      9.722013e-01                     
Mkt_RF (B_MKT)     0.985004  1.813046e-238 0.000000e+00      0.000000e+00                     
   SMB (B_SMB)    -0.136763   1.215965e-43 1.117937e-57      1.578528e-46                     
   HML (B_HML)     0.013152   1.931426e-02 4.381160e-02      5.089316e-02                 YES!

CRITICAL: If a variable is significant under classic but NOT under robust SEs
(or vice versa), using the classic method could lead to wrong conclusions!

<Figure size 1500x600 with 2 Axes>

Figure: Confidence interval and standard error comparison
================================================================================
RECOMMENDED APPROACH: FAMA-FRENCH 3-FACTOR WITH NEWEY-WEST SEs
================================================================================

Our data: Monthly ETF returns (time-series)
Sample: January 2010 to September 2026
Number of observations: 201

Newey-West lag length (sample-size bandwidth heuristic): 4

Full Results with Newey-West HAC Standard Errors:
================================================================================
                            OLS Regression Results                            
==============================================================================
Dep. Variable:                      y   R-squared:                       0.996
Model:                            OLS   Adj. R-squared:                  0.996
Method:                 Least Squares   F-statistic:                 1.313e+04
Date:                Sat, 31 Oct 2026   Prob (F-statistic):          1.56e-226
Time:                        17:06:03   Log-Likelihood:                 917.70
No. Observations:                 201   AIC:                            -1827.
Df Residuals:                     197   BIC:                            -1814.
Df Model:                           3                                         
Covariance Type:                  HAC                                         
==============================================================================
                 coef    std err          z      P>|z|      [0.025      0.975]
------------------------------------------------------------------------------
const      -6.073e-06      0.000     -0.035      0.972      -0.000       0.000
x1             0.9850      0.005    184.512      0.000       0.975       0.995
x2            -0.1368      0.010    -14.323      0.000      -0.155      -0.118
x3             0.0132      0.007      1.952      0.051   -5.11e-05       0.026
==============================================================================
Omnibus:                       33.723   Durbin-Watson:                   1.739
Prob(Omnibus):                  0.000   Jarque-Bera (JB):               87.953
Skew:                           0.708   Prob(JB):                     7.97e-20
Kurtosis:                       5.915   Cond. No.                         43.5
==============================================================================

Notes:
[1] Standard Errors are heteroscedasticity and autocorrelation robust (HAC) using 4 lags and with small sample correction
HAC joint Wald test of all slopes = 0: chi-square=39393.388, p=0
HAC: Bartlett weights, n/(n-k) correction, asymptotic normal coefficient tests.
Point estimates are unchanged; compare SEs and confidence intervals above.
A beta measures conditional sensitivity, not the asset's total volatility.
Non-significant alpha means insufficient evidence against zero, not proof of zero.

Section 10: Interpreting the Results — t-Statistics, p-Values, and Confidence Intervals

Now that we have reliable standard errors (Newey-West), we can properly interpret each coefficient and its statistical significance.

Why can we use the normal distribution for p-values here? A score-process CLT gives asymptotic normality under exogeneity, suitable moments, dependence conditions and a nonsingular regressor moment matrix; arbitrary error distributions are not covered. Combined with Newey-West and Slutsky’s theorem, the t-statistic converges to N(0,1)\mathcal{N}(0,1). For the full five-step argument, see the Statistical Foundations Tutorial, Section 4 (Subsections 4.7–4.8), which is then restated in the applied-inference context in Section 9.

Quick Refresher (detailed derivations: Statistical Foundations, Section 4)

StatisticFormulaRule of thumb
t-statistict=β^j / SE(β^j)t = \hat{\beta}_j \,/\, \text{SE}(\hat{\beta}_j)$
p-valueProb of result this extreme under H0:βj=0H_0:\beta_j=0p<0.05p < 0.05 → reject H0H_0
95 % CIβ^j±1.96×SE(β^j)\hat{\beta}_j \pm 1.96 \times \text{SE}(\hat{\beta}_j)CI excludes 0 → significant

Statistical vs economic significance: A tiny but precisely estimated effect can be statistically significant (small pp) without being economically meaningful. For example, α^=0.0002\hat\alpha = 0.0002 (0.02 %/month ≈ 0.24 %/year) may matter statistically but is smaller than most transaction costs. Always ask: Is the magnitude large enough to matter in practice?

Interpreting the Fama-French Coefficients

CoefficientWhat it measuresPositive meansNegative means
α\alpha (Jensen’s Alpha)Abnormal return beyond factor exposureOutperformanceUnderperformance
βMKT\beta_{MKT}Conditional market sensitivityMoves with the market, holding other factors fixedMoves against the market, holding other factors fixed
βSMB\beta_{SMB}Size tiltSmall-cap tiltLarge-cap tilt
βHML\beta_{HML}Value tiltValue tiltGrowth tilt

Let’s Now Interpret Our Fama-French Results Using Newey-West Standard Errors

======================================================================
DETAILED INTERPRETATION OF COEFFICIENTS (NEWEY-WEST SEs)
======================================================================

1. ALPHA (Jensen's Alpha)
   Estimate: -0.000006 (-0.0006% per month)
   Newey-West Std Error: 0.000174
   t-statistic: -0.0348
   p-value: 0.9722
   95% CI: [-0.000348, 0.000336]
   Interpretation: This is Jensen's alpha. It represents the abnormal
   monthly return not explained by the three factors. A value of -0.0000
   (-0.00% per month) means this ETF earns an excess return of
   -0.00% per month beyond what the model predicts.
   NOT statistically significant (p >= 0.05): Cannot distinguish alpha from zero.

2. BETA_MKT (Market Beta)
   Estimate: 0.985004
   Newey-West Std Error: 0.005338
   t-statistic: 184.5117
   p-value: 0.0000
   95% CI: [0.974541, 0.995467]
   Interpretation: For every 1% increase in the market excess return,
   the ETF's excess return changes by 0.9850% (holding other
   factors constant). This is market sensitivity, not total volatility relative to
   the market. Other factors and residual risk also contribute to volatility.
   STATISTICALLY SIGNIFICANT: Market exposure is real.

3. BETA_SMB (Size Factor)
   Estimate: -0.136763
   Newey-West Std Error: 0.009549
   t-statistic: -14.3227
   p-value: 0.0000
   95% CI: [-0.155478, -0.118048]
   Interpretation: For every 1% increase in the SMB factor (small
   relative to big), the ETF's excess return changes by -0.1368%.
   A positive value means the ETF tilts toward SMALL-cap exposure.
   A negative value would mean it tilts toward LARGE-cap exposure.
   STATISTICALLY SIGNIFICANT: Real size effect exposure.

4. BETA_HML (Value Factor)
   Estimate: 0.013152
   Newey-West Std Error: 0.006737
   t-statistic: 1.9524
   p-value: 0.0509
   95% CI: [-0.000051, 0.026356]
   Interpretation: For every 1% increase in the HML factor (value
   relative to growth), the ETF's excess return changes by 0.0132%.
   A positive value means the ETF tilts toward VALUE exposure.
   A negative value would mean it tilts toward GROWTH exposure.
   NOT statistically significant: Value effect not clearly present.

======================================================================
KEY TAKEAWAY
======================================================================

All the standard errors, t-statistics, p-values, and confidence intervals
above use Newey-West HAC estimation. This means:

- They account for heteroscedasticity (time-varying volatility)
- They account for autocorrelation (serial dependence in returns)
- Robust covariance relaxes classical variance assumptions; finite-sample accuracy remains uncertain

When reporting Fama-French results:
  - Choose covariance assumptions explicitly and check HAC lag sensitivity
  - Report the lag length used (here: sample-size bandwidth heuristic)
  - Discuss economic significance separately from statistical significance
  - Compare with classic SEs as a robustness check


Section 11: Complete Worked Example - Using the Model to Make Predictions

Now let’s use our fitted model to make a prediction and see how the model works in practice.

The Prediction Formula

For any month with known factor values, we can predict the ETF’s excess return using:

R^i=α^+β^MKT⋅(Rm−Rf)+β^SMB⋅SMB+β^HML⋅HML\hat{R}_i = \hat{\alpha} + \hat{\beta}_{MKT} \cdot (R_m - R_f) + \hat{\beta}_{SMB} \cdot SMB + \hat{\beta}_{HML} \cdot HML

Let’s make a prediction for a specific month and compare it to the actual return.

This is an in-sample conditional fitted value, using realized factors and coefficients estimated on the full sample. It is not a forecast made before the month began and does not measure out-of-sample predictive performance.

======================================================================
WORKED EXAMPLE: PREDICTING ETF RETURN FOR A SPECIFIC MONTH
======================================================================

Date: March 2014

Factor values for this month:
  Market Risk Premium (Mkt_RF): 0.0043 (0.43%)
  SMB Factor:                    -0.0186 (-1.86%)
  HML Factor:                    0.0490 (4.90%)

Our estimated model coefficients:
  α̂ (Alpha):     -0.000006
  β̂_MKT:         0.985004
  β̂_SMB:         -0.136763
  β̂_HML:         0.013152

======================================================================
PREDICTION CALCULATION (STEP BY STEP)
======================================================================

Formula: R̂_i = α̂ + β̂_MKT × Mkt_RF + β̂_SMB × SMB + β̂_HML × HML

R̂_i = -0.000006
     + 0.985004 × 0.004300
     + -0.136763 × -0.018600
     + 0.013152 × 0.049000

Component breakdown:
  α̂:                    -0.000006 (-0.0006%)
  β̂_MKT × Mkt_RF:       0.004236 (0.4236%)
  β̂_SMB × SMB:          0.002544 (0.2544%)
  β̂_HML × HML:          0.000644 (0.0644%)

Predicted return (sum): 0.007418 (0.7418%)
Actual return:          0.008295 (0.8295%)

Prediction error (residual): 0.000877 (0.0877%)

Interpretation:
  The model predicted very accurately for this month!

Verification with statsmodels:
  Fitted value from statsmodels:  0.007418
  Residual from statsmodels:      0.000877
  Match? True
Source
<Figure size 1400x800 with 2 Axes>
Prediction visualization created.

What the three-fund comparison teaches

SPY, IWN and VTV separate broad equity exposure from size and value tilts. Compare their estimated coefficients and intervals, then their alphas under the same model and sample. High R² means that the factors describe much of a fund’s return variation; it does not mean the fund has a higher expected return.

The reusable function below collects the same steps in one place. Use it for the exercises, preserving the units, dates and covariance assumptions. The next chapter asks whether this three-factor benchmark omits economically relevant information.

Appendix: Reusing the data workflow

from analysis_utils import load_french, load_prices, monthly_returns, fit_hac
ff = load_french('F-F_Research_Data_Factors_CSV.zip', start='2010-01-01')
r = monthly_returns(load_prices('AAPL')).rename('Return')
d = ff.join(r, how='inner').dropna()
res = fit_hac(d['Return'] - d['RF'], d[['Mkt-RF', 'SMB', 'HML']])
print(res.summary())

Match the investment universe, currency, return frequency and sample before fitting. US listing alone does not imply US stock exposure. French supplies Developed, Europe and Emerging Markets datasets; use the definitions in the Data Library.

Report sample dates, N, units, factor version, estimates and intervals, covariance method and lag length, and sensitivity to specification. A low R², large p-value or rejected diagnostic each requires investigation, not automatic variable selection or outlier deletion.


######################################################################
# COMPARING FACTOR EXPOSURES: SPY vs. IWN vs. VTV
######################################################################

We now run the same Fama-French regression on all three ETFs and
compare their factor loadings. This reveals how each investment
style maps onto the market, size, and value dimensions.

======================================================================
FAMA-FRENCH 3-FACTOR ANALYSIS: SPY (S&P 500)
======================================================================

Using Newey-West HAC (lags=4) standard errors

                            OLS Regression Results                            
==============================================================================
Dep. Variable:                      y   R-squared:                       0.996
Model:                            OLS   Adj. R-squared:                  0.996
Method:                 Least Squares   F-statistic:                 1.313e+04
Date:                Sat, 31 Oct 2026   Prob (F-statistic):          1.56e-226
Time:                        17:06:03   Log-Likelihood:                 917.70
No. Observations:                 201   AIC:                            -1827.
Df Residuals:                     197   BIC:                            -1814.
Df Model:                           3                                         
Covariance Type:                  HAC                                         
==============================================================================
                 coef    std err          z      P>|z|      [0.025      0.975]
------------------------------------------------------------------------------
const      -6.073e-06      0.000     -0.035      0.972      -0.000       0.000
x1             0.9850      0.005    184.512      0.000       0.975       0.995
x2            -0.1368      0.010    -14.323      0.000      -0.155      -0.118
x3             0.0132      0.007      1.952      0.051   -5.11e-05       0.026
==============================================================================
Omnibus:                       33.723   Durbin-Watson:                   1.739
Prob(Omnibus):                  0.000   Jarque-Bera (JB):               87.953
Skew:                           0.708   Prob(JB):                     7.97e-20
Kurtosis:                       5.915   Cond. No.                         43.5
==============================================================================

Notes:
[1] Standard Errors are heteroscedasticity and autocorrelation robust (HAC) using 4 lags and with small sample correction

======================================================================
KEY FINDINGS
======================================================================

alpha:     -0.000006  p-value: 0.9722  
beta_mkt:   0.985004  p-value: 0.0000  ***
beta_smb:  -0.136763  p-value: 0.0000  ***
beta_hml:   0.013152  p-value: 0.0509  

R-squared: 0.996282
Adj R-squared: 0.996225
F-statistic: 13131.1294 (p < 1.56e-226)

Note: *** p<0.01, ** p<0.05, * p<0.10


======================================================================
FAMA-FRENCH 3-FACTOR ANALYSIS: IWN (Small Cap Value)
======================================================================

Using Newey-West HAC (lags=4) standard errors

                            OLS Regression Results                            
==============================================================================
Dep. Variable:                      y   R-squared:                       0.982
Model:                            OLS   Adj. R-squared:                  0.982
Method:                 Least Squares   F-statistic:                     3108.
Date:                Sat, 31 Oct 2026   Prob (F-statistic):          1.41e-165
Time:                        17:06:03   Log-Likelihood:                 698.03
No. Observations:                 201   AIC:                            -1388.
Df Residuals:                     197   BIC:                            -1375.
Df Model:                           3                                         
Covariance Type:                  HAC                                         
==============================================================================
                 coef    std err          z      P>|z|      [0.025      0.975]
------------------------------------------------------------------------------
const         -0.0011      0.001     -1.852      0.064      -0.002    6.26e-05
x1             0.9749      0.015     66.738      0.000       0.946       1.004
x2             0.8255      0.029     28.653      0.000       0.769       0.882
x3             0.5263      0.022     23.625      0.000       0.483       0.570
==============================================================================
Omnibus:                        5.348   Durbin-Watson:                   1.919
Prob(Omnibus):                  0.069   Jarque-Bera (JB):                7.234
Skew:                          -0.115   Prob(JB):                       0.0269
Kurtosis:                       3.900   Cond. No.                         43.5
==============================================================================

Notes:
[1] Standard Errors are heteroscedasticity and autocorrelation robust (HAC) using 4 lags and with small sample correction

======================================================================
KEY FINDINGS
======================================================================

alpha:     -0.001073  p-value: 0.0640  
beta_mkt:   0.974912  p-value: 0.0000  ***
beta_smb:   0.825485  p-value: 0.0000  ***
beta_hml:   0.526309  p-value: 0.0000  ***

R-squared: 0.982417
Adj R-squared: 0.982149
F-statistic: 3108.2145 (p < 1.41e-165)

Note: *** p<0.01, ** p<0.05, * p<0.10


======================================================================
FAMA-FRENCH 3-FACTOR ANALYSIS: VTV (Large Cap Value)
======================================================================

Using Newey-West HAC (lags=4) standard errors

                            OLS Regression Results                            
==============================================================================
Dep. Variable:                      y   R-squared:                       0.937
Model:                            OLS   Adj. R-squared:                  0.936
Method:                 Least Squares   F-statistic:                     849.5
Date:                Sat, 31 Oct 2026   Prob (F-statistic):          2.16e-112
Time:                        17:06:03   Log-Likelihood:                 639.39
No. Observations:                 201   AIC:                            -1271.
Df Residuals:                     197   BIC:                            -1258.
Df Model:                           3                                         
Covariance Type:                  HAC                                         
==============================================================================
                 coef    std err          z      P>|z|      [0.025      0.975]
------------------------------------------------------------------------------
const       8.819e-06      0.001      0.014      0.989      -0.001       0.001
x1             0.8681      0.022     39.563      0.000       0.825       0.911
x2            -0.0915      0.029     -3.140      0.002      -0.149      -0.034
x3             0.3796      0.027     14.271      0.000       0.327       0.432
==============================================================================
Omnibus:                        5.161   Durbin-Watson:                   2.044
Prob(Omnibus):                  0.076   Jarque-Bera (JB):                7.378
Skew:                          -0.026   Prob(JB):                       0.0250
Kurtosis:                       3.937   Cond. No.                         43.5
==============================================================================

Notes:
[1] Standard Errors are heteroscedasticity and autocorrelation robust (HAC) using 4 lags and with small sample correction

======================================================================
KEY FINDINGS
======================================================================

alpha:      0.000009  p-value: 0.9890  
beta_mkt:   0.868123  p-value: 0.0000  ***
beta_smb:  -0.091525  p-value: 0.0017  ***
beta_hml:   0.379632  p-value: 0.0000  ***

R-squared: 0.937001
Adj R-squared: 0.936041
F-statistic: 849.5143 (p < 2.16e-112)

Note: *** p<0.01, ** p<0.05, * p<0.10


======================================================================
SIDE-BY-SIDE FACTOR LOADING COMPARISON
======================================================================
                             SPY         IWN         VTV
--------------------------------------------------------
Alpha (a)                -0.0000     -0.0011      0.0000
Beta MKT                  0.9850      0.9749      0.8681
Beta SMB                 -0.1368      0.8255     -0.0915
Beta HML                  0.0132      0.5263      0.3796
R-squared                 0.9963      0.9824      0.9370
Adj R-squared             0.9962      0.9821      0.9360

  p-sig Alpha                              *            
  p-sig Beta MKT             ***         ***         ***
  p-sig Beta SMB             ***         ***         ***
  p-sig Beta HML               *         ***         ***

======================================================================
INTERPRETATION
======================================================================

What to look for in the table above:

- SPY (S&P 500): Should have beta_MKT very close to 1.0, with low/insignificant
  SMB and HML loadings. It tracks large US companies, whereas the French market includes smaller stocks.
  Meaningful style loadings can remain. Expect high R-squared.

- IWN (Small Cap Value): Should have POSITIVE beta_SMB (small-cap tilt) AND
  POSITIVE beta_HML (value tilt). This ETF explicitly targets small companies
  with high book-to-market ratios -- exactly the stocks that drive the SMB
  and HML factors.

- VTV (Large Cap Value): Should have near-zero or NEGATIVE beta_SMB (large-cap)
  and POSITIVE beta_HML (value tilt). This isolates the value effect from
  the size effect, showing that HML captures something distinct from SMB.

Source
<Figure size 1000x600 with 1 Axes>

This chart makes the factor exposures immediately visible:
- SPY: Compare its market and style loadings; the S&P 500 is not the full CRSP market
- IWN: Positive loadings on ALL three factors, especially SMB and HML
- VTV: Strong market and value exposure, but low/negative size exposure


Exercises

Test your understanding by working through these exercises. They range from conceptual questions to hands-on coding tasks.

Exercise 1: Predicting Factor Loadings (Conceptual)

Before running any code, predict the factor loadings for the following hypothetical ETFs. For each, state whether you expect βMKT\beta_{MKT}, βSMB\beta_{SMB}, and βHML\beta_{HML} to be positive, negative, or near zero, and briefly explain why.

  1. QQQ (Invesco Nasdaq-100 ETF) -- tracks large-cap US technology/growth stocks

  2. IWM (iShares Russell 2000 ETF) -- tracks US small-cap stocks (no value/growth tilt)

  3. A leveraged 2x S&P 500 ETF

Exercise 2: Comparing ETFs (Hands-On)

Using the fama_french_analysis() template function defined above:

  1. Re-run the analysis for IWN (Small Cap Value) only. Is the R2R^2 higher or lower than for SPY? Why might that be?

  2. Compare the βSMB\beta_{SMB} values between SPY, IWN, and VTV. Do they match your intuition about each fund’s size exposure?

Hint: The data is already loaded in etf_dataframes. You can access it as etf_dataframes['IWN'].

Exercise 3: Sensitivity of Newey-West Standard Errors (Hands-On)

The Newey-West lag length affects the standard errors. Try the following:

  1. Re-fit the SPY regression using maxlags=0 (no autocorrelation correction, only heteroscedasticity). How do the standard errors compare to classic OLS?

  2. Re-fit using maxlags=8 (more lags). How do the standard errors change compared to the default lag length?

  3. What does this tell you about the sensitivity of your conclusions to lag choice?

# Starter code for Exercise 3:
model_ex3 = sm.OLS(df['Stock_Excess_Return'].values, sm.add_constant(df[['Mkt_RF', 'SMB', 'HML']].values))
results_ex3 = model_ex3.fit()

# Try different lag lengths:
results_lag0 = results_ex3.get_robustcov_results(cov_type='HAC', maxlags=0, use_correction=True, use_t=False)
results_lag8 = results_ex3.get_robustcov_results(cov_type='HAC', maxlags=8, use_correction=True, use_t=False)

print("Lag 0 SEs:", results_lag0.bse)
print("Lag 8 SEs:", results_lag8.bse)

Exercise 4: Economic vs. Statistical Significance (Discussion)

Suppose you find that α^=0.0003\hat{\alpha} = 0.0003 (0.03% per month) with a p-value of 0.04.

  1. Is this result statistically significant at the 5% level?

  2. Is it economically significant? (Hint: What does 0.03% per month translate to annually? Would an investor change their portfolio based on this?)

  3. What might happen to the p-value if you used Newey-West standard errors instead of classic OLS?

Exercise 5: Extending to Another Asset (Challenge)

Download return data for a stock or ETF of your choice and run the full Fama-French 3-factor analysis:

  1. Download the data using yfinance

  2. Compute monthly excess returns

  3. Run the regression with Newey-West standard errors

  4. Interpret the coefficients -- what does the asset’s factor exposure tell you about its risk characteristics?

  5. Check the diagnostic plots -- are the regression assumptions satisfied?

# Starter code for Exercise 5:
# Replace 'AAPL' with any US-listed ticker
import yfinance as yf
my_returns = monthly_returns(load_prices('AAPL')).loc['2010-01-01':]
# ... compute monthly returns, align with ff_factors, and run fama_french_analysis()

From three factors to the next question

You can now estimate exposures, report uncertainty, and distinguish within-sample fit from a pricing claim. If a value fund has nonzero alpha, one possibility is that the benchmark omits relevant exposures. Another is sampling noise.

Chapter 4 asks what profitability, investment and momentum add, beginning with the economics of valuation rather than simply adding regressors. Return to the solutions after attempting the exercises above.