# Load packages
library(tidyverse)
library(tidyquant)
library(moments)
library(ggrepel)
library(scales)
library(stringr)
library(timetk)
library(PerformanceAnalytics)
1. Import Prices
symbols <- c("AAPL", "MSFT", "GOOG", "AMZN", "TSLA", "NFLX")
prices <- tq_get(
x = symbols,
get = "stock.prices",
from = "2012-01-01",
to = "2022-12-31"
)
2. Convert to Monthly log Returns
asset_returns_tbl <- prices %>%
group_by(symbol) %>%
tq_transmute(select = adjusted,
mutate_fun = periodReturn,
period = "monthly",
type = "log") %>%
ungroup()
# Create portfolio returns
weights <- rep(1/6, 6)
portfolio_returns_tbl <- asset_returns_tbl %>%
tq_portfolio(
assets_col = symbol,
returns_col = monthly.returns,
weights = weights,
col_rename = "returns"
)
# Step 1: Calculate kurtosis for the portfolio
portfolio_kurt_tbl <- portfolio_returns_tbl %>%
tq_performance(Ra = returns, performance_fun = table.Stats) %>%
select(Kurtosis)
# Step 2: Histogram of portfolio returns
portfolio_returns_tbl %>%
ggplot(aes(x = returns)) +
geom_histogram(binwidth = 0.01, fill = "cornflowerblue", alpha = 0.5) +
geom_vline(xintercept = 0.0003, color = "green", size = 1) +
annotate("text", x = 0.0015, y = 13, label = "Risk-free rate", angle = 90)

Step 3: Scatterplot of Expected Return vs. Kurtosis
mean_kurt_tbl <- asset_returns_tbl %>%
group_by(symbol) %>%
summarise(mean = mean(monthly.returns),
kurt = kurtosis(monthly.returns)) %>%
ungroup()
portfolio_stats <- portfolio_returns_tbl %>%
summarise(mean = mean(returns), kurt = kurtosis(returns)) %>%
mutate(symbol = "portfolio")
mean_kurt_tbl <- bind_rows(mean_kurt_tbl, portfolio_stats)
# Plot
mean_kurt_tbl %>%
ggplot(aes(x = kurt, y = mean, color = symbol)) +
geom_point() +
geom_text_repel(aes(label = symbol)) +
theme(legend.position = "none") +
labs(x = "Kurtosis", y = "Expected Return") +
scale_y_continuous(labels = scales::percent_format(accuracy = 0.1))

Step 4: Rolling 24-Month Kurtosis Line Plot
# Define the rolling window
window <- 24
rolling_kurt_tbl <- portfolio_returns_tbl %>%
tq_mutate(
select = returns,
mutate_fun = rollapply,
width = window,
FUN = kurtosis,
col_rename = "kurt"
) %>%
na.omit() %>%
select(date, kurt)
# Plot Rolling Kurtosis
rolling_kurt_tbl %>%
ggplot(aes(x = date, y = kurt)) +
geom_line(color = "cornflowerblue") +
scale_y_continuous(breaks = seq(-1, 4, 0.5)) +
scale_x_date(breaks = scales::pretty_breaks(n = 7)) +
labs(
x = NULL,
y = "Kurtosis",
title = paste0("Rolling ", window, " Month Kurtosis")
) +
theme(plot.title = element_text(hjust = 0.5)) +
annotate(
"text",
x = as.Date("2017-07-01"),
y = 3.3,
label = str_glue("Downside risk\nskyrocketed toward end of 2017"),
size = 5,
color = "red"
)

Step 5: Rolling 24-Month Sharpe Ratio
# Define risk-free rate
risk_free_rate <- 0.0003
# Define custom function
calculate_rolling_sharpe_ratio <- function(data) {
SharpeRatio(
R = data,
Rf = risk_free_rate,
FUN = "StdDev"
)
}
# Calculate rolling Sharpe Ratio
rolling_sharpe_tbl <- portfolio_returns_tbl %>%
tq_mutate(
select = returns,
mutate_fun = rollapply,
width = window,
FUN = calculate_rolling_sharpe_ratio,
col_rename = "rolling_sharpe"
) %>%
na.omit() %>%
select(date, rolling_sharpe)
# Plot Rolling Sharpe Ratio
rolling_sharpe_tbl %>%
ggplot(aes(x = date, y = rolling_sharpe)) +
geom_line(color = "cornflowerblue") +
labs(
x = NULL,
y = "Rolling Sharpe Ratio",
title = paste0("Rolling ", window, "-Month Sharpe Ratio")
) +
annotate(
"text",
x = as.Date("2016-06-01"),
y = 0.5,
label = "This portfolio has done quite well since 2016",
color = "red",
size = 5
) +
theme(plot.title = element_text(hjust = 0.5))
