library(xts)
## Loading required package: zoo
## 
## Attaching package: 'zoo'
## The following objects are masked from 'package:base':
## 
##     as.Date, as.Date.numeric
library(PerformanceAnalytics)
## 
## Attaching package: 'PerformanceAnalytics'
## The following object is masked from 'package:graphics':
## 
##     legend
library(ggplot2)
## Warning: package 'ggplot2' was built under R version 4.5.2
data <- read.csv("/Users/faizhaikal/Downloads/myetf4.csv", stringsAsFactors = FALSE)

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

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

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

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

price_monthly <- to.monthly(data_xts, indexAt = "lastof", OHLC = FALSE)
ret_monthly <- na.omit(diff(log(price_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)

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)

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)

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.

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
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))

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

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

The results show that both the GMVP and the Tangency Portfolio can be formed using four Taiwan ETFs. Based on daily data, the GMVP achieves the lowest level of risk, but it also comes with relatively lower returns. When monthly data is used instead, the outcomes differ slightly because the returns are smoother, which influences the calculations. The Tangency Portfolio, assuming a zero risk-free rate, offers a more optimal trade-off between risk and return by maximizing returns for each unit of risk taken. In general, the GMVP is more appropriate for risk-averse investors, while the Tangency Portfolio is better suited for investors who are aiming for higher returns.