library(xts)
## Warning: package 'xts' was built under R version 4.5.3
## Loading required package: zoo
## 
## Attaching package: 'zoo'
## The following objects are masked from 'package:base':
## 
##     as.Date, as.Date.numeric
library(PerformanceAnalytics)
## Warning: package 'PerformanceAnalytics' was built under R version 4.5.3
## 
## Attaching package: 'PerformanceAnalytics'
## The following object is masked from 'package:graphics':
## 
##     legend
library(ggplot2)
## Warning: package 'ggplot2' was built under R version 4.5.3
# ================================
# 1. Load & Clean Data
# ================================
data <- read.csv(file.choose(), stringsAsFactors = FALSE)

colnames(data)[1] <- "Date"
data$Date <- as.Date(data$Date)

# Convert to numeric
data[,-1] <- lapply(data[,-1], function(x) as.numeric(as.character(x)))
data <- na.omit(data)

# Convert to xts
data_xts <- xts(data[,-1], order.by = data$Date)

# ================================
# 2. Compute Returns
# ================================
ret_daily <- na.omit(diff(log(data_xts)))
ret_daily <- ret_daily["2015-12-14/2018-12-28"]

# Monthly
price_monthly <- to.monthly(data_xts, indexAt = "lastof", OHLC = FALSE)
ret_monthly <- na.omit(diff(log(price_monthly)))

# ================================
# 3. GMVP (Monthly)
# ================================
cov_mat <- cov(ret_monthly) + diag(1e-6, ncol(ret_monthly))
mean_ret <- colMeans(ret_monthly)

one_vec <- rep(1, ncol(ret_monthly))
inv_cov <- solve(cov_mat)

w_gmvp <- inv_cov %*% one_vec / as.numeric(t(one_vec) %*% inv_cov %*% one_vec)

ret_gmvp <- sum(w_gmvp * mean_ret)
sd_gmvp <- sqrt(t(w_gmvp) %*% cov_mat %*% w_gmvp)

# ================================
# 4. Tangency Portfolio
# ================================
w_tan <- inv_cov %*% mean_ret / as.numeric(t(one_vec) %*% inv_cov %*% mean_ret)

ret_tan <- sum(w_tan * mean_ret)
sd_tan <- sqrt(t(w_tan) %*% cov_mat %*% w_tan)

# ================================
# 5. Efficient Frontier
# ================================
portfolio_returns <- c()
portfolio_risk <- c()

set.seed(123)

for(i in 1:5000){
  w <- runif(ncol(ret_monthly))
  w <- w / sum(w)
  
  port_return <- sum(w * mean_ret)
  port_risk <- sqrt(t(w) %*% cov_mat %*% w)
  
  portfolio_returns <- c(portfolio_returns, port_return)
  portfolio_risk <- c(portfolio_risk, port_risk)
}

df <- data.frame(Risk = portfolio_risk, Return = portfolio_returns)

# ================================
# 6. Plot Efficient Frontier
# ================================
ggplot(df, aes(x = Risk, y = Return)) +
  geom_point(alpha = 0.3) +
  
  # GMVP
  geom_point(aes(x = sd_gmvp, y = ret_gmvp), size = 4) +
  
  # Tangency
  geom_point(aes(x = sd_tan, y = ret_tan), size = 4) +
  
  ggtitle("Efficient Frontier with GMVP and Tangency Portfolio") +
  xlab("Risk (Standard Deviation)") +
  ylab("Expected Return") +
  theme_minimal()
## Warning in geom_point(aes(x = sd_gmvp, y = ret_gmvp), size = 4): All aesthetics have length 1, but the data has 5000 rows.
## ℹ Please consider using `annotate()` or provide this layer with data containing
##   a single row.
## Warning in geom_point(aes(x = sd_tan, y = ret_tan), size = 4): All aesthetics have length 1, but the data has 5000 rows.
## ℹ Please consider using `annotate()` or provide this layer with data containing
##   a single row.

# ================================
# 7. Portfolio Comparison Table
# ================================
results <- data.frame(
  Portfolio = c("GMVP", "Tangency"),
  Return = c(ret_gmvp, ret_tan),
  Risk = c(sd_gmvp, sd_tan)
)

print(results)
##   Portfolio      Return       Risk
## 1      GMVP 0.005306363 0.02507979
## 2  Tangency 0.018702323 0.04708399
# ================================
# 8. Cumulative Returns Plot
# ================================
# Portfolio returns
ret_gmvp_series <- xts(ret_monthly %*% w_gmvp, order.by = index(ret_monthly))
ret_tan_series  <- xts(ret_monthly %*% w_tan, order.by = index(ret_monthly))

# Combine
combined <- merge(ret_gmvp_series, ret_tan_series)
colnames(combined) <- c("GMVP", "Tangency")

# Plot
charts.PerformanceSummary(combined, main = "Cumulative Returns Comparison")

This analysis demonstrates how the Global Minimum Variance Portfolio (GMVP) and the Tangency Portfolio can be constructed using four Taiwan ETFs. Based on daily data, the GMVP effectively reduces overall risk by assigning weights that minimize portfolio volatility, although it tends to generate lower returns as a trade-off.

When the analysis is conducted using monthly data, the portfolio weights and outcomes show slight differences. This is because monthly returns smooth out short-term market movements, leading to changes in the covariance matrix and, consequently, the optimization results.

In contrast, the Tangency Portfolio offers a more favorable trade-off between risk and return. With the risk-free rate assumed to be zero, the portfolio is designed to maximize returns relative to risk, making it a more appealing option for investors who are willing to accept a higher level of risk for potentially greater returns.