Step 1: Import Stock Prices

library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.2.1     ✔ readr     2.2.0
## ✔ forcats   1.0.1     ✔ stringr   1.6.0
## ✔ ggplot2   4.0.3     ✔ tibble    3.3.1
## ✔ lubridate 1.9.5     ✔ tidyr     1.3.2
## ✔ purrr     1.2.2     
## ── 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.12 ──
## ✔ PerformanceAnalytics 2.1.0      ✔ TTR                  0.24.4
## ✔ quantmod             0.4.29     ✔ xts                  0.14.2── 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
symbols <- c("SPY", "EFA", "IJS", "EEM", "AGG")

prices <- tq_get(
  x    = symbols,
  get  = "stock.prices",
  from = "2012-01-01",
  to   = "2017-01-01"
)

prices
## # A tibble: 6,290 × 8
##    symbol date        open  high   low close    volume adjusted
##    <chr>  <date>     <dbl> <dbl> <dbl> <dbl>     <dbl>    <dbl>
##  1 SPY    2012-01-03  128.  128.  127.  128. 193697900     98.8
##  2 SPY    2012-01-04  127.  128.  127.  128. 127186500     99.0
##  3 SPY    2012-01-05  127.  128.  126.  128. 173895000     99.2
##  4 SPY    2012-01-06  128.  128.  127.  128. 148050000     99.0
##  5 SPY    2012-01-09  128   128.  127.  128.  99530200     99.2
##  6 SPY    2012-01-10  129.  130.  129.  129. 115282000    100. 
##  7 SPY    2012-01-11  129.  129.  129.  129. 111540700    100. 
##  8 SPY    2012-01-12  130.  130.  129.  130. 118983700    100. 
##  9 SPY    2012-01-13  129.  129.  128.  129. 179836200     99.9
## 10 SPY    2012-01-17  130.  130.  129.  129. 132209200    100. 
## # ℹ 6,280 more rows

Step 2: Convert Prices to Returns

asset_returns_tbl <- prices %>%
  group_by(symbol) %>%
  tq_transmute(
    select     = adjusted,
    mutate_fun = periodReturn,
    period     = "monthly",
    type       = "log"
  ) %>%
  set_names(c("asset", "date", "returns")) %>%
  ungroup()

asset_returns_tbl
## # A tibble: 300 × 3
##    asset date        returns
##    <chr> <date>        <dbl>
##  1 SPY   2012-01-31  0.0295 
##  2 SPY   2012-02-29  0.0425 
##  3 SPY   2012-03-30  0.0317 
##  4 SPY   2012-04-30 -0.00670
##  5 SPY   2012-05-31 -0.0619 
##  6 SPY   2012-06-29  0.0398 
##  7 SPY   2012-07-31  0.0118 
##  8 SPY   2012-08-31  0.0247 
##  9 SPY   2012-09-28  0.0250 
## 10 SPY   2012-10-31 -0.0184 
## # ℹ 290 more rows

Step 3: Plot Returns

asset_returns_tbl %>%
  ggplot(aes(x = returns)) +
  geom_histogram(aes(fill = asset), binwidth = 0.01, alpha = 0.3, show.legend = FALSE) +
  geom_density(aes(color = asset), show.legend = FALSE) +
  facet_wrap(~asset, ncol = 1) +
  labs(
    title    = "Distribution of Monthly Returns, 2012–2016",
    x        = "Monthly Returns",
    y        = "Frequency",
    caption  = "Monthly return is higher for SPY and IJS than for AGG and EEM."
  )