# Load packages
 
# Core
library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.4     ✔ readr     2.1.5
## ✔ forcats   1.0.0     ✔ stringr   1.5.2
## ✔ ggplot2   3.5.2     ✔ tibble    3.3.0
## ✔ lubridate 1.9.4     ✔ tidyr     1.3.1
## ✔ purrr     1.1.0     
## ── 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.11 ──
## ✔ PerformanceAnalytics 2.0.8      ✔ TTR                  0.24.4
## ✔ quantmod             0.4.28     ✔ xts                  0.14.1── 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
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 2012-12-31 to 2017-12-31

1 Import stock prices

symbols <- c("XOM", "CVX", "COP", "BP", "SHEL")
 
# Using tq_get() ----
prices <- tq_get(x = symbols,
                 get = "stock.prices",
                 from = "2012-12-31",
                 to = "2017-12-31")

2 Convert prices to returns

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

3 Assign a weight to each asset

symbols <- asset_returns_tbl %>% distinct(asset) %>% pull()
 
w <- c(0.25,
       0.25,
       0.20,
       0.20,
       0.10)
 
w_tbl <- tibble(symbols, w)

4 Build a portfolio

portfolio_returns_rebalanced_monthly_tbl <- asset_returns_tbl %>%
   
    tq_portfolio(assets_col   = asset,
                 returns_col  = returns,
                 weights      = w_tbl,
                 col_rename   = "returns",
                 rebalance_on = "months")
## Warning in check_weights(weights, assets_col, map, x): Sum of weights does not
## equal 1.
portfolio_returns_rebalanced_monthly_tbl
## # A tibble: 60 × 2
##    date        returns
##    <date>        <dbl>
##  1 2013-01-31  0.0377 
##  2 2013-02-28 -0.0251 
##  3 2013-03-28  0.0228 
##  4 2013-04-30  0.0212 
##  5 2013-05-31  0.00831
##  6 2013-06-28 -0.0257 
##  7 2013-07-31  0.0481 
##  8 2013-08-30 -0.0144 
##  9 2013-09-30  0.0200 
## 10 2013-10-31  0.0457 
## # ℹ 50 more rows
# write_rds(portfolio_returns_rebalanced_monthly_tbl,
#           "00_data/Ch03_portfolio_returns_rebalanced_monthly_tbl.rds")

5 Compute Standard Deviation

portfolio_sd_tidyquant_builtin_percent <- portfolio_returns_rebalanced_monthly_tbl %>%
   
    tq_performance(Ra = returns,
                   Rb = NULL,
                   performance_fun = table.Stats) %>%
   
    select(Stdev) %>%
    mutate(tq_sd = round(Stdev, 4) * 100)
 
portfolio_sd_tidyquant_builtin_percent
## # A tibble: 1 × 2
##    Stdev tq_sd
##    <dbl> <dbl>
## 1 0.0507  5.07

6 Plot

portfolio_returns_rebalanced_monthly_tbl %>%
 
    ggplot(aes(date, returns)) +
    geom_point(color = "cornflowerblue", size = 2) +
 
    labs(title = "Scatterplot of Returns by Date") +
    theme(plot.title = element_text(hjust = 0.5))

sd_plot <- sd(portfolio_returns_rebalanced_monthly_tbl$returns)
mean_plot <- mean(portfolio_returns_rebalanced_monthly_tbl$returns)
 
portfolio_returns_rebalanced_monthly_tbl %>%
 
    mutate(hist_col = case_when(
        returns > mean_plot + sd_plot ~ "high",
        returns < mean_plot - sd_plot ~ "middle",
        TRUE                          ~ "low"
    )) %>%
 
    ggplot(aes(date, returns, col = hist_col)) +
    geom_point(size = 2) +
 
    labs(title = "Colored Scatter") +
    theme(plot.title = element_text(hjust = 0.5))

sd_plot <- sd(portfolio_returns_rebalanced_monthly_tbl$returns)
mean_plot <- mean(portfolio_returns_rebalanced_monthly_tbl$returns)
 
portfolio_returns_rebalanced_monthly_tbl %>%
 
    mutate(hist_col = case_when(
        returns > mean_plot + sd_plot ~ "high",
        returns < mean_plot - sd_plot ~ "middle",
        TRUE                          ~ "low"
    )) %>%
 
    ggplot(aes(date, returns, col = hist_col)) +
    geom_point(size = 2) +
 
    labs(title = "Colored Scatter with Line") +
    theme(plot.title = element_text(hjust = 0.5)) +
 
    geom_hline(yintercept = mean_plot + sd_plot, linetype = "dotted", color = "purple") +
    geom_hline(yintercept = mean_plot - sd_plot, linetype = "dotted", color = "purple")

portfolio_returns_rebalanced_monthly_tbl
## # A tibble: 60 × 2
##    date        returns
##    <date>        <dbl>
##  1 2013-01-31  0.0377 
##  2 2013-02-28 -0.0251 
##  3 2013-03-28  0.0228 
##  4 2013-04-30  0.0212 
##  5 2013-05-31  0.00831
##  6 2013-06-28 -0.0257 
##  7 2013-07-31  0.0481 
##  8 2013-08-30 -0.0144 
##  9 2013-09-30  0.0200 
## 10 2013-10-31  0.0457 
## # ℹ 50 more rows
asset_returns_sd_tbl <- asset_returns_tbl %>%
 
    group_by(asset) %>%
    tq_performance(Ra = returns,
                   Rb = NULL,
                   performance_fun = table.Stats) %>%
 
    select(asset, Stdev) %>%
    ungroup() %>%
 
    add_row(tibble(asset = "Portfolio",
                  Stdev = sd(portfolio_returns_rebalanced_monthly_tbl$returns)))
 
asset_returns_sd_tbl %>%
 
    # Plot
    ggplot(aes(asset, Stdev, col = asset)) +
    geom_point() +
    ggrepel::geom_text_repel(aes(label = asset),
                             data = asset_returns_sd_tbl %>%
                                 filter(asset == "Portfolio")) +
 
    labs(title = "")

asset_returns_sd_mean_tbl <- asset_returns_tbl %>%
 
    group_by(asset) %>%
    tq_performance(Ra = returns,
                   Rb = NULL,
                   performance_fun = table.Stats) %>%
 
    select(asset, Mean = ArithmeticMean, Stdev) %>%
    ungroup() %>%
 
    add_row(tibble(asset = "Portfolio",
                   Mean  = mean(portfolio_returns_rebalanced_monthly_tbl$returns),
                   Stdev = sd(portfolio_returns_rebalanced_monthly_tbl$returns)))
 
 
asset_returns_sd_mean_tbl %>%
 
    ggplot(aes(Stdev, Mean, col = asset)) +
    geom_point() +
    ggrepel::geom_text_repel(aes(label = asset))

window <- 24
 
port_rolling_sd_tbl <- portfolio_returns_rebalanced_monthly_tbl %>%
 
    tq_mutate(select = returns,
              mutate_fun = rollapply,
              width      = window,
              FUN        = sd,
              col_rename = "rolling_sd") %>%
    select(date, rolling_sd) %>%
    na.omit()
 
port_rolling_sd_tbl %>%
 
    ggplot(aes(date, rolling_sd)) +
    geom_line(color = "cornflowerblue") +
 
    scale_y_continuous(labels = scales::percent) +
    scale_x_date(breaks = scales::breaks_pretty(n = 7))+
 
    labs(title = "24-Month Rolling Volatility",
         x = NULL,
         y = NULL) +
    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.

I would invest my money in Shell because, based on the data, it offers the strongest potential for profit among the companies I evaluated. It has the highest expected return while also carrying the lowest level of risk, making it the most attractive option overall.