# ── Packages ──────────────────────────────────────────────
library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.4     ✔ readr     2.1.5
## ✔ forcats   1.0.0     ✔ stringr   1.5.1
## ✔ ggplot2   4.0.2     ✔ tibble    3.2.1
## ✔ lubridate 1.9.5     ✔ tidyr     1.3.1
## ✔ purrr     1.2.1     
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(lubridate)

# ── Load Data ─────────────────────────────────────────────
prices <- read_csv("myetf4.csv") %>%
  rename(date      = Index,
         etf0050   = tw0050,
         etf0056   = tw0056,
         etf006205 = tw006205,
         etf00646  = tw00646) %>%
  mutate(date = as.Date(date))
## Rows: 751 Columns: 5
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (1): Index
## dbl (4): tw0050, tw0056, tw006205, tw00646
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
# ═══════════════════════════════════════════════════════════
# Q1: GMVP — DAILY RETURNS
# ═══════════════════════════════════════════════════════════

daily_ret <- prices %>%
  filter(date >= as.Date("2015-12-14"),
         date <= as.Date("2018-12-28")) %>%
  arrange(date) %>%
  mutate(
    r0050   = log(etf0050   / lag(etf0050)),
    r0056   = log(etf0056   / lag(etf0056)),
    r006205 = log(etf006205 / lag(etf006205)),
    r00646  = log(etf00646  / lag(etf00646))
  ) %>%
  drop_na()

R_daily   <- as.matrix(daily_ret[, c("r0050","r0056","r006205","r00646")])
cov_d     <- cov(R_daily)
mu_d      <- colMeans(R_daily)
ones      <- rep(1, 4)

# Analytical GMVP: w = Sigma^-1 * 1 / (1' * Sigma^-1 * 1)
w_gmvp_d  <- solve(cov_d) %*% ones
w_gmvp_d  <- w_gmvp_d / sum(w_gmvp_d)
names(w_gmvp_d) <- c("0050","0056","006205","00646")

ret_gmvp_d <- as.numeric(t(w_gmvp_d) %*% mu_d)
sd_gmvp_d  <- sqrt(as.numeric(t(w_gmvp_d) %*% cov_d %*% w_gmvp_d))

cat("=== Q1: GMVP — Daily Returns ===\n")
## === Q1: GMVP — Daily Returns ===
cat("Weights:\n"); print(round(w_gmvp_d, 6))
## Weights:
##              [,1]
## r0050   -0.224131
## r0056    0.729873
## r006205  0.108076
## r00646   0.386183
## attr(,"names")
## [1] "0050"   "0056"   "006205" "00646"
cat("Daily Return  :", round(ret_gmvp_d, 6), "\n")
## Daily Return  : 0.000226
cat("Daily Std Dev :", round(sd_gmvp_d,  6), "\n")
## Daily Std Dev : 0.005922
# ═══════════════════════════════════════════════════════════
# Q2: GMVP — MONTHLY RETURNS
# ═══════════════════════════════════════════════════════════

monthly_ret <- prices %>%
  filter(date >= as.Date("2015-12-14"),
         date <= as.Date("2018-12-28")) %>%
  mutate(ym = floor_date(date, "month")) %>%
  group_by(ym) %>%
  slice_tail(n = 1) %>%          # last trading day of each month
  ungroup() %>%
  arrange(ym) %>%
  mutate(
    r0050   = log(etf0050   / lag(etf0050)),
    r0056   = log(etf0056   / lag(etf0056)),
    r006205 = log(etf006205 / lag(etf006205)),
    r00646  = log(etf00646  / lag(etf00646))
  ) %>%
  drop_na()

R_monthly  <- as.matrix(monthly_ret[, c("r0050","r0056","r006205","r00646")])
cov_m      <- cov(R_monthly)
mu_m       <- colMeans(R_monthly)

w_gmvp_m   <- solve(cov_m) %*% ones
w_gmvp_m   <- w_gmvp_m / sum(w_gmvp_m)
names(w_gmvp_m) <- c("0050","0056","006205","00646")

ret_gmvp_m <- as.numeric(t(w_gmvp_m) %*% mu_m)
sd_gmvp_m  <- sqrt(as.numeric(t(w_gmvp_m) %*% cov_m %*% w_gmvp_m))

cat("\n=== Q2: GMVP — Monthly Returns ===\n")
## 
## === Q2: GMVP — Monthly Returns ===
cat("Weights:\n"); print(round(w_gmvp_m, 6))
## Weights:
##              [,1]
## r0050   -0.028541
## r0056    0.520687
## r006205 -0.001783
## r00646   0.509637
## attr(,"names")
## [1] "0050"   "0056"   "006205" "00646"
cat("Monthly Return  :", round(ret_gmvp_m, 6), "\n")
## Monthly Return  : 0.005304
cat("Monthly Std Dev :", round(sd_gmvp_m,  6), "\n")
## Monthly Std Dev : 0.025069
# ═══════════════════════════════════════════════════════════
# Q3: TANGENCY PORTFOLIO — Monthly Returns, Rf = 0
# ═══════════════════════════════════════════════════════════

Rf     <- 0
w_tp   <- solve(cov_m) %*% (mu_m - Rf)
w_tp   <- w_tp / sum(w_tp)
names(w_tp) <- c("0050","0056","006205","00646")

ret_tp <- as.numeric(t(w_tp) %*% mu_m)
sd_tp  <- sqrt(as.numeric(t(w_tp) %*% cov_m %*% w_tp))
sr_tp  <- (ret_tp - Rf) / sd_tp

cat("\n=== Q3: Tangency Portfolio — Monthly, Rf=0 ===\n")
## 
## === Q3: Tangency Portfolio — Monthly, Rf=0 ===
cat("Weights:\n"); print(round(w_tp, 6))
## Weights:
##              [,1]
## r0050    1.278969
## r0056   -0.087371
## r006205 -0.896758
## r00646   0.705159
## attr(,"names")
## [1] "0050"   "0056"   "006205" "00646"
cat("Monthly Return  :", round(ret_tp, 6), "\n")
## Monthly Return  : 0.018719
cat("Monthly Std Dev :", round(sd_tp,  6), "\n")
## Monthly Std Dev : 0.047094
cat("Sharpe Ratio    :", round(sr_tp,  6), "\n")
## Sharpe Ratio    : 0.397475
# ── Summary Table ─────────────────────────────────────────
cat("\n=== Summary: All Portfolios ===\n")
## 
## === Summary: All Portfolios ===
summary_tbl <- data.frame(
  Portfolio = c("GMVP (Daily)","GMVP (Monthly)","Tangency (Monthly)"),
  w_0050    = round(c(w_gmvp_d[1], w_gmvp_m[1], w_tp[1]), 4),
  w_0056    = round(c(w_gmvp_d[2], w_gmvp_m[2], w_tp[2]), 4),
  w_006205  = round(c(w_gmvp_d[3], w_gmvp_m[3], w_tp[3]), 4),
  w_00646   = round(c(w_gmvp_d[4], w_gmvp_m[4], w_tp[4]), 4),
  Return    = round(c(ret_gmvp_d, ret_gmvp_m, ret_tp), 6),
  Std_Dev   = round(c(sd_gmvp_d,  sd_gmvp_m,  sd_tp),  6)
)
print(summary_tbl)
##            Portfolio  w_0050  w_0056 w_006205 w_00646   Return  Std_Dev
## 1       GMVP (Daily) -0.2241  0.7299   0.1081  0.3862 0.000226 0.005922
## 2     GMVP (Monthly) -0.0285  0.5207  -0.0018  0.5096 0.005304 0.025069
## 3 Tangency (Monthly)  1.2790 -0.0874  -0.8968  0.7052 0.018719 0.047094