# Load packages

# Core
library(tidyverse)
library(tidyquant)

Goal

Examine how each asset contributes to portfolio standard deviation. This is to ensure that our risk is not concentrated in any one asset.

1 Import stock prices

Choose your stocks from 2012-12-31 to present.

symbols <- c("AAPL", "MSFT", "NVDA", "TSLA", "JNJ")

prices <- tq_get(x = symbols,
                 get = "stock.prices",
                 from = "2012-12-31",
                 to = Sys.Date())

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 Calculate Component Contribution to Portfolio Volatility

# Step 1: Convert to wide format
asset_returns_wide_tbl <- asset_returns_tbl %>%
  pivot_wider(names_from = asset, values_from = returns) %>%
  column_to_rownames(var = "date")

# Step 2: Set weights
w <- c(0.2, 0.2, 0.2, 0.2, 0.2)  # Equal weights

# Step 3: Function to calculate contribution
calculate_component_contribution <- function(data, weights) {
  cov_matrix <- cov(data)
  sd_portfolio <- sqrt(t(weights) %*% cov_matrix %*% weights)
  component_contribution <- (t(weights) %*% cov_matrix * weights) / sd_portfolio[1, 1]
  component_percentages <- (component_contribution / sd_portfolio[1, 1]) %>%
    round(3) %>%
    as_tibble()
  return(component_percentages)
}

component_tbl <- calculate_component_contribution(asset_returns_wide_tbl, w)

6 Plot: Column Chart of Component Contribution and Weight

plot_data <- component_tbl %>%
  pivot_longer(cols = everything(),
               names_to = "asset",
               values_to = "contribution") %>%
  mutate(weight = w)

plot_data_long <- plot_data %>%
  pivot_longer(cols = c(contribution, weight),
               names_to = "type",
               values_to = "value")

plot_data_long %>%
  ggplot(aes(x = asset, y = value, fill = type)) +
  geom_col(position = "dodge") +
  scale_y_continuous(labels = scales::percent_format(accuracy = 1)) +
  labs(title = "Contribution to Portfolio Volatility vs. Portfolio Weights",
       y = "Percent", x = NULL, fill = NULL) +
  theme_minimal() +
  theme(plot.title = element_text(hjust = 0.5))

Reflection

Based on the chart, the asset with the largest contribution to portfolio volatility is likely NVDA due to its high return volatility. Since it stands out from the others, it suggests the portfolio’s risk is somewhat concentrated. To better balance the risk, the portfolio could be adjusted by lowering exposure to NVDA or adding more low-volatility assets.