# Load packages

# Core
library(tidyverse)
library(tidyquant)

Goal

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

Choose your stocks.

1 Import stock prices

symbols <- c("Asker.st", "Atco-B.st", "Axfo.st", "Bahn-b.st", "BRK-B", "Cers", "LLY", "Embrac-b.st", "Indu-c.st", "Inve-b.st", "Inwi.st", "Novo-b.co", "NVDA", "Yubico.st")

prices <- tq_get(x    = symbols, 
                 get  = "stock.prices", 
                 from = "2020-04-01",
                 to   = "2025-06-01")
prices
## # A tibble: 16,672 Ă— 8
##    symbol   date        open  high   low close   volume adjusted
##    <chr>    <date>     <dbl> <dbl> <dbl> <dbl>    <dbl>    <dbl>
##  1 Asker.st 2025-03-27  83    87.2  80.2  83.7 16441271     83.7
##  2 Asker.st 2025-03-28  83    84.0  81.7  82    1262083     82  
##  3 Asker.st 2025-03-31  81.3  81.9  80.1  80.5   626988     80.5
##  4 Asker.st 2025-04-01  80.8  82.2  80.6  81.9   356628     81.9
##  5 Asker.st 2025-04-02  81.9  82.1  80.9  82.1   576561     82.1
##  6 Asker.st 2025-04-03  81    81.8  80.1  80.7   235131     80.7
##  7 Asker.st 2025-04-04  80.5  81.2  77.3  78.6   780928     78.6
##  8 Asker.st 2025-04-07  74.8  80.1  71.4  77.8   377461     77.8
##  9 Asker.st 2025-04-08  79.2  79.9  75    77     371563     77  
## 10 Asker.st 2025-04-09  76.1  78.7  72.8  74.2  1171607     74.2
## # ℹ 16,662 more rows

2 Convert prices to returns (monthly)

asset_returns_tbl <- 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_tbl %>% distinct(asset) %>% pull() 
symbols
##  [1] "Asker.st"    "Atco-B.st"   "Axfo.st"     "BRK-B"       "Bahn-b.st"  
##  [6] "Cers"        "Embrac-b.st" "Indu-c.st"   "Inve-b.st"   "Inwi.st"    
## [11] "LLY"         "NVDA"        "Novo-b.co"   "Yubico.st"
weights <- c(0.0314, 0.0133, 0.0136, 0.0589, 0.0112, 0.0068, 0.0201, 0.1858, 0.2298, 0.0584, 0.0892, 0.2504, 0.0168, 0.0143)
weights
##  [1] 0.0314 0.0133 0.0136 0.0589 0.0112 0.0068 0.0201 0.1858 0.2298 0.0584
## [11] 0.0892 0.2504 0.0168 0.0143
w_tbl <- tibble(symbols, weights)
w_tbl
## # A tibble: 14 Ă— 2
##    symbols     weights
##    <chr>         <dbl>
##  1 Asker.st     0.0314
##  2 Atco-B.st    0.0133
##  3 Axfo.st      0.0136
##  4 BRK-B        0.0589
##  5 Bahn-b.st    0.0112
##  6 Cers         0.0068
##  7 Embrac-b.st  0.0201
##  8 Indu-c.st    0.186 
##  9 Inve-b.st    0.230 
## 10 Inwi.st      0.0584
## 11 LLY          0.0892
## 12 NVDA         0.250 
## 13 Novo-b.co    0.0168
## 14 Yubico.st    0.0143

4 Build a portfolio

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

portfolio_returns_tbl
## # A tibble: 68 Ă— 2
##    date       portfolio.returns
##    <date>                 <dbl>
##  1 2020-05-29            0.0285
##  2 2020-06-30            0.0121
##  3 2020-07-31            0.0243
##  4 2020-08-31            0.0373
##  5 2020-09-30            0.0430
##  6 2020-10-30           -0.0622
##  7 2020-11-30            0.0746
##  8 2020-12-30            0.0297
##  9 2020-12-31            0.0128
## 10 2021-01-29            0.0187
## # ℹ 58 more rows

5 Compute 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.0431 0.0431
# Mean of portfolio returns
portfolio_mean_tidyquant_builtin_percent <- mean(portfolio_returns_tbl$portfolio.returns)

portfolio_mean_tidyquant_builtin_percent
## [1] 0.01309267

6 Plot: Expected Returns versus Risk

# 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 portfolio 
    add_row(tibble(asset = "Portfolio", 
                   Mean = portfolio_mean_tidyquant_builtin_percent,
                   Stdev = portfolio_sd_tidyquant_builtin_percent$tq_sd)) 
sd_mean_tbl
## # A tibble: 15 Ă— 3
##    asset          Mean  Stdev
##    <chr>         <dbl>  <dbl>
##  1 Asker.st     0.125  0.0918
##  2 Atco-B.st    0.0113 0.0711
##  3 Axfo.st      0.0077 0.0748
##  4 BRK-B        0.0162 0.0571
##  5 Bahn-b.st    0.0166 0.0733
##  6 Cers        -0.0259 0.15  
##  7 Embrac-b.st -0.0162 0.170 
##  8 Indu-c.st    0.0106 0.0589
##  9 Inve-b.st    0.0154 0.0564
## 10 Inwi.st      0.0249 0.0862
## 11 LLY          0.0267 0.0912
## 12 NVDA         0.0479 0.143 
## 13 Novo-b.co    0.0134 0.0779
## 14 Yubico.st    0.007  0.110 
## 15 Portfolio    0.0131 0.0431
sd_mean_tbl %>%
    
    ggplot(aes(x = Stdev, y = Mean, color = asset)) +
    geom_point() + 
    ggrepel::geom_text_repel(aes(label = asset))

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

rolling_sd_tbl
## # A tibble: 45 Ă— 2
##    date       rolling_sd
##    <date>          <dbl>
##  1 2021-12-31     0.0340
##  2 2022-01-31     0.0405
##  3 2022-02-28     0.0418
##  4 2022-03-31     0.0429
##  5 2022-04-29     0.0435
##  6 2022-05-31     0.0432
##  7 2022-06-30     0.0424
##  8 2022-07-29     0.0435
##  9 2022-08-31     0.0482
## 10 2022-09-30     0.0493
## # ℹ 35 more rows
rolling_sd_tbl %>%
    
    ggplot(aes(x = date, y = rolling_sd)) + 
    geom_line(color = "royalblue") + 
    
    # 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 = .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.

The portfolio would perform somewhat worse than some of the individual assets in the portfolio, assets like Nividia or Asker have performed a lot better. However, the individual assets that perform strong also come with a higher standard deviation the portfolio average standard deviation is 0.0431 with Nvidia being 0.14 and Asker 0.09. The portfolio also holds stocks like Embracer and Cers that have a very high standard deviation of 0.17 and 0.15 which indicates high volatility and risk. The weight of these assets are low however which means their volatility doesn’t affect the entire portfolio a lot. The portfolio offers a moderate return with lowered volatility, looking at the individual assets they are all more volatile than the overall portfolio. This means that the portfolios diversification is working. If I was to invest all money into a single asset it would be into Eli & Lilly, Inwido, Berkshire Hathaway, Bahnhof or Investor since they offer a slightly better return with a slightly higher volatility. In conlusion the portfolio is expected to perform good with consistent returns and lowered risk.