# 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("JPM", "MS", "DNB.OL", "NDA-FI.HE")

prices <- tq_get(x    = symbols, 
                 get  = "stock.prices", 
                 from = "2017-01-01", 
                 to   = "2023-12-31") %>%
          filter(!is.na(close)) 

2 Convert prices to returns (monthly)

asset_returns_tbl <-prices %>%
    
    group_by(symbol) %>%
    
    tq_transmute(select     = adjusted, 
                 mutate_fun = periodReturn, 
                 period     = "quarterly",
                 type = "log") %>%
    ungroup() %>%
    
    set_names(c("asset", "date", "returns"))
asset_returns_tbl
## # A tibble: 112 × 3
##    asset date       returns
##    <chr> <date>       <dbl>
##  1 JPM   2017-03-31  0.0125
##  2 JPM   2017-06-30  0.0455
##  3 JPM   2017-09-29  0.0495
##  4 JPM   2017-12-29  0.119 
##  5 JPM   2018-03-29  0.0331
##  6 JPM   2018-06-29 -0.0488
##  7 JPM   2018-09-28  0.0851
##  8 JPM   2018-12-31 -0.138 
##  9 JPM   2019-03-29  0.0444
## 10 JPM   2019-06-28  0.107 
## # ℹ 102 more rows

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

symbols <- asset_returns_tbl %>% distinct(asset) %>% pull()
symbols
## [1] "JPM"       "MS"        "DNB.OL"    "NDA-FI.HE"
#weights 
weights <- c(0.25, 0.25, 0.25, 0.25)
weights
## [1] 0.25 0.25 0.25 0.25
w_tbl <- tibble(symbols, weights)
w_tbl
## # A tibble: 4 × 2
##   symbols   weights
##   <chr>       <dbl>
## 1 JPM          0.25
## 2 MS           0.25
## 3 DNB.OL       0.25
## 4 NDA-FI.HE    0.25

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: 33 × 2
##    date       portfolio.returns
##    <date>                 <dbl>
##  1 2017-03-31         0.0324   
##  2 2017-06-30         0.0531   
##  3 2017-09-29         0.0704   
##  4 2017-12-29         0.00773  
##  5 2018-03-28         0.0000821
##  6 2018-03-29        -0.00540  
##  7 2018-06-29        -0.0306   
##  8 2018-09-28         0.0706   
##  9 2018-12-28        -0.119    
## 10 2018-12-31        -0.0829   
## # ℹ 23 more rows

5 Compute kurtosis

portfolio_returns_tbl %>%

    tq_performance(Ra = portfolio.returns,
                   Rb = NULL,
                   performance_fun = table.Stats) %>%
    select(Kurtosis)
## # A tibble: 1 × 1
##   Kurtosis
##      <dbl>
## 1     5.39

6 Plot: Rolling kurtosis

window <- 24

port_rolling_kurtosis_tbl <- portfolio_returns_tbl %>%

    tq_mutate(select = portfolio.returns,
              mutate_fun = rollapply,
              width      = window,
              FUN        = kurtosis,
              col_rename = "rolling_kurtosis") %>%
    select(date, rolling_kurtosis) %>%
    na.omit()



port_rolling_kurtosis_tbl %>%

    ggplot(aes(date, rolling_kurtosis)) +
    geom_line(color = "cornflowerblue", size = 1) +

    scale_y_continuous(breaks = scales::pretty_breaks(n = 8)) +
    scale_x_date(breaks = scales::breaks_pretty(n = 6)) +

    labs(title = paste0("Rolling ", window, "-Month Kurtosis"),
         x = NULL,
         y = "kurtosis") +
    theme(plot.title = element_text(hjust = 0.5)) +

    annotate(geom = "text",
             x = as.Date("2022-12-01"), y = 5.5,
             color = "red", size = 5,
             label = str_glue("The 24-month rolling kurtosis dropped early on\nbut spiked above three by the period’s end,\nsignaling increased downside risk and volatility."))

Comparison with Skewness

# Calculate skewness for each asset and add the portfolio skewness
asset_returns_skew_tbl <- asset_returns_tbl %>%
    group_by(asset) %>%
    summarise(skew = skewness(returns, na.rm = TRUE)) %>%
    ungroup() %>%
    add_row(
        asset = "Portfolio", 
        skew = skewness(pull(portfolio_returns_tbl, portfolio.returns), na.rm = TRUE)
    )

# Plot the skewness
asset_returns_skew_tbl %>%
    ggplot(aes(x = asset, y = skew, color = asset)) +
    geom_point(size = 3) +
    ggrepel::geom_text_repel(
        aes(label = asset),
        data = asset_returns_skew_tbl %>% filter(asset == "Portfolio"),
        size = 5
    ) +
    labs(title = "Asset and Portfolio Skewness", 
         x = "Asset", 
         y = "Skewness") +
    theme(legend.position = "none")

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.

The chart shows that the 24-month rolling kurtosis experienced a sharp increase toward the end of the period, rising above three. This indicates a significant rise in downside risk or the likelihood of extreme outcomes. A normal distribution has a skewness of 0. A skewness of -1 or lower suggests extreme outliers on the left side of the distribution (negative skew).

By combining kurtosis that is larger than 3 and frequent extreme values with the skewness lower than -1, shows that there is a frequency of large losses.