library(tidyverse)
library(quadprog) # For portfolio optimization
# Read the CSV file
prices <- read.csv("C:/tsogoo/ENJY/hicheel/dalgavar/myetf4.csv")
# Convert Index to Date
prices$Index <- as.Date(prices$Index, format = "%Y/%m/%d")
head(prices)
## Index tw0050 tw0056 tw006205 tw00646
## 1 2015-12-14 53.29 18.25 31.06 19.61
## 2 2015-12-15 53.33 18.38 31.59 19.63
## 3 2015-12-16 54.14 18.56 31.60 19.89
## 4 2015-12-17 54.77 18.81 32.23 20.05
## 5 2015-12-18 54.50 18.95 32.18 19.85
## 6 2015-12-21 54.41 19.02 33.00 19.64
str(prices)
## 'data.frame': 751 obs. of 5 variables:
## $ Index : Date, format: "2015-12-14" "2015-12-15" ...
## $ tw0050 : num 53.3 53.3 54.1 54.8 54.5 ...
## $ tw0056 : num 18.2 18.4 18.6 18.8 18.9 ...
## $ tw006205: num 31.1 31.6 31.6 32.2 32.2 ...
## $ tw00646 : num 19.6 19.6 19.9 20.1 19.9 ...
# Compute daily simple returns: r_t = (P_t - P_{t-1}) / P_{t-1}
daily_returns <- prices %>%
mutate(
r_0050 = tw0050 / lag(tw0050) - 1,
r_0056 = tw0056 / lag(tw0056) - 1,
r_006205 = tw006205 / lag(tw006205) - 1,
r_00646 = tw00646 / lag(tw00646) - 1
) %>%
select(Index, r_0050, r_0056, r_006205, r_00646) %>%
drop_na()
head(daily_returns)
## Index r_0050 r_0056 r_006205 r_00646
## 1 2015-12-15 0.0007506099 0.007123288 0.0170637476 0.001019888
## 2 2015-12-16 0.0151884493 0.009793254 0.0003165559 0.013245033
## 3 2015-12-17 0.0116364980 0.013469828 0.0199367089 0.008044243
## 4 2015-12-18 -0.0049297060 0.007442850 -0.0015513497 -0.009975062
## 5 2015-12-21 -0.0016513761 0.003693931 0.0254816656 -0.010579345
## 6 2015-12-22 0.0023892667 -0.003680336 0.0030303030 0.004073320
# Extract returns matrix
R_daily <- daily_returns %>% select(-Index) %>% as.matrix()
# Mean daily returns
mu_daily <- colMeans(R_daily)
cat("Mean Daily Returns:\n")
## Mean Daily Returns:
print(mu_daily)
## r_0050 r_0056 r_006205 r_00646
## 0.0004632227 0.0003846366 -0.0002118311 0.0002554122
# Covariance matrix of daily returns
Sigma_daily <- cov(R_daily)
cat("\nCovariance Matrix (Daily):\n")
##
## Covariance Matrix (Daily):
print(Sigma_daily)
## r_0050 r_0056 r_006205 r_00646
## r_0050 7.837060e-05 4.559164e-05 4.467258e-05 3.663388e-05
## r_0056 4.559164e-05 4.526413e-05 2.673674e-05 2.353543e-05
## r_006205 4.467258e-05 2.673674e-05 1.304184e-04 2.910367e-05
## r_00646 3.663388e-05 2.353543e-05 2.910367e-05 5.902892e-05
The GMVP minimizes portfolio variance subject to the constraint that weights sum to 1. The analytical solution is:
\[w = \frac{\Sigma^{-1} \mathbf{1}}{\mathbf{1}' \Sigma^{-1} \mathbf{1}}\]
# Number of assets
n <- ncol(Sigma_daily)
# Analytical GMVP solution
ones <- rep(1, n)
Sigma_inv <- solve(Sigma_daily)
w_gmvp_daily <- as.numeric(Sigma_inv %*% ones) / as.numeric(t(ones) %*% Sigma_inv %*% ones)
names(w_gmvp_daily) <- colnames(R_daily)
cat("GMVP Weights (Daily Returns):\n")
## GMVP Weights (Daily Returns):
print(round(w_gmvp_daily, 6))
## r_0050 r_0056 r_006205 r_00646
## -0.219358 0.728372 0.107623 0.383363
# GMVP expected daily return
gmvp_return_daily <- sum(w_gmvp_daily * mu_daily)
cat("GMVP Expected Daily Return:", round(gmvp_return_daily, 6), "\n")
## GMVP Expected Daily Return: 0.000254
# GMVP daily standard deviation
gmvp_sd_daily <- sqrt(t(w_gmvp_daily) %*% Sigma_daily %*% w_gmvp_daily)
cat("GMVP Daily Standard Deviation:", round(gmvp_sd_daily, 6), "\n")
## GMVP Daily Standard Deviation: 0.005905
# Annualized (approx 252 trading days)
cat("\nAnnualized Return:", round(gmvp_return_daily * 252, 6), "\n")
##
## Annualized Return: 0.063923
cat("Annualized Std Dev:", round(gmvp_sd_daily * sqrt(252), 6), "\n")
## Annualized Std Dev: 0.093738
# Add year-month column
prices_monthly <- prices %>%
mutate(YM = format(Index, "%Y-%m"))
# Get the last trading day price of each month
monthly_prices <- prices_monthly %>%
group_by(YM) %>%
slice_tail(n = 1) %>%
ungroup() %>%
arrange(Index)
# Compute monthly simple returns
monthly_returns <- monthly_prices %>%
mutate(
r_0050 = tw0050 / lag(tw0050) - 1,
r_0056 = tw0056 / lag(tw0056) - 1,
r_006205 = tw006205 / lag(tw006205) - 1,
r_00646 = tw00646 / lag(tw00646) - 1
) %>%
select(Index, YM, r_0050, r_0056, r_006205, r_00646) %>%
drop_na()
head(monthly_returns)
## # A tibble: 6 × 6
## Index YM r_0050 r_0056 r_006205 r_00646
## <date> <chr> <dbl> <dbl> <dbl> <dbl>
## 1 2016-01-30 2016-01 -0.0198 -0.0138 -0.173 -0.0389
## 2 2016-02-26 2016-02 0.0286 0.0435 -0.0276 -0.00363
## 3 2016-03-31 2016-03 0.0555 -0.00258 0.0828 0.0260
## 4 2016-04-29 2016-04 -0.0472 -0.0372 -0.0248 0.00964
## 5 2016-05-31 2016-05 0.0252 0.0166 0.00442 0.0221
## 6 2016-06-30 2016-06 0.0364 0.0296 -0.0256 -0.0261
cat("\nNumber of monthly observations:", nrow(monthly_returns), "\n")
##
## Number of monthly observations: 36
R_monthly <- monthly_returns %>% select(r_0050, r_0056, r_006205, r_00646) %>% as.matrix()
mu_monthly <- colMeans(R_monthly)
cat("Mean Monthly Returns:\n")
## Mean Monthly Returns:
print(mu_monthly)
## r_0050 r_0056 r_006205 r_00646
## 0.008819836 0.007086721 -0.005355481 0.004510630
Sigma_monthly <- cov(R_monthly)
cat("\nCovariance Matrix (Monthly):\n")
##
## Covariance Matrix (Monthly):
print(Sigma_monthly)
## r_0050 r_0056 r_006205 r_00646
## r_0050 0.0011751458 0.0008661004 0.0008472189 0.0003928466
## r_0056 0.0008661004 0.0009080806 0.0005553289 0.0003572509
## r_006205 0.0008472189 0.0005553289 0.0024412877 0.0006736296
## r_00646 0.0003928466 0.0003572509 0.0006736296 0.0008605161
Sigma_inv_m <- solve(Sigma_monthly)
w_gmvp_monthly <- as.numeric(Sigma_inv_m %*% ones) / as.numeric(t(ones) %*% Sigma_inv_m %*% ones)
names(w_gmvp_monthly) <- colnames(R_monthly)
cat("GMVP Weights (Monthly Returns):\n")
## GMVP Weights (Monthly Returns):
print(round(w_gmvp_monthly, 6))
## r_0050 r_0056 r_006205 r_00646
## 0.003184 0.474049 0.001204 0.521563
gmvp_return_monthly <- sum(w_gmvp_monthly * mu_monthly)
cat("GMVP Expected Monthly Return:", round(gmvp_return_monthly, 6), "\n")
## GMVP Expected Monthly Return: 0.005734
gmvp_sd_monthly <- sqrt(t(w_gmvp_monthly) %*% Sigma_monthly %*% w_gmvp_monthly)
cat("GMVP Monthly Standard Deviation:", round(gmvp_sd_monthly, 6), "\n")
## GMVP Monthly Standard Deviation: 0.024904
# Annualized
cat("\nAnnualized Return:", round(gmvp_return_monthly * 12, 6), "\n")
##
## Annualized Return: 0.068804
cat("Annualized Std Dev:", round(gmvp_sd_monthly * sqrt(12), 6), "\n")
## Annualized Std Dev: 0.086271
comparison <- data.frame(
ETF = c("0050", "0056", "006205", "00646"),
Daily_Weights = round(w_gmvp_daily, 4),
Monthly_Weights = round(w_gmvp_monthly, 4)
)
print(comparison)
## ETF Daily_Weights Monthly_Weights
## r_0050 0050 -0.2194 0.0032
## r_0056 0056 0.7284 0.4740
## r_006205 006205 0.1076 0.0012
## r_00646 00646 0.3834 0.5216
The tangency portfolio maximizes the Sharpe ratio. When the risk-free rate is zero, the weights are:
\[w = \frac{\Sigma^{-1} \mu}{\mathbf{1}' \Sigma^{-1} \mu}\]
w_tangency <- as.numeric(Sigma_inv_m %*% mu_monthly) / as.numeric(t(ones) %*% Sigma_inv_m %*% mu_monthly)
names(w_tangency) <- colnames(R_monthly)
cat("Tangency Portfolio Weights:\n")
## Tangency Portfolio Weights:
print(round(w_tangency, 6))
## r_0050 r_0056 r_006205 r_00646
## 1.305054 -0.157681 -0.847532 0.700159
tang_return <- sum(w_tangency * mu_monthly)
cat("Tangency Portfolio Expected Monthly Return:", round(tang_return, 6), "\n")
## Tangency Portfolio Expected Monthly Return: 0.01809
tang_sd <- sqrt(t(w_tangency) %*% Sigma_monthly %*% w_tangency)
cat("Tangency Portfolio Monthly Std Dev:", round(tang_sd, 6), "\n")
## Tangency Portfolio Monthly Std Dev: 0.044236
# Sharpe Ratio (Rf = 0)
sharpe <- tang_return / tang_sd
cat("Sharpe Ratio (monthly):", round(sharpe, 6), "\n")
## Sharpe Ratio (monthly): 0.40894
# Annualized
cat("\nAnnualized Return:", round(tang_return * 12, 6), "\n")
##
## Annualized Return: 0.21708
cat("Annualized Std Dev:", round(tang_sd * sqrt(12), 6), "\n")
## Annualized Std Dev: 0.153239
cat("Annualized Sharpe Ratio:", round(sharpe * sqrt(12), 6), "\n")
## Annualized Sharpe Ratio: 1.416609
summary_df <- data.frame(
Portfolio = c("GMVP (Daily)", "GMVP (Monthly)", "Tangency (Monthly)"),
w_0050 = round(c(w_gmvp_daily[1], w_gmvp_monthly[1], w_tangency[1]), 4),
w_0056 = round(c(w_gmvp_daily[2], w_gmvp_monthly[2], w_tangency[2]), 4),
w_006205 = round(c(w_gmvp_daily[3], w_gmvp_monthly[3], w_tangency[3]), 4),
w_00646 = round(c(w_gmvp_daily[4], w_gmvp_monthly[4], w_tangency[4]), 4)
)
print(summary_df)
## Portfolio w_0050 w_0056 w_006205 w_00646
## 1 GMVP (Daily) -0.2194 0.7284 0.1076 0.3834
## 2 GMVP (Monthly) 0.0032 0.4740 0.0012 0.5216
## 3 Tangency (Monthly) 1.3051 -0.1577 -0.8475 0.7002