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