# Load packages

# Core
library(tidyverse)
library(tidyquant)

Goal

Collect individual returns into a portfolio by assigning a weight to each stock

Choose your stocks.

from 2012-12-31 to 2017-12-31

1 Import stock prices

stocks <- c("MSFT", "JPM", "WMT", "DIS", "NKE") |>
  tq_get(get = "stock.prices",
         from = "2012-12-31",
         to = "2017-12-31")

stocks
## # A tibble: 6,300 × 8
##    symbol date        open  high   low close   volume adjusted
##    <chr>  <date>     <dbl> <dbl> <dbl> <dbl>    <dbl>    <dbl>
##  1 MSFT   2012-12-31  26.6  26.8  26.4  26.7 42749500     21.4
##  2 MSFT   2013-01-02  27.2  27.7  27.1  27.6 52899300     22.1
##  3 MSFT   2013-01-03  27.6  27.6  27.2  27.2 48294400     21.8
##  4 MSFT   2013-01-04  27.3  27.3  26.7  26.7 52521100     21.4
##  5 MSFT   2013-01-07  26.8  26.9  26.6  26.7 37110400     21.4
##  6 MSFT   2013-01-08  26.8  26.8  26.5  26.5 44703100     21.2
##  7 MSFT   2013-01-09  26.7  26.8  26.6  26.7 49047900     21.4
##  8 MSFT   2013-01-10  26.6  27.0  26.3  26.5 71431300     21.2
##  9 MSFT   2013-01-11  26.5  26.9  26.3  26.8 55512100     21.5
## 10 MSFT   2013-01-14  26.9  27.1  26.8  26.9 48324400     21.5
## # ℹ 6,290 more rows

2 Convert prices to returns (quarterly)

returns <- stocks |>
  group_by(symbol) |>
  tq_transmute(select = adjusted,
               mutate_fun = periodReturn,
               period = "quarterly",
               col_rename = "Ra")

returns
## # A tibble: 105 × 3
## # Groups:   symbol [5]
##    symbol date             Ra
##    <chr>  <date>        <dbl>
##  1 MSFT   2012-12-31  0      
##  2 MSFT   2013-03-28  0.0800 
##  3 MSFT   2013-06-28  0.216  
##  4 MSFT   2013-09-30 -0.0297 
##  5 MSFT   2013-12-31  0.133  
##  6 MSFT   2014-03-31  0.104  
##  7 MSFT   2014-06-30  0.0245 
##  8 MSFT   2014-09-30  0.119  
##  9 MSFT   2014-12-31  0.00826
## 10 MSFT   2015-03-31 -0.118  
## # ℹ 95 more rows

3 Assign a weight to each asset (change the weigting scheme)

weights <- c(0.30, 0.25, 0.20, 0.15, 0.10)

weights
## [1] 0.30 0.25 0.20 0.15 0.10

4 Build a portfolio

portfolio <- returns |>
  tq_portfolio(assets_col = symbol,
               returns_col = Ra,
               weights = weights,
               col_rename = "Rp")

portfolio
## # A tibble: 21 × 2
##    date             Rp
##    <date>        <dbl>
##  1 2012-12-31  0      
##  2 2013-03-28  0.102  
##  3 2013-06-28  0.119  
##  4 2013-09-30  0.00478
##  5 2013-12-31  0.128  
##  6 2014-03-31  0.0410 
##  7 2014-06-30  0.0124 
##  8 2014-09-30  0.0784 
##  9 2014-12-31  0.0535 
## 10 2015-03-31 -0.0255 
## # ℹ 11 more rows

5 Plot: Portfolio Histogram and Density

portfolio |>
  ggplot(aes(x = Rp)) +
  geom_histogram(aes(y = after_stat(density)),
                 binwidth = 0.01,
                 fill = "steelblue",
                 color = "white") +
  geom_density(color = "darkblue",
               linewidth = 1) +
  labs(title = "Portfolio Quarterly Returns",
       x = "Quarterly Return",
       y = "density")

What return should you expect from the portfolio in a typical quarter?

portfolio |>
  mutate(return_range = cut(Rp,
                            breaks = seq(-1, 1, by = 0.01))) |>
  count(return_range, sort = TRUE)
## # A tibble: 16 × 2
##    return_range      n
##    <fct>         <int>
##  1 (-0.03,-0.02]     2
##  2 (0.05,0.06]       2
##  3 (0.06,0.07]       2
##  4 (0.1,0.11]        2
##  5 (0.11,0.12]       2
##  6 (-0.04,-0.03]     1
##  7 (-0.02,-0.01]     1
##  8 (-0.01,0]         1
##  9 (0,0.01]          1
## 10 (0.01,0.02]       1
## 11 (0.02,0.03]       1
## 12 (0.03,0.04]       1
## 13 (0.04,0.05]       1
## 14 (0.07,0.08]       1
## 15 (0.12,0.13]       1
## 16 (0.15,0.16]       1

Based on the histogram and frequency table, there is no single clear typical quarterly return. The most frequent 1% return ranges were −3% to −2%, 5% to 6%, 6% to 7%, 10% to 11%, and 11% to 12%, each occurring twice. The positive returns clustered around 5%–7% and 10%–12%, with each cluster containing four of the 21 quarters. Overall, 15 quarters had positive returns and six had negative returns, so gains occurred more frequently than losses, but a specific return should not be expected every quarter.

The lowest observed return fell between −4% and −3%, while the highest fell between 15% and 16%. These show the historical downside and upside, but they are not limits on future returns. A risk-averse investor would focus on the frequency and size of losses, while an investor willing to accept more risk might find the higher positive returns attractive. I would use the entire distribution to evaluate whether the portfolio’s potential gains justify its risk.