Load Libraries

library(tidyverse)
library(quadprog)  # For portfolio optimization

Read Data

# 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 ...

Q1. Global Minimum Variance Portfolio (GMVP) — Daily Returns

Compute Daily Returns

# 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

Compute Covariance Matrix and Mean Returns (Daily)

# 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

Solve for GMVP Weights (Daily)

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 Return and Standard Deviation (Daily)

# 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

Q2. GMVP Using Monthly Returns

Compute Monthly Returns

# 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

Covariance Matrix and Mean Returns (Monthly)

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

Solve for GMVP Weights (Monthly)

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 and Standard Deviation (Monthly)

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: Daily vs Monthly GMVP Weights

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

Q3. Tangency Portfolio (Based on Monthly Returns, Rf = 0)

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

Tangency Portfolio Return and Standard Deviation

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 Table

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