8.1 Fama-French Three Factor Model
Fama and French (1992);Fama and French (1993) extended the basic CAPM to include size and book-to-market effects as explanatory factors in explaining the cross-section of stock returns.
SMB (Small minus Big) gives the size premium which is the additional return received by investors from investing in companies having a low market capitalization.
HML (High minus Low), gives the value premium which is the return provided to investors for investing in companies having high book-to-market values.
The three factor Fama-French model is written as:
\[\begin{equation} r_{A}-r_{F}=+\beta_{A}(r_{M}-r_{F})+s_{A}SMB+h_{A}HML+\alpha+e \#eq:ff1) \end{equation}\]
Where \(s_{A}\) and \(h_{A}\) capture the security’s sensitivity to these two additional factors.
8.1.1 Data Preprocessing
- The three factors daily data is downloaded as a CSV file from the Kenneth French website. Link: https://mba.tuck.dartmouth.edu/pages/faculty/ken.french/data_library.html
- AAPL stock prices are downloaded using the quantmod package
- The following code snippets will pre-process three factor data and stock return data and then combine it in one single
# use read.table for text file
= read.csv("data/F-F_Research_Data_Factors_daily.CSV", skip = 3)
ff_data = na.omit(ff_data) #remove missing values
ff_data
head(ff_data) #date is missing column names
X Mkt.RF SMB HML RF
1 19260701 0.10 -0.23 -0.28 0.009
2 19260702 0.45 -0.34 -0.03 0.009
3 19260706 0.17 0.29 -0.38 0.009
4 19260707 0.09 -0.59 0.00 0.009
5 19260708 0.21 -0.38 0.18 0.009
6 19260709 -0.71 0.44 0.58 0.009
colnames(ff_data)[1] = "Date"
# convert dates in R date format
$Date = as.Date(strptime(ff_data$Date, format = "%Y%m%d"))
ff_datahead(ff_data)
Date Mkt.RF SMB HML RF
1 1926-07-01 0.10 -0.23 -0.28 0.009
2 1926-07-02 0.45 -0.34 -0.03 0.009
3 1926-07-06 0.17 0.29 -0.38 0.009
4 1926-07-07 0.09 -0.59 0.00 0.009
5 1926-07-08 0.21 -0.38 0.18 0.009
6 1926-07-09 -0.71 0.44 0.58 0.009
- Download the data and convert to returns
= getSymbols("AAPL", from = "2019-01-01", to = "2021-09-30", auto.assign = F)
d_aapl # select closing prices and covert to log returns
= d_aapl$AAPL.Close
aapl = dailyReturn(aapl, type = "log")
aapl_ret
# convert to data frame
= fortify.zoo(aapl_ret) #Dates column will be named Index
aapl_ret2
# rename
colnames(aapl_ret2) = c("Date", "AAPL")
# use merge (can use left_join from dplyr as well) to combine the
# stock returns and factor data
= merge(aapl_ret2, ff_data, by = "Date") data_ffex
8.1.2 Regression Analysis
- The Fama-French regression uses excess returns so first convert Apple returns to excess returns and then fit the model using the
lm
function
# create another column with AAPL-RF
$AAPL.Rf = data_ffex$AAPL - data_ffex$RF
data_ffex
= lm(AAPL.Rf ~ Mkt.RF + SMB + HML, data = data_ffex) ff_lreg
- A plot function can be used to plot the four regression plots similar to simple regression.
par(mfrow = c(2, 2))
plot(ff_lreg)

Figure 8.1: Linear Regression Plots
- There are packages in R which provide functions to export the summary output of a regression model in a LaTeX, HTML or ASCII files.
- The following code uses function to print the OLS results in ASCII format.
- The same output can also be exported to an HTML or LaTeX file which can be later used in a word/LaTeX document.
stargazer(ff_lreg, summary = T, title = "Fama-French Regression OLS", type = "html")
Dependent variable: | |
AAPL.Rf | |
Mkt.RF | 0.013*** |
(0.0003) | |
SMB | -0.003*** |
(0.001) | |
HML | -0.004*** |
(0.0004) | |
Constant | -0.003*** |
(0.0005) | |
Observations | 692 |
R2 | 0.677 |
Adjusted R2 | 0.676 |
Residual Std. Error | 0.013 (df = 688) |
F Statistic | 481.374*** (df = 3; 688) |
Note: | p<0.1; p<0.05; p<0.01 |
8.1.3 Visualisation
- The ggeffects package provides good functionality on visualising the marginal effects and adjusted predictions. The predictions generated by a model by varying one independent variable and keeping the others constant.
- The following example visualises the predictions based on the SMB factor
library(ggeffects)
= ggpredict(ff_lreg, terms = c("SMB"))
mydf p_ff = plot(mydf) + geom_point(data = data_ffex, aes(x = SMB, y = AAPL.Rf),
(color = "darkblue"))

Figure 8.2: Marginal Effect