# ================================
# Portfolio Optimization Project
# GMVP & Tangency Portfolio Analysis
# ================================
# Libraries
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.2
# ================================
# 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")
Conclusion
This analysis shows how the Global Minimum Variance Portfolio (GMVP) and the Tangency Portfolio can be built using four Taiwan ETFs. From the daily data, the GMVP successfully minimizes risk by allocating weights in a way that reduces overall volatility, although this comes with relatively lower returns.
When the analysis is repeated using monthly data, the portfolio weights and results change slightly. This happens because monthly returns smooth out short-term fluctuations, which affects the covariance structure and ultimately the optimization results.
In comparison, the Tangency Portfolio delivers a better balance between risk and return. Since the risk-free rate is assumed to be zero, this portfolio focuses entirely on maximizing the return per unit of risk, making it more attractive for investors who are willing to take on some risk for higher potential returns.
Overall, the results suggest that while the GMVP is suitable for risk-averse investors, the Tangency Portfolio may be a better choice for those aiming for higher efficiency. It also highlights how the choice of data frequency can influence portfolio decisions.