# Load packages

# Core
library(tidyverse)
library(tidyquant)

Goal

Visualize and examine changes in the underlying trend in the downside risk of your portfolio in terms of kurtosis.

Choose your stocks.

from 2012-12-31 to present

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-26")
prices
## # A tibble: 7,300 × 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,290 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",
                 col_rename = "returns")

portfolio_returns_tbl2
## # A tibble: 70 × 2
##    date       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

Calculate Skewness

portfolio_skew_tbl <- portfolio_returns_tbl2 %>%
    
    tq_performance(Ra = returns,
                   performance_fun = table.Stats) %>%
    select(Skewness)

portfolio_skew_tbl
## # A tibble: 1 × 1
##   Skewness
##      <dbl>
## 1    0.222

5 Compute kurtosis

portfolio_kurt_tidyquant_bulitin_percent <- portfolio_returns_tbl2 %>%

    tq_performance(Ra = returns, 
                   performance_fun = table.Stats) %>%
    
    select(Kurtosis)
#The portfolio have a kurtosis is smaller than 3, meaning it has thinner tail that normal distribution -> Less risky, more returns is in the center (more predictable)

portfolio_kurt_tidyquant_bulitin_percent
## # A tibble: 1 × 1
##   Kurtosis
##      <dbl>
## 1    0.709

6 Plot: Rolling 24 - Month Kurtosis

# Assign a value for window
window = 24
# Transform data: calculate 24 month rolling kurtosis

rolling_kurt_tbl <- portfolio_returns_tbl2 %>%
    tq_mutate(select = returns,
              mutate_fun = rollapply,
              width = window, 
              FUN = kurtosis,
              col_rename = "kurt") %>%
    
    na.omit() %>%
    select(-returns)

# Plot
rolling_kurt_tbl %>%
    
    ggplot(aes(x = date, y = kurt)) +
    geom_line(colour = "violet") +
    
    #Formatting
    scale_y_continuous(breaks = seq(-1, 4, 0.5)) +
    scale_x_date(breaks = scales::pretty_breaks(n = 7)) +
    theme(plot.title = element_text(hjust = 0.5)) +
    
    #Labeling
    
    labs(x = NULL,
         y= "Kurtosis",
         title = paste0("Rolling", window, "- Month Kurtosis")) +
        
        annotate(geom = "text",
                 x = as.Date("2024-12-31"),
                 y = 3, 
                 size = 5,
                 color = "red",
                 hjust = 0.5,
                 label = ("Upside return rocketed towards the middle of 2026"))

Has the downside risk of your portfolio increased or decreased over time? Explain using the plot you created. You may also refer to the skewness of the returns distribution you plotted in the previous assignment.

As the skewness table previously shows, the portfolio does not have a negative tail, but rather a slightly positive skewness of 0.222. It re-frames the question to has the upside return increased or decreased over time, accordingly to the rolling 24-month kurtosis graphic. The portfolio’s upside return and kurtosis have increased significantly after Q1 2026, growing by more than 100% from, bellow 0.5 to roughly 1.25. The size of the tail however, is not of a substantial size compared to the normal distributions, as it is bellow the kurtosis threshold at 3. What the jump means is that, during the 2026 period there was a large increase positive returns that exceeded the normal distribution, meaning abnormal high returns.