Load Libraries

library(dplyr)
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
library(tidyr)
library(tidyquant)
## Registered S3 method overwritten by 'quantmod':
##   method            from
##   as.zoo.data.frame zoo
## ── Attaching core tidyquant packages ─────────────────────── tidyquant 1.0.11 ──
## ✔ PerformanceAnalytics 2.0.8      ✔ TTR                  0.24.4
## ✔ quantmod             0.4.26     ✔ xts                  0.14.1
## ── Conflicts ────────────────────────────────────────── tidyquant_conflicts() ──
## ✖ zoo::as.Date()                 masks base::as.Date()
## ✖ zoo::as.Date.numeric()         masks base::as.Date.numeric()
## ✖ dplyr::filter()                masks stats::filter()
## ✖ xts::first()                   masks dplyr::first()
## ✖ dplyr::lag()                   masks stats::lag()
## ✖ xts::last()                    masks dplyr::last()
## ✖ PerformanceAnalytics::legend() masks graphics::legend()
## ✖ quantmod::summary()            masks base::summary()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(readr)
library(ggplot2)

Load Data

CAPM Data

capm_data <- read_csv("capm.csv")
## Rows: 2363 Columns: 6
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (1): Date
## dbl (5): Close-tbill, Close-sp500, Close-msft, Close-ge, Close-ford
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
head(capm_data)
## # A tibble: 6 × 6
##   Date      `Close-tbill` `Close-sp500` `Close-msft` `Close-ge` `Close-ford`
##   <chr>             <dbl>         <dbl>        <dbl>      <dbl>        <dbl>
## 1 1993/11/1          3.06          469.         2.52       6.72         8.16
## 2 1993/11/2          3.12          468.         2.5        6.69         8.31
## 3 1993/11/3          3.08          463.         2.45       6.64         8.24
## 4 1993/11/4          3.07          457.         2.38       6.46         7.98
## 5 1993/11/5          3.07          460.         2.45       6.49         7.94
## 6 1993/11/8          3.06          460.         2.45       6.46         8.02

Fama-French Data

fama_data <- read_csv("FamaFrench_mon_69_98_3stocks.csv")
## Rows: 360 Columns: 9
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## dbl (9): date, Mkt-RF, SMB, HML, RF, ge, ibm, mobil, CRSP
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
head(fama_data)
## # A tibble: 6 × 9
##     date `Mkt-RF`   SMB   HML    RF    ge    ibm  mobil   CRSP
##    <dbl>    <dbl> <dbl> <dbl> <dbl> <dbl>  <dbl>  <dbl>  <dbl>
## 1 196901    -1.2  -0.8   1.57  0.53 -1.20 -5.95   -1.40 -0.671
## 2 196902    -5.82 -3.9   0.93  0.46 -6.04 -0.700  -7.84 -5.36 
## 3 196903     2.59 -0.28 -0.45  0.46  6.65  7.03   21.5   3.05 
## 4 196904     1.52 -0.85  0.06  0.53  5.96  4.46    3.00  2.05 
## 5 196905     0.02 -0.27  0.74  0.48 -3.58 -2.5     2.67  0.504
## 6 196906    -7.25 -5.31 -1.15  0.51 -3.82  5.88  -13.0  -6.74

CAPM Model Implementation

capm_dat <- capm_data %>% 
  mutate(Date = as.Date(Date, format = "%Y/%m/%d")) %>% 
  filter(Date >= as.Date("1993-11-01") & Date <= as.Date("1998-11-30")) %>% 
  rename(rf = `Close-tbill`, sp500 = `Close-sp500`, msft = `Close-msft`, ge = `Close-ge`, ford = `Close-ford`) %>% 
  mutate(rf = rf / (100 * 360))

# Compute excess returns
capm_ret <- capm_dat %>% select(-rf) %>% 
  gather(key = stock, value = price, -Date) %>%
  group_by(stock) %>%
  tq_transmute(mutate_fun = periodReturn, period = "daily", type = "arithmetic", col_rename = "daily.returns") %>% 
  ungroup() %>%
  spread(stock, daily.returns) %>% 
  bind_cols(., rf = capm_dat$rf) %>%
  mutate(ford_rf = ford - rf, ge_rf = ge - rf, msft_rf = msft - rf, sp500_rf = sp500 - rf) %>%
  slice(-1) %>%
  select(Date, ends_with("_rf"))

# Run CAPM Regression
capm_reg <- lm(cbind(msft_rf, ge_rf, ford_rf) ~ sp500_rf, data = capm_ret)
summary(capm_reg)
## Response msft_rf :
## 
## Call:
## lm(formula = msft_rf ~ sp500_rf, data = capm_ret)
## 
## Residuals:
##       Min        1Q    Median        3Q       Max 
## -0.056456 -0.010372 -0.001143  0.009762  0.087597 
## 
## Coefficients:
##              Estimate Std. Error t value Pr(>|t|)    
## (Intercept) 0.0012339  0.0004747   2.599  0.00945 ** 
## sp500_rf    1.2823822  0.0531051  24.148  < 2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.01692 on 1275 degrees of freedom
## Multiple R-squared:  0.3138, Adjusted R-squared:  0.3133 
## F-statistic: 583.1 on 1 and 1275 DF,  p-value: < 2.2e-16
## 
## 
## Response ge_rf :
## 
## Call:
## lm(formula = ge_rf ~ sp500_rf, data = capm_ret)
## 
## Residuals:
##       Min        1Q    Median        3Q       Max 
## -0.032199 -0.006674 -0.000136  0.006388  0.036949 
## 
## Coefficients:
##              Estimate Std. Error t value Pr(>|t|)    
## (Intercept) 0.0003448  0.0002684   1.285    0.199    
## sp500_rf    1.1981993  0.0300256  39.906   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.009569 on 1275 degrees of freedom
## Multiple R-squared:  0.5554, Adjusted R-squared:  0.555 
## F-statistic:  1592 on 1 and 1275 DF,  p-value: < 2.2e-16
## 
## 
## Response ford_rf :
## 
## Call:
## lm(formula = ford_rf ~ sp500_rf, data = capm_ret)
## 
## Residuals:
##       Min        1Q    Median        3Q       Max 
## -0.063903 -0.009529 -0.001148  0.008793  0.075596 
## 
## Coefficients:
##              Estimate Std. Error t value Pr(>|t|)    
## (Intercept) 0.0002996  0.0004423   0.677    0.498    
## sp500_rf    1.0250995  0.0494758  20.719   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.01577 on 1275 degrees of freedom
## Multiple R-squared:  0.2519, Adjusted R-squared:  0.2513 
## F-statistic: 429.3 on 1 and 1275 DF,  p-value: < 2.2e-16

Fama-French One-Factor Model

ff_data <- fama_data %>%
  rename(Mkt_RF = `Mkt-RF`) %>%
  select(c(2,6,7,8)) / 100

# Run Regression
ff_reg <- lm(cbind(ge, ibm, mobil) ~ Mkt_RF, data = ff_data)
summary(ff_reg)
## Response ge :
## 
## Call:
## lm(formula = ge ~ Mkt_RF, data = ff_data)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -0.09824 -0.02921 -0.00327  0.02924  0.11613 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept) 0.009381   0.002188   4.288 2.32e-05 ***
## Mkt_RF      1.058083   0.047351  22.345  < 2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.04126 on 358 degrees of freedom
## Multiple R-squared:  0.5824, Adjusted R-squared:  0.5813 
## F-statistic: 499.3 on 1 and 358 DF,  p-value: < 2.2e-16
## 
## 
## Response ibm :
## 
## Call:
## lm(formula = ibm ~ Mkt_RF, data = ff_data)
## 
## Residuals:
##       Min        1Q    Median        3Q       Max 
## -0.279847 -0.035451 -0.000568  0.029353  0.187294 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept) 0.005717   0.003011   1.899   0.0584 .  
## Mkt_RF      0.814995   0.065180  12.504   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.0568 on 358 degrees of freedom
## Multiple R-squared:  0.304,  Adjusted R-squared:  0.302 
## F-statistic: 156.3 on 1 and 358 DF,  p-value: < 2.2e-16
## 
## 
## Response mobil :
## 
## Call:
## lm(formula = mobil ~ Mkt_RF, data = ff_data)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -0.12533 -0.03520 -0.00115  0.02579  0.36312 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept) 0.009487   0.002862   3.315  0.00101 ** 
## Mkt_RF      0.815807   0.061943  13.170  < 2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.05398 on 358 degrees of freedom
## Multiple R-squared:  0.3264, Adjusted R-squared:  0.3245 
## F-statistic: 173.5 on 1 and 358 DF,  p-value: < 2.2e-16