# Load packages

# Core
library(tidyverse)
library(tidyquant)
library(ggrepel)

Goal

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

Choose your stocks.

from 2020-12-31 to 2026-10-08

1 Import stock prices

symbols <- c("TSM", "MU", "NOW", "MRVL", "PLTR")

prices <- tq_get(x = symbols,
                 get = "stock.prices",
                 from = "2020-12-31",
                 to = "2026-10-08")
prices
## # A tibble: 7,240 × 8
##    symbol date        open  high   low close   volume adjusted
##    <chr>  <date>     <dbl> <dbl> <dbl> <dbl>    <dbl>    <dbl>
##  1 TSM    2020-12-31  110.  110.  108.  109.  4909500     99.5
##  2 TSM    2021-01-04  111.  114.  110.  112. 11262100    102. 
##  3 TSM    2021-01-05  112.  115.  112.  113. 10583600    103. 
##  4 TSM    2021-01-06  114.  116.  113.  116. 10609300    105. 
##  5 TSM    2021-01-07  119.  123.  118.  121. 13556100    111. 
##  6 TSM    2021-01-08  126.  126.  117.  119. 18976800    108. 
##  7 TSM    2021-01-11  120.  124.  119.  123. 12006800    112. 
##  8 TSM    2021-01-12  125.  125.  122.  123  14163200    112. 
##  9 TSM    2021-01-13  124.  125.  118   119. 20650500    109. 
## 10 TSM    2021-01-14  123.  135.  122.  126. 37125400    115. 
## # ℹ 7,230 more rows

2 Convert prices to returns (monthly)

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

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

symbols <- asset_returns_tbl2 %>% distinct(asset) %>% pull

symbols
## [1] "MRVL" "MU"   "NOW"  "PLTR" "TSM"
weight <- c(0.15, 0.20, 0.30, 0.25, 0.10)
weight
## [1] 0.15 0.20 0.30 0.25 0.10
weight_tbl2 <- tibble(symbols, weight)
weight_tbl2
## # A tibble: 5 × 2
##   symbols weight
##   <chr>    <dbl>
## 1 MRVL      0.15
## 2 MU        0.2 
## 3 NOW       0.3 
## 4 PLTR      0.25
## 5 TSM       0.1

4 Build a portfolio

library(PerformanceAnalytics)

portfolio_returns_tbl2 <- 
    asset_returns_tbl2 %>%
    
    tq_portfolio(assets_col = asset,
                 returns_col = returns,
                 weights = weight_tbl2,
                 rebalence_on = "months")

portfolio_returns_tbl2
## # A tibble: 70 × 2
##    date       portfolio.returns
##    <date>                 <dbl>
##  1 2021-01-29            0.127 
##  2 2021-02-26           -0.102 
##  3 2021-03-31           -0.0373
##  4 2021-04-30           -0.0186
##  5 2021-05-28           -0.0149
##  6 2021-06-30            0.106 
##  7 2021-07-30           -0.0408
##  8 2021-08-31            0.0589
##  9 2021-09-30           -0.0457
## 10 2021-10-29            0.0731
## # ℹ 60 more rows

5 Compute Standard Deviation

portfolio_sd_tidyquant_bulitin_percent2 <- portfolio_returns_tbl2 %>%
    
    tq_performance(Ra = portfolio.returns, performance_fun = table.Stats) %>%
    
    select(Stdev) %>%
    mutate(tq_sd = round(Stdev, 4))

portfolio_sd_tidyquant_bulitin_percent2
## # A tibble: 1 × 2
##   Stdev tq_sd
##   <dbl> <dbl>
## 1 0.101 0.101
portfolio_mean_tidyquant_bulitin_percent2 <- mean(portfolio_returns_tbl2$portfolio.returns)

portfolio_mean_tidyquant_bulitin_percent2
## [1] 0.01944229

6 Plot: Expected Returns versus Risk

Expected return vs Risk

sd_mean_tbl2 <- asset_returns_tbl2 %>%
    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_bulitin_percent2,
                   Stdev = portfolio_sd_tidyquant_bulitin_percent2$tq_sd))

sd_mean_tbl2
## # A tibble: 6 × 3
##   asset       Mean  Stdev
##   <chr>      <dbl>  <dbl>
## 1 MRVL      0.0259 0.171 
## 2 MU        0.0385 0.162 
## 3 NOW       0.0032 0.107 
## 4 PLTR      0.0301 0.196 
## 5 TSM       0.0222 0.0969
## 6 Portfolio 0.0194 0.101
sd_mean_tbl2 %>%
    
    ggplot(aes(x = Stdev, y = Mean, colour = asset)) +
    geom_point() +
    ggrepel::geom_text_repel(aes(label = asset))

24 Months Rolling Volatility

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

rolling_sd_tbl2
## # A tibble: 47 × 2
##    date       rolling_sd
##    <date>          <dbl>
##  1 2022-12-30     0.0845
##  2 2023-01-31     0.0887
##  3 2023-02-28     0.0871
##  4 2023-03-31     0.0880
##  5 2023-04-28     0.0880
##  6 2023-05-31     0.100 
##  7 2023-06-30     0.0971
##  8 2023-07-31     0.0988
##  9 2023-08-31     0.0985
## 10 2023-09-29     0.0984
## # ℹ 37 more rows
rolling_sd_tbl2 %>%
    
    ggplot(aes(x = date, y = rolling_sd)) +
    geom_line(color = "violet") +
    
    #Formatting
    scale_y_continuous(labels = scales::percent_format()) +
    
    # Labeling
    
    labs(x = NULL,
         y = NULL,
         title = "24 - Months Rolling Volatility") +
    theme(plot.title = element_text(hjust = 0.5))

How should you expect your portfolio to perform relative to its assets in the portfolio? Would you invest all your money in any of the individual stocks instead of the portfolio? Discuss both in terms of expected return and risk.

When looking at the portfolio return isolated from risk, the portfolio generates a relative low return compared to majority of the assets in the portfolio. As graphic 1 shows, stock “NOW” is pulling down the return quite significantly. I will however address that a 2% monthly return is not bad at all, and would be beating the S&P50, by growing 24% in the period of 12 months.

The portfolio do however, have a low standard deviation compared to the holdings, where there are the stocks TSM and NOW that provides this diversification, and reduction in Stdev. The highest yielding stocks MU, PLTR, and MRVL have almost twice as large deviation. That means that one could feel more comfortable / secure in the portfolio yielding its average return, vs. the three top stocks.

There is primarily two stocks that could compare with the portfolio. 1. TSM: This is a clear choose, as the Stdev is lower than the portfolio, meaning it is less risky, while its return is higher. 2. MU: This stock have a much higher risk, but also yields twice the return, meaning that it technically would grow by 48% on a yearly basis. What I realistically would do, is to remove NOW from the portfolio, and rather up the %share of TSM and MU, to maximize the return and Stdev. See bellow for illustration.

#Adjusted Portfolio

1 Import stock prices

symbols_A <- c("TSM", "MU", "MRVL", "PLTR")

prices_A <- tq_get(x = symbols_A,
                 get = "stock.prices",
                 from = "2020-12-31",
                 to = "2026-10-08")
prices_A
## # A tibble: 5,792 × 8
##    symbol date        open  high   low close   volume adjusted
##    <chr>  <date>     <dbl> <dbl> <dbl> <dbl>    <dbl>    <dbl>
##  1 TSM    2020-12-31  110.  110.  108.  109.  4909500     99.5
##  2 TSM    2021-01-04  111.  114.  110.  112. 11262100    102. 
##  3 TSM    2021-01-05  112.  115.  112.  113. 10583600    103. 
##  4 TSM    2021-01-06  114.  116.  113.  116. 10609300    105. 
##  5 TSM    2021-01-07  119.  123.  118.  121. 13556100    111. 
##  6 TSM    2021-01-08  126.  126.  117.  119. 18976800    108. 
##  7 TSM    2021-01-11  120.  124.  119.  123. 12006800    112. 
##  8 TSM    2021-01-12  125.  125.  122.  123  14163200    112. 
##  9 TSM    2021-01-13  124.  125.  118   119. 20650500    109. 
## 10 TSM    2021-01-14  123.  135.  122.  126. 37125400    115. 
## # ℹ 5,782 more rows

2 Convert prices to returns (monthly)

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

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

symbols_A <- asset_returns_tbl_A %>% distinct(asset) %>% pull

symbols_A
## [1] "MRVL" "MU"   "PLTR" "TSM"
weight_A <- c(0.35, 0.30, 0.25, 0.10)
weight_A
## [1] 0.35 0.30 0.25 0.10
weight_tbl_A <- tibble(symbols_A, weight_A)
weight_tbl_A
## # A tibble: 4 × 2
##   symbols_A weight_A
##   <chr>        <dbl>
## 1 MRVL          0.35
## 2 MU            0.3 
## 3 PLTR          0.25
## 4 TSM           0.1

4 Build a portfolio

library(PerformanceAnalytics)

portfolio_returns_tbl_A <- 
    asset_returns_tbl_A %>%
    
    tq_portfolio(assets_col = asset,
                 returns_col = returns,
                 weights = weight_tbl_A,
                 rebalence_on = "months")

portfolio_returns_tbl_A
## # A tibble: 70 × 2
##    date       portfolio.returns
##    <date>                 <dbl>
##  1 2021-01-29            0.151 
##  2 2021-02-26           -0.0927
##  3 2021-03-31           -0.0197
##  4 2021-04-30           -0.0396
##  5 2021-05-28            0.0141
##  6 2021-06-30            0.102 
##  7 2021-07-30           -0.0578
##  8 2021-08-31            0.0270
##  9 2021-09-30           -0.0414
## 10 2021-10-29            0.0639
## # ℹ 60 more rows

5 Compute Standard Deviation

portfolio_sd_tidyquant_bulitin_percent_A <- portfolio_returns_tbl_A %>%
    
    tq_performance(Ra = portfolio.returns, performance_fun = table.Stats) %>%
    
    select(Stdev) %>%
    mutate(tq_sd = round(Stdev, 4))

portfolio_sd_tidyquant_bulitin_percent_A
## # A tibble: 1 × 2
##   Stdev tq_sd
##   <dbl> <dbl>
## 1 0.123 0.123
portfolio_mean_tidyquant_bulitin_percent_A <- mean(portfolio_returns_tbl_A$portfolio.returns)

portfolio_mean_tidyquant_bulitin_percent_A
## [1] 0.02568467

6 Plot: Expected Returns versus Risk

Expected return vs Risk

sd_mean_tbl_A <- asset_returns_tbl_A %>%
    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_bulitin_percent_A,
                   Stdev = portfolio_sd_tidyquant_bulitin_percent_A$tq_sd))

sd_mean_tbl_A
## # A tibble: 5 × 3
##   asset       Mean  Stdev
##   <chr>      <dbl>  <dbl>
## 1 MRVL      0.0259 0.171 
## 2 MU        0.0385 0.162 
## 3 PLTR      0.0301 0.196 
## 4 TSM       0.0222 0.0969
## 5 Portfolio 0.0257 0.123
sd_mean_tbl_A %>%
    
    ggplot(aes(x = Stdev, y = Mean, colour = asset)) +
    geom_point() +
    ggrepel::geom_text_repel(aes(label = asset))