# load packages
library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.4     ✔ readr     2.1.5
## ✔ forcats   1.0.0     ✔ stringr   1.5.1
## ✔ ggplot2   3.5.2     ✔ tibble    3.3.0
## ✔ lubridate 1.9.4     ✔ tidyr     1.3.1
## ✔ purrr     1.1.0     
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
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.28     ✔ 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

1 Get stock prices and convert to returns

Ra <- c("AAPL", "GOOG", "NFLX") %>% 
  tq_get("stock.prices",
         from = "10-01-01",
         to = "2015-12-31") %>%
  group_by("symbol") %>%
  tq_transmute(select = adjusted,
               mutate_fun = periodReturn,
               period = "monthly",
               col_rename = "Ra")
Ra
## # A tibble: 421 × 3
## # Groups:   "symbol" [1]
##    `"symbol"` date            Ra
##    <chr>      <date>       <dbl>
##  1 symbol     1980-12-31  0.187 
##  2 symbol     1981-01-30 -0.172 
##  3 symbol     1981-02-27 -0.0619
##  4 symbol     1981-03-31 -0.0755
##  5 symbol     1981-04-30  0.158 
##  6 symbol     1981-05-29  0.167 
##  7 symbol     1981-06-30 -0.215 
##  8 symbol     1981-07-31 -0.0385
##  9 symbol     1981-08-31 -0.195 
## 10 symbol     1981-09-30 -0.242 
## # ℹ 411 more rows

2 Get basline and convert to returns

Rb <- c("XLK") %>% 
  tq_get("stock.prices",
         from = "10-01-01",
         to = "2015-12-31") %>%
  group_by("symbol") %>%
  tq_transmute(select = adjusted,
               mutate_fun = periodReturn,
               period = "monthly",
               col_rename = "Rb")
Rb
## # A tibble: 205 × 3
## # Groups:   "symbol" [1]
##    `"symbol"` date             Rb
##    <chr>      <date>        <dbl>
##  1 symbol     1998-12-31  0.0180 
##  2 symbol     1999-01-29  0.159  
##  3 symbol     1999-02-26 -0.0992 
##  4 symbol     1999-03-31  0.0743 
##  5 symbol     1999-04-30  0.00598
##  6 symbol     1999-05-28  0.00340
##  7 symbol     1999-06-30  0.0960 
##  8 symbol     1999-07-30 -0.0116 
##  9 symbol     1999-08-31  0.0351 
## 10 symbol     1999-09-30 -0.00415
## # ℹ 195 more rows

3 Join the two tables

RaRb <- left_join(Ra, Rb, by = c("date" = "date"))
RaRb
## # A tibble: 421 × 5
##    `"symbol".x` date            Ra `"symbol".y`    Rb
##    <chr>        <date>       <dbl> <chr>        <dbl>
##  1 symbol       1980-12-31  0.187  <NA>            NA
##  2 symbol       1981-01-30 -0.172  <NA>            NA
##  3 symbol       1981-02-27 -0.0619 <NA>            NA
##  4 symbol       1981-03-31 -0.0755 <NA>            NA
##  5 symbol       1981-04-30  0.158  <NA>            NA
##  6 symbol       1981-05-29  0.167  <NA>            NA
##  7 symbol       1981-06-30 -0.215  <NA>            NA
##  8 symbol       1981-07-31 -0.0385 <NA>            NA
##  9 symbol       1981-08-31 -0.195  <NA>            NA
## 10 symbol       1981-09-30 -0.242  <NA>            NA
## # ℹ 411 more rows

4 Calculate CAPM

RaRb_capm <- RaRb %>% 
  tq_performance(Ra = Ra,
                 Rb = Rb,
                 performance_fun = table.CAPM)
## Registered S3 method overwritten by 'robustbase':
##   method          from     
##   hatvalues.lmrob RobStatTM
RaRb_capm 
## # A tibble: 1 × 17
##   ActivePremium  Alpha AlphaRobust AnnualizedAlpha  Beta `Beta-` `Beta-Robust`
##           <dbl>  <dbl>       <dbl>           <dbl> <dbl>   <dbl>         <dbl>
## 1         0.408 0.0473      0.0363           0.742 0.910    1.21          1.03
## # ℹ 10 more variables: `Beta+` <dbl>, `Beta+Robust` <dbl>, BetaRobust <dbl>,
## #   Correlation <dbl>, `Correlationp-value` <dbl>, InformationRatio <dbl>,
## #   `R-squared` <dbl>, `R-squaredRobust` <dbl>, TrackingError <dbl>,
## #   TreynorRatio <dbl>