# ================================
# 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
## Warning: package 'zoo' was built under R version 4.5.2
##
## 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(("C:/Users/jayde/Downloads/myetf4.csv"), 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 shows how to build two types of portfolios, the Global Minimum Variance Portfolio (GMVP) and the Tangency Portfolio, using four Taiwan ETFs.
Using daily data, the GMVP focuses on reducing risk as much as possible. It spreads the investment in a way that lowers overall volatility, but this usually leads to lower returns.
When monthly data is used instead, the results change slightly. Monthly data smooths out short-term ups and downs, which affects how the assets relate to each other and leads to different portfolio weights.
The Tangency Portfolio, on the other hand, offers a better balance between risk and return. Since the risk-free rate is assumed to be zero, it aims to maximize return for each unit of risk. This makes it more attractive for investors who are willing to accept some risk to earn higher returns.
Overall, the GMVP is more suitable for conservative investors who want to minimize risk, while the Tangency Portfolio is better for those seeking higher returns. The analysis also shows that the choice between daily and monthly data can influence portfolio decisions.