Goal

Visualize expected returns and risk to make it easier to compare the performance of multiple assets and portfolios.


1. Import Stock Prices

We will analyze performance from 2012-12-31 to 2017-12-31.

symbols <- c("NVDA", "INTC", "GOOG", "AMD", "AAPL")

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

head(prices)
## # A tibble: 6 × 8
##   symbol date        open  high   low close    volume adjusted
##   <chr>  <date>     <dbl> <dbl> <dbl> <dbl>     <dbl>    <dbl>
## 1 NVDA   2012-12-31 0.301 0.308 0.301 0.306 326460000    0.283
## 2 NVDA   2013-01-02 0.314 0.318 0.313 0.318 478836000    0.293
## 3 NVDA   2013-01-03 0.318 0.322 0.315 0.318 298888000    0.294
## 4 NVDA   2013-01-04 0.319 0.330 0.318 0.329 524968000    0.303
## 5 NVDA   2013-01-07 0.329 0.329 0.317 0.319 610732000    0.295
## 6 NVDA   2013-01-08 0.320 0.321 0.310 0.312 466424000    0.288

2. Convert Prices to Returns

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

head(asset_returns_tbl)
## # A tibble: 6 × 3
##   asset date       returns
##   <chr> <date>       <dbl>
## 1 AAPL  2013-03-28 -0.178 
## 2 AAPL  2013-06-28 -0.103 
## 3 AAPL  2013-09-30  0.191 
## 4 AAPL  2013-12-31  0.169 
## 5 AAPL  2014-03-31 -0.0383
## 6 AAPL  2014-06-30  0.198

3. Assign Weights to Each Asset

symbols <- asset_returns_tbl %>% distinct(asset) %>% pull()
weights <- c(0.20, 0.15, 0.15, 0.30, 0.20)

w_tbl <- tibble(symbols, weights)
w_tbl
## # A tibble: 5 × 2
##   symbols weights
##   <chr>     <dbl>
## 1 AAPL       0.2 
## 2 AMD        0.15
## 3 GOOG       0.15
## 4 INTC       0.3 
## 5 NVDA       0.2

4. Build Portfolio

portfolio_returns_tbl <- asset_returns_tbl %>%
    tq_portfolio(assets_col     = asset,
                 returns_col    = returns,
                 weights        = w_tbl, 
                 rebalance_on   = "quarters")

head(portfolio_returns_tbl)
## # A tibble: 6 × 2
##   date       portfolio.returns
##   <date>                 <dbl>
## 1 2013-03-28            0.0216
## 2 2013-06-28            0.118 
## 3 2013-09-30            0.0349
## 4 2013-12-31            0.120 
## 5 2014-03-31            0.0212
## 6 2014-06-30            0.115

5. Calculate Standard Deviation and Mean of Portfolio Returns

# Portfolio Standard Deviation
portfolio_sd_tidyquant_builtin_percent <- portfolio_returns_tbl %>%
    tq_performance(Ra = portfolio.returns, 
                   performance_fun = table.Stats) %>%
    select(Stdev) %>%
    mutate(tq_sd = round(Stdev, 4))

portfolio_sd_tidyquant_builtin_percent
## # A tibble: 1 × 2
##    Stdev  tq_sd
##    <dbl>  <dbl>
## 1 0.0722 0.0722
# Portfolio Mean Return
portfolio_mean_tidyquant_builtin_percent <- 
  mean(portfolio_returns_tbl$portfolio.returns)
portfolio_mean_tidyquant_builtin_percent
## [1] 0.07085562

6. Plot Expected Returns vs Risk

sd_mean_tbl <- asset_returns_tbl %>%
    group_by(asset) %>%
    tq_performance(Ra = returns,
                   performance_fun = table.Stats) %>%
    select(Mean = ArithmeticMean, Stdev) %>%
    ungroup() %>%
    add_row(tibble(asset = "Portfolio", 
                   Mean = portfolio_mean_tidyquant_builtin_percent,
                   Stdev = portfolio_sd_tidyquant_builtin_percent$tq_sd))
## Adding missing grouping variables: `asset`
sd_mean_tbl
## # A tibble: 6 × 3
##   asset       Mean  Stdev
##   <chr>      <dbl>  <dbl>
## 1 AAPL      0.045  0.119 
## 2 AMD       0.0727 0.274 
## 3 GOOG      0.0544 0.0905
## 4 INTC      0.0483 0.0948
## 5 NVDA      0.141  0.134 
## 6 Portfolio 0.0709 0.0722
# Plot
sd_mean_tbl %>%
    ggplot(aes(x = Stdev, y = Mean, color = asset)) +
    geom_point(size = 3) +
    geom_text(aes(label = asset), vjust = 1.5, hjust = 0.5, size = 4) +
    labs(title = "Expected Returns vs Risk (2012–2017)",
         x = "Standard Deviation (Risk)",
         y = "Mean Quarterly Return") +
    theme_minimal()


7. 24-Month (8-Quarter) Rolling Volatility

rolling_sd_tbl <- portfolio_returns_tbl %>%
    tq_mutate(select     = portfolio.returns, 
              mutate_fun = rollapply, 
              width      = 8,
              FUN        = sd,
              col_rename = "rolling_sd") %>%
    na.omit() %>%
    select(date, rolling_sd)

rolling_sd_tbl %>% 
   ggplot(aes(x = date, y = rolling_sd)) +
    geom_line(color = "cornflowerblue") +
    scale_y_continuous(labels = scales::percent_format()) +
    labs(x = NULL,
         y = NULL,
         title = "8-Quarter Rolling Volatility") +
    theme(plot.title = element_text(hjust = 0.5))


Interpretation

The visualization shows the trade-off between expected return and risk for each stock and the overall portfolio.