R Markdown

Install and load necessary libraries

required_packages <- c(“quantmod”, “tidyquant”, “lubridate”, “timetk”, “purrr”, “xts”, “dplyr”, “ggplot2”, “zoo”)

install_missing <- required_packages[!(required_packages %in% installed.packages()[,“Package”])] if(length(install_missing)) install.packages(install_missing)

Load libraries

library(quantmod) library(tidyquant) library(lubridate) library(timetk) library(purrr) library(xts) library(dplyr) library(ggplot2) library(zoo)

Define ETF tickers and download historical data from Yahoo Finance

tickers <- c(“SPY”, “QQQ”, “EEM”, “IWM”, “EFA”, “TLT”, “IYR”, “GLD”) start_date <- “2010-01-01” end_date <- Sys.Date()

etf_data <- map(tickers, ~getSymbols(.x, src = “yahoo”, from = start_date, to = end_date, auto.assign = FALSE)) etf_prices <- do.call(merge, lapply(etf_data, Ad)) # Extract adjusted closing prices

Calculate weekly and monthly returns

etf_weekly_returns <- etf_prices %>% periodReturn(period = “weekly”, type = “log”)

etf_monthly_returns <- etf_prices %>% periodReturn(period = “monthly”, type = “log”)

Convert monthly returns to tibble format

monthly_returns_tbl <- etf_monthly_returns %>% as_tibble(rownames = “date”) %>% mutate(date = as.Date(date))

Download Fama-French 3-factor data

ff_url <- “http://mba.tuck.dartmouth.edu/pages/faculty/ken.french/ftp/F-F_Research_Data_Factors.CSV” ff_data <- read.csv(ff_url, skip = 3)

Rename and clean columns

names(ff_data)[1] <- “Date” ff_data <- ff_data %>% mutate(Date = as.Date(paste0(substr(Date, 1, 4), “-”, substr(Date, 5, 6), “-01”)), Mkt_RF = as.numeric(Mkt.RF)/100, SMB = as.numeric(SMB)/100, HML = as.numeric(HML)/100) %>% select(Date, Mkt_RF, SMB, HML) %>% na.omit()

Convert Fama-French data to xts format

ff_xts <- xts(ff_data[, -1], order.by = ff_data$Date)

Merge monthly returns with Fama-French factors

merged_data <- left_join(monthly_returns_tbl, ff_data, by = c(“date” = “Date”))

Remove rows with missing values

merged_data <- na.omit(merged_data)

Compute covariance matrices for CAPM and Fama-French models (past 60-month returns)

start_cov <- as.Date(“2010-02-01”) end_cov <- as.Date(“2015-01-01”)

capm_cov_matrix <- cov(subset(merged_data, date >= start_cov & date <= end_cov)[, -1]) ff_cov_matrix <- cov(subset(merged_data, date >= start_cov & date <= end_cov)[, -c(1, length(merged_data))])

Compute global minimum variance portfolio weights

gmv_weights_capm <- solve(capm_cov_matrix) %*% rep(1, ncol(capm_cov_matrix)) gmv_weights_capm <- gmv_weights_capm / sum(gmv_weights_capm)

gmv_weights_ff <- solve(ff_cov_matrix) %*% rep(1, ncol(ff_cov_matrix)) gmv_weights_ff <- gmv_weights_ff / sum(gmv_weights_ff)

Function for rolling estimation of GMV weights

compute_gmv_weights <- function(return_matrix) { cov_matrix <- cov(return_matrix) weights <- solve(cov_matrix) %*% rep(1, ncol(cov_matrix)) return(weights / sum(weights)) }

Apply rolling estimation for GMV portfolio returns (past 60 months)

window_size <- 60 gmv_returns_capm <- rollapply(merged_data[, -1], width = window_size, FUN = compute_gmv_weights, by.column = FALSE, align = “right”) gmv_returns_ff <- rollapply(merged_data[, -c(1, length(merged_data))], width = window_size, FUN = compute_gmv_weights, by.column = FALSE, align = “right”)

Compute cumulative returns

cum_returns_capm <- cumsum(gmv_returns_capm) cum_returns_ff <- cumsum(gmv_returns_ff)

Plot cumulative returns

ggplot() + geom_line(aes(x = seq_along(cum_returns_capm), y = cum_returns_capm), color = “blue”) + geom_line(aes(x = seq_along(cum_returns_ff), y = cum_returns_ff), color = “red”) + labs(title = “Cumulative Returns of GMV Portfolios”, x = “Time”, y = “Cumulative Return”)