Setup and Data Cleaning

First, we load the required packages and format our ETF dataset.

# Import libraries
library(xts)
library(PerformanceAnalytics)

# Read the CSV file
etf_data <- read.csv("myetf4.csv")

# Convert the Index column to proper Date format
etf_data$Index <- as.Date(etf_data$Index, format="%Y/%m/%d")

# Create an xts time-series object
etf_xts <- xts(etf_data[, -1], order.by = etf_data$Index)

# Subset the data for the in-sample period
insample_data <- etf_xts["2015-12-14/2018-12-28"]

Q1: Daily Returns (GMVP)

Here we compute the Global Minimum Variance Portfolio using daily returns.

# Get daily discrete returns and remove NAs
daily_returns <- na.omit(Return.calculate(insample_data, method="discrete"))

# Calculate the sample covariance matrix and mean vector
cov_matrix_d <- cov(daily_returns)
mean_vector_d <- colMeans(daily_returns)

# Inverse covariance matrix
inv_cov_d <- solve(cov_matrix_d)
ones_vec_d <- rep(1, ncol(daily_returns))

# Formula for GMVP weights
weights_gmvp_d <- (inv_cov_d %*% ones_vec_d) / as.numeric(t(ones_vec_d) %*% inv_cov_d %*% ones_vec_d)

# Calculate Portfolio Return and Risk (Standard Deviation)
port_ret_d <- t(weights_gmvp_d) %*% mean_vector_d
port_sd_d <- sqrt(t(weights_gmvp_d) %*% cov_matrix_d %*% weights_gmvp_d)

# Display the results
print("Optimal Weights for Daily GMVP:")
## [1] "Optimal Weights for Daily GMVP:"
round(weights_gmvp_d, 4)
##             [,1]
## tw0050   -0.2194
## tw0056    0.7284
## tw006205  0.1076
## tw00646   0.3834
cat(sprintf("Expected Daily Return: %f\n", port_ret_d))
## Expected Daily Return: 0.000254
cat(sprintf("Daily Standard Deviation: %f\n", port_sd_d))
## Daily Standard Deviation: 0.005905

Q2: Monthly Returns (GMVP)

Now, we extract the monthly endpoints to calculate the GMVP using monthly returns.

# Extract prices at the end of each month
monthly_prices <- insample_data[endpoints(insample_data, on="months")]

# Calculate monthly returns
monthly_returns <- na.omit(Return.calculate(monthly_prices, method="discrete"))

# Covariance and mean for monthly data
cov_matrix_m <- cov(monthly_returns)
mean_vector_m <- colMeans(monthly_returns)
inv_cov_m <- solve(cov_matrix_m)
ones_vec_m <- rep(1, ncol(monthly_returns))

# Monthly GMVP weights
weights_gmvp_m <- (inv_cov_m %*% ones_vec_m) / as.numeric(t(ones_vec_m) %*% inv_cov_m %*% ones_vec_m)

# Monthly Return and Risk
port_ret_m <- t(weights_gmvp_m) %*% mean_vector_m
port_sd_m <- sqrt(t(weights_gmvp_m) %*% cov_matrix_m %*% weights_gmvp_m)

print("Optimal Weights for Monthly GMVP:")
## [1] "Optimal Weights for Monthly GMVP:"
round(weights_gmvp_m, 4)
##            [,1]
## tw0050   0.0032
## tw0056   0.4740
## tw006205 0.0012
## tw00646  0.5216
cat(sprintf("Expected Monthly Return: %f\n", port_ret_m))
## Expected Monthly Return: 0.005734
cat(sprintf("Monthly Standard Deviation: %f\n", port_sd_m))
## Monthly Standard Deviation: 0.024904

Q3: Tangency Portfolio (Monthly)

Using the monthly data from Q2, we find the tangency portfolio weights. The risk-free rate is assumed to be 0.

risk_free_rate <- 0 

# Formula for Tangency Portfolio weights
weights_tangency <- (inv_cov_m %*% (mean_vector_m - risk_free_rate)) / 
                    as.numeric(t(ones_vec_m) %*% inv_cov_m %*% (mean_vector_m - risk_free_rate))

# Tangency Return and Risk
tan_ret_m <- t(weights_tangency) %*% mean_vector_m
tan_sd_m <- sqrt(t(weights_tangency) %*% cov_matrix_m %*% weights_tangency)

print("Optimal Weights for Tangency Portfolio:")
## [1] "Optimal Weights for Tangency Portfolio:"
round(weights_tangency, 4)
##             [,1]
## tw0050    1.3051
## tw0056   -0.1577
## tw006205 -0.8475
## tw00646   0.7002
cat(sprintf("Expected Monthly Return (Tangency): %f\n", tan_ret_m))
## Expected Monthly Return (Tangency): 0.018090
cat(sprintf("Monthly Standard Deviation (Tangency): %f\n", tan_sd_m))
## Monthly Standard Deviation (Tangency): 0.044236