# Clear environment
rm(list = ls())

# Load libraries
library(tidyverse)
library(lubridate)
library(xts)
library(PerformanceAnalytics)

Load Data

data <- read.csv("myetf4.csv", check.names = FALSE)

# Auto-detect the date column (handles "Date", "date", "DATE", "Index", etc.)
date_col <- names(data)[tolower(names(data)) %in% c("date", "index")]
if (length(date_col) == 0) stop("No date column found. Columns are: ", paste(names(data), collapse = ", "))

data[[date_col]] <- as.Date(data[[date_col]])

# Select in-sample period
data_in <- data %>%
  filter(.data[[date_col]] >= as.Date("2015-12-14") &
         .data[[date_col]] <= as.Date("2018-12-28"))

# Auto-detect ETF price columns (everything except the date column)
price_cols <- setdiff(names(data_in), date_col)
cat("Using price columns:", paste(price_cols, collapse = ", "), "\n")
## Using price columns: tw0050, tw0056, tw006205, tw00646
# Convert to xts
etf_xts <- xts(data_in[, price_cols],
               order.by = data_in[[date_col]])

Q1: GMVP using DAILY Returns

# Compute daily log returns
ret_daily <- Return.calculate(etf_xts, method = "log")
ret_daily <- na.omit(ret_daily)

# Mean vector and covariance matrix
mu_daily    <- colMeans(ret_daily)
Sigma_daily <- cov(ret_daily)

# GMVP weights formula:
# w = Sigma^{-1} 1 / (1' Sigma^{-1} 1)
one_vec   <- rep(1, 4)
inv_Sigma <- solve(Sigma_daily)

w_gmvp_daily <- inv_Sigma %*% one_vec /
  as.numeric(t(one_vec) %*% inv_Sigma %*% one_vec)

# Portfolio return & standard deviation
ret_gmvp_daily <- as.numeric(t(w_gmvp_daily) %*% mu_daily)
sd_gmvp_daily  <- as.numeric(sqrt(t(w_gmvp_daily) %*% Sigma_daily %*% w_gmvp_daily))

cat("===== Q1: DAILY GMVP =====\n")
## ===== Q1: DAILY GMVP =====
cat("Weights:\n")
## Weights:
print(w_gmvp_daily)
##                [,1]
## tw0050   -0.2241314
## tw0056    0.7298726
## tw006205  0.1080760
## tw00646   0.3861827
cat("Expected Return:", ret_gmvp_daily, "\n")
## Expected Return: 0.0002263853
cat("Std Dev:", sd_gmvp_daily, "\n")
## Std Dev: 0.005921611

Q2: GMVP using MONTHLY Returns

# Convert to monthly prices (last price of each month)
monthly_prices <- apply.monthly(etf_xts, last)

# Compute monthly log returns
ret_monthly <- Return.calculate(monthly_prices, method = "log")
ret_monthly <- na.omit(ret_monthly)

mu_monthly    <- colMeans(ret_monthly)
Sigma_monthly <- cov(ret_monthly)

inv_Sigma_m <- solve(Sigma_monthly)

w_gmvp_monthly <- inv_Sigma_m %*% one_vec /
  as.numeric(t(one_vec) %*% inv_Sigma_m %*% one_vec)

ret_gmvp_monthly <- as.numeric(t(w_gmvp_monthly) %*% mu_monthly)
sd_gmvp_monthly  <- as.numeric(sqrt(t(w_gmvp_monthly) %*% Sigma_monthly %*% w_gmvp_monthly))

cat("===== Q2: MONTHLY GMVP =====\n")
## ===== Q2: MONTHLY GMVP =====
cat("Weights:\n")
## Weights:
print(w_gmvp_monthly)
##                  [,1]
## tw0050   -0.028540599
## tw0056    0.520686730
## tw006205 -0.001782652
## tw00646   0.509636521
cat("Expected Return:", ret_gmvp_monthly, "\n")
## Expected Return: 0.005304254
cat("Std Dev:", sd_gmvp_monthly, "\n")
## Std Dev: 0.02506921

Q3: Tangency Portfolio (rf = 0)

# Tangency weights formula (rf = 0):
# w = Sigma^{-1} mu / (1' Sigma^{-1} mu)
w_tangency <- inv_Sigma_m %*% mu_monthly /
  as.numeric(t(one_vec) %*% inv_Sigma_m %*% mu_monthly)

ret_tangency <- as.numeric(t(w_tangency) %*% mu_monthly)
sd_tangency  <- as.numeric(sqrt(t(w_tangency) %*% Sigma_monthly %*% w_tangency))

cat("===== Q3: TANGENCY PORTFOLIO (Monthly, rf=0) =====\n")
## ===== Q3: TANGENCY PORTFOLIO (Monthly, rf=0) =====
cat("Weights:\n")
## Weights:
print(w_tangency)
##                [,1]
## tw0050    1.2789695
## tw0056   -0.0873706
## tw006205 -0.8967581
## tw00646   0.7051592
cat("Expected Return:", ret_tangency, "\n")
## Expected Return: 0.01871875
cat("Std Dev:", sd_tangency, "\n")
## Std Dev: 0.04709414
cat("Sharpe Ratio:", ret_tangency / sd_tangency, "\n")
## Sharpe Ratio: 0.3974751