How has your portfolio performed over time?

Load packages

# Core
library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.4     ✔ readr     2.1.5
## ✔ forcats   1.0.1     ✔ stringr   1.5.2
## ✔ ggplot2   4.0.0     ✔ 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

Goal

Visualize and examine changes in the underlying trend in the performance of your portfolio in terms of Sharpe Ratio.


1. Import stock prices

symbols <- c("AMZN", "META", "NFLX", "NVDA", "GOOGL")

prices <- tq_get(x    = symbols,
                 get  = "stock.prices", 
                 from = "2012-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 AMZN   2012-12-31  12.2  12.6  12.1  12.5 68380000     12.5
## 2 AMZN   2013-01-02  12.8  12.9  12.7  12.9 65420000     12.9
## 3 AMZN   2013-01-03  12.9  13.0  12.8  12.9 55018000     12.9
## 4 AMZN   2013-01-04  12.9  13.0  12.8  13.0 37484000     13.0
## 5 AMZN   2013-01-07  13.1  13.5  13.1  13.4 98200000     13.4
## 6 AMZN   2013-01-08  13.4  13.4  13.2  13.3 60214000     13.3

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"))

head(asset_returns_tbl)
## # A tibble: 6 × 3
##   asset date        returns
##   <chr> <date>        <dbl>
## 1 AMZN  2013-01-31  0.0567 
## 2 AMZN  2013-02-28 -0.00464
## 3 AMZN  2013-03-28  0.00837
## 4 AMZN  2013-04-30 -0.0488 
## 5 AMZN  2013-05-31  0.0589 
## 6 AMZN  2013-06-28  0.0311

3. Assign a weight to each asset

# symbols
symbols <- asset_returns_tbl %>% distinct(asset) %>% pull()
symbols
## [1] "AMZN"  "GOOGL" "META"  "NFLX"  "NVDA"
# weights
weights <- c(0.2, 0.2, 0.2, 0.2, 0.2)
weights
## [1] 0.2 0.2 0.2 0.2 0.2
w_tbl <- tibble(symbols, weights)
w_tbl
## # A tibble: 5 × 2
##   symbols weights
##   <chr>     <dbl>
## 1 AMZN        0.2
## 2 GOOGL       0.2
## 3 META        0.2
## 4 NFLX        0.2
## 5 NVDA        0.2

4. Build a portfolio

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

head(portfolio_returns_tbl)
## # A tibble: 6 × 2
##   date        returns
##   <date>        <dbl>
## 1 2013-01-31  0.171  
## 2 2013-02-28  0.0186 
## 3 2013-03-28 -0.00879
## 4 2013-04-30  0.0548 
## 5 2013-05-31  0.0166 
## 6 2013-06-28 -0.00730

5. Compute Sharpe Ratio

# Define risk free rate
rfr <- 0.0003

portfolio_SharpeRatio_tbl <- portfolio_returns_tbl %>%
    tq_performance(Ra              = returns,
                   performance_fun = SharpeRatio,
                   Rf              = rfr,
                   FUN             = "StdDev")

portfolio_SharpeRatio_tbl
## # A tibble: 1 × 1
##   `StdDevSharpe(Rf=0%,p=95%)`
##                         <dbl>
## 1                       0.339

6. Plot: Rolling Sharpe Ratio

# Custom function for rolling SR
Calculate_rolling_SharpeRatio <- function(data) {
    rolling_SR <- SharpeRatio(R   = data, 
                              Rf  = rfr,
                              FUN = "StdDev")
    return(rolling_SR)
}

# Define window
window <- 24

# Calculate rolling SR
rolling_sr_tbl <- portfolio_returns_tbl %>%
    tq_mutate(select     = returns,
              mutate_fun = rollapply, 
              width      = window, 
              FUN        = Calculate_rolling_SharpeRatio, 
              col_rename = "rolling_sr") %>%
    select(-returns) %>%
    na.omit()

head(rolling_sr_tbl)
## # A tibble: 6 × 2
##   date       rolling_sr
##   <date>          <dbl>
## 1 2014-12-31      0.475
## 2 2015-01-30      0.458
## 3 2015-02-27      0.493
## 4 2015-03-31      0.464
## 5 2015-04-30      0.477
## 6 2015-05-29      0.485

Rolling Sharpe Ratio Plot

rolling_sr_tbl %>%
    ggplot(aes(x = date, y = rolling_sr)) +
    geom_line(color = "cornflowerblue", linewidth = 1) +
    labs(x = NULL, 
         y = "Rolling Sharpe Ratio", 
         title = "Rolling 24-Month Sharpe Ratio") +
    theme_minimal() +
    theme(plot.title = element_text(hjust = 0.5))


Interpretation

You can identify structural breaks—major shifts in the portfolio’s trend—by visually observing the chart or performing a statistical breakpoint test (optional).
In the Code Along 9 example, there was one noticeable structural break in November 2016, likely due to macroeconomic events such as: - The 2016 U.S. presidential election, which caused market volatility. - Shifts in tech sector valuations and expectations for interest rates. - Increased investor risk appetite and capital inflows to growth stocks.

Overall, post-2016 periods reflect higher volatility but also greater returns in the tech-heavy portfolio.