# Load libraries
#install.packages("quadprog")
library(quadprog)
library(SIT)
library(ROI)
## ROI: R Optimization Infrastructure
## Registered solver plugins: nlminb.
## Default solver: auto.
library(ggplot2)
library(readr)
library(dplyr)
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
library(tidyquant)
## Loading required package: lubridate
## 
## Attaching package: 'lubridate'
## The following objects are masked from 'package:base':
## 
##     date, intersect, setdiff, union
## Loading required package: PerformanceAnalytics
## Loading required package: xts
## Loading required package: zoo
## 
## Attaching package: 'zoo'
## The following objects are masked from 'package:base':
## 
##     as.Date, as.Date.numeric
## 
## ######################### Warning from 'xts' package ##########################
## #                                                                             #
## # The dplyr lag() function breaks how base R's lag() function is supposed to  #
## # work, which breaks lag(my_xts). Calls to lag(my_xts) that you type or       #
## # source() into this session won't work correctly.                            #
## #                                                                             #
## # Use stats::lag() to make sure you're not using dplyr::lag(), or you can add #
## # conflictRules('dplyr', exclude = 'lag') to your .Rprofile to stop           #
## # dplyr from breaking base R's lag() function.                                #
## #                                                                             #
## # Code in packages is not affected. It's protected by R's namespace mechanism #
## # Set `options(xts.warn_dplyr_breaks_lag = FALSE)` to suppress this warning.  #
## #                                                                             #
## ###############################################################################
## 
## Attaching package: 'xts'
## The following objects are masked from 'package:dplyr':
## 
##     first, last
## 
## Attaching package: 'PerformanceAnalytics'
## The following object is masked from 'package:graphics':
## 
##     legend
## Loading required package: quantmod
## Loading required package: TTR
## Registered S3 method overwritten by 'quantmod':
##   method            from
##   as.zoo.data.frame zoo
library(lubridate)
library(timetk)
library(purrr)
library(xts)
library(dplyr)
library(quantmod)
library(zoo)

Question 1:Import Data

#Define the symbols we want to take 
symbols <- c("SPY", "QQQ", "EEM", "IWM", "EFA", "TLT", "IYR", "GLD")

#Extract data from yahoo finance and feed the data in prices
prices <- 
  getSymbols(symbols, src = 'yahoo', from = "2010-01-01", 
             auto.assign = TRUE, warnings = FALSE) %>% 
  map(~Ad(get(.))) %>% 
  reduce(merge) %>%
  `colnames<-`(symbols)

#Show the data prices
head(prices)
##                 SPY      QQQ      EEM      IWM      EFA      TLT      IYR
## 2010-01-04 88.11790 41.00547 32.42903 53.08979 37.99449 63.06114 28.77183
## 2010-01-05 88.35116 41.00547 32.66441 52.90723 38.02797 63.46841 28.84092
## 2010-01-06 88.41336 40.75812 32.73273 52.85744 38.18871 62.61879 28.82835
## 2010-01-07 88.78656 40.78462 32.54291 53.24745 38.04136 62.72412 29.08586
## 2010-01-08 89.08205 41.12031 32.80107 53.53788 38.34275 62.69606 28.89116
## 2010-01-11 89.20642 40.95246 32.73273 53.32213 38.65752 62.35194 29.02933
##               GLD
## 2010-01-04 109.80
## 2010-01-05 109.70
## 2010-01-06 111.51
## 2010-01-07 110.82
## 2010-01-08 111.37
## 2010-01-11 112.85

Question 2: Calculate weekly and monthly returns using log returns

#Change data from daily to weekly
prices_weekly <- to.weekly(prices, indexAt = "last", OHLC = FALSE)

#Change data from daily to monthly
prices_monthly <- to.monthly(prices, indexAt = "last", OHLC = FALSE)

#Calculate weekly retun
asset_returns_wk_xts <- na.omit(Return.calculate(prices_weekly))

#Calculate monthly return
asset_returns_mon_xts <- na.omit(Return.calculate(prices_monthly))

#Weekly return
head(asset_returns_wk_xts)
##                     SPY          QQQ         EEM         IWM          EFA
## 2010-01-15 -0.008117136 -0.015037604 -0.02893499 -0.01301908 -0.003493471
## 2010-01-22 -0.038982902 -0.036859682 -0.05578083 -0.03062212 -0.055740561
## 2010-01-29 -0.016665455 -0.031023488 -0.03357756 -0.02624321 -0.025802879
## 2010-02-05 -0.006797525  0.004440521 -0.02821289 -0.01397431 -0.019054778
## 2010-02-12  0.012938512  0.018147852  0.03333309  0.02952562  0.005244577
## 2010-02-19  0.028692893  0.024451690  0.02445406  0.03343163  0.022995169
##                      TLT          IYR          GLD
## 2010-01-15  2.004649e-02 -0.006304758 -0.004579349
## 2010-01-22  1.010088e-02 -0.041784529 -0.033285246
## 2010-01-29  3.369511e-03 -0.008447493 -0.011290465
## 2010-02-05 -5.449858e-05  0.003223196 -0.012080019
## 2010-02-12 -1.946053e-02 -0.007573717  0.022544905
## 2010-02-19 -8.205009e-03  0.050184585  0.022701796
#Monthly return
head(asset_returns_mon_xts)
##                    SPY         QQQ         EEM         IWM          EFA
## 2010-02-26  0.03119488  0.04603881  0.01776399  0.04475160  0.002667612
## 2010-03-31  0.06087977  0.07710931  0.08110871  0.08230646  0.063854206
## 2010-04-30  0.01546992  0.02242514 -0.00166157  0.05678478 -0.028045606
## 2010-05-28 -0.07945455 -0.07392326 -0.09393619 -0.07536643 -0.111927959
## 2010-06-30 -0.05174072 -0.05975729 -0.01398617 -0.07743395 -0.020619634
## 2010-07-30  0.06830041  0.07258270  0.10932453  0.06730889  0.116104338
##                     TLT         IYR          GLD
## 2010-02-26 -0.003424347  0.05457016  0.032748219
## 2010-03-31 -0.020573275  0.09748511 -0.004386396
## 2010-04-30  0.033217634  0.06388090  0.058834363
## 2010-05-28  0.051084116 -0.05683555  0.030513147
## 2010-06-30  0.057978195 -0.04670071  0.023553189
## 2010-07-30 -0.009463862  0.09404732 -0.050871157

Question 3:Convert monthly returns into tibble format. Hint: use as_tibble(., rownames = 'date') or tk_tbl(., rename_index = 'date').

#Monthly return to tibble format
monthly_return <- as_tibble(asset_returns_mon_xts,rownames = 'date')
tail(monthly_return)
## # A tibble: 6 × 9
##   date         SPY      QQQ      EEM      IWM     EFA      TLT      IYR      GLD
##   <chr>      <dbl>    <dbl>    <dbl>    <dbl>   <dbl>    <dbl>    <dbl>    <dbl>
## 1 2023-0…  0.0629   0.106    0.0913   0.0982   0.0900  0.0764   0.0998   0.0576 
## 2 2023-0… -0.0251  -0.00360 -0.0757  -0.0172  -0.0307 -0.0485  -0.0596  -0.0537 
## 3 2023-0…  0.0371   0.0949   0.0322  -0.0485   0.0313  0.0484  -0.0194   0.0792 
## 4 2023-0…  0.0160   0.00508 -0.00836 -0.0179   0.0294  0.00338  0.00919  0.00862
## 5 2023-0…  0.00462  0.0788  -0.0240  -0.00816 -0.0401 -0.0302  -0.0403  -0.0134 
## 6 2023-0…  0.0355   0.0324   0.0547   0.0732   0.0342 -0.00893  0.0274  -0.00274

Question 4: Download Fama French 3 factors data and change to digit numbers

ff_url <- "https://mba.tuck.dartmouth.edu/pages/faculty/ken.french/ftp/F-F_Research_Data_Factors_CSV.zip"

temp_file <- tempfile()
download.file(ff_url, temp_file)
ff_data_raw <- read_csv(unzip(temp_file), skip = 3)
## New names:
## • `` -> `...1`
## Warning: One or more parsing issues, call `problems()` on your data frame for details,
## e.g.:
##   dat <- vroom(...)
##   problems(dat)
## Rows: 1261 Columns: 5
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (1): ...1
## dbl (4): Mkt-RF, SMB, HML, RF
## 
## ℹ 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.
ff_data_raw <- ff_data_raw[c(1:1162),]
colnames(ff_data_raw) <- paste(c('date', "mkt_excess", "smb", "hml", "rf"))
attach(ff_data_raw)
ff_data_raw
## # A tibble: 1,162 × 5
##    date   mkt_excess   smb   hml    rf
##    <chr>       <dbl> <dbl> <dbl> <dbl>
##  1 192607       2.96 -2.56 -2.43  0.22
##  2 192608       2.64 -1.17  3.82  0.25
##  3 192609       0.36 -1.4   0.13  0.23
##  4 192610      -3.24 -0.09  0.7   0.32
##  5 192611       2.53 -0.1  -0.51  0.31
##  6 192612       2.62 -0.03 -0.05  0.28
##  7 192701      -0.06 -0.37  4.54  0.25
##  8 192702       4.18  0.04  2.94  0.26
##  9 192703       0.13 -1.65 -2.61  0.3 
## 10 192704       0.46  0.3   0.81  0.25
## # ℹ 1,152 more rows
#Convert into xts data
ff_data <- ff_data_raw %>% mutate(date = as.character(date)) %>%
  mutate(date = ymd(parse_date(date, format = "%Y%m"))) %>%
  mutate(date = rollback(date))
ff_data
## # A tibble: 1,162 × 5
##    date       mkt_excess   smb   hml    rf
##    <date>          <dbl> <dbl> <dbl> <dbl>
##  1 1926-06-30       2.96 -2.56 -2.43  0.22
##  2 1926-07-31       2.64 -1.17  3.82  0.25
##  3 1926-08-31       0.36 -1.4   0.13  0.23
##  4 1926-09-30      -3.24 -0.09  0.7   0.32
##  5 1926-10-31       2.53 -0.1  -0.51  0.31
##  6 1926-11-30       2.62 -0.03 -0.05  0.28
##  7 1926-12-31      -0.06 -0.37  4.54  0.25
##  8 1927-01-31       4.18  0.04  2.94  0.26
##  9 1927-02-28       0.13 -1.65 -2.61  0.3 
## 10 1927-03-31       0.46  0.3   0.81  0.25
## # ℹ 1,152 more rows
ff_data_xts <- xts(ff_data[, -1], 
                       order.by = as.Date(as.numeric(ff_data$date)))
head(ff_data_xts,10)
##            mkt_excess   smb   hml   rf
## 1926-06-30       2.96 -2.56 -2.43 0.22
## 1926-07-31       2.64 -1.17  3.82 0.25
## 1926-08-31       0.36 -1.40  0.13 0.23
## 1926-09-30      -3.24 -0.09  0.70 0.32
## 1926-10-31       2.53 -0.10 -0.51 0.31
## 1926-11-30       2.62 -0.03 -0.05 0.28
## 1926-12-31      -0.06 -0.37  4.54 0.25
## 1927-01-31       4.18  0.04  2.94 0.26
## 1927-02-28       0.13 -1.65 -2.61 0.30
## 1927-03-31       0.46  0.30  0.81 0.25
tail(ff_data_xts)
##            mkt_excess   smb   hml   rf
## 2022-10-31       4.60 -3.40  1.38 0.29
## 2022-11-30      -6.41 -0.68  1.32 0.33
## 2022-12-31       6.65  5.02 -4.05 0.35
## 2023-01-31      -2.58  1.21 -0.78 0.34
## 2023-02-28       2.51 -5.59 -9.01 0.36
## 2023-03-31       0.61 -3.34 -0.03 0.35

Question 5: Merge monthly return data in question 3 and 4 into tibble format

monthly_return_xts <- xts(monthly_return[,-1], order.by = as.Date(monthly_return$date))
merged <- merge(x = ff_data_xts, y = monthly_return_xts)
head(merged, 5)
##            mkt_excess   smb   hml   rf SPY QQQ EEM IWM EFA TLT IYR GLD
## 1926-06-30       2.96 -2.56 -2.43 0.22  NA  NA  NA  NA  NA  NA  NA  NA
## 1926-07-31       2.64 -1.17  3.82 0.25  NA  NA  NA  NA  NA  NA  NA  NA
## 1926-08-31       0.36 -1.40  0.13 0.23  NA  NA  NA  NA  NA  NA  NA  NA
## 1926-09-30      -3.24 -0.09  0.70 0.32  NA  NA  NA  NA  NA  NA  NA  NA
## 1926-10-31       2.53 -0.10 -0.51 0.31  NA  NA  NA  NA  NA  NA  NA  NA

Question 6: Compute covariance matrix for the 8-asset portfolio based on CAPM model

data <- na.omit(merged["2010-02-28/2014-12-31"])
head(data)
##            mkt_excess  smb   hml   rf         SPY          QQQ         EEM
## 2010-03-31       2.00 4.87  2.89 0.01  0.06087977  0.077109311  0.08110871
## 2010-04-30      -7.89 0.09 -2.44 0.01  0.01546992  0.022425137 -0.00166157
## 2010-06-30       6.93 0.22 -0.33 0.01 -0.05174072 -0.059757290 -0.01398617
## 2010-08-31       9.54 3.97 -3.18 0.01 -0.04498073 -0.051298482 -0.03236697
## 2010-09-30       3.88 1.19 -2.51 0.01  0.08955493  0.131727546  0.11757384
## 2010-11-30       6.82 0.69  3.76 0.01  0.00000000 -0.001725048 -0.02905442
##                    IWM         EFA         TLT         IYR          GLD
## 2010-03-31  0.08230646  0.06385421 -0.02057328  0.09748511 -0.004386396
## 2010-04-30  0.05678478 -0.02804561  0.03321763  0.06388090  0.058834363
## 2010-06-30 -0.07743395 -0.02061963  0.05797820 -0.04670071  0.023553189
## 2010-08-31 -0.07443866 -0.03795033  0.08390980 -0.01297181  0.057061253
## 2010-09-30  0.12445308  0.09971961 -0.02520427  0.04629667  0.047755584
## 2010-11-30  0.03485089 -0.04823713 -0.01689359 -0.01582959  0.021112978
# Create an empty list to store the results
result_list <- list()
y <- NULL
# Perform the calculation using a for loop
for (symbol in symbols) {
  diff <- data[,symbol] - data$rf
  result_name <- paste0(symbol, "_rf")
  assign(result_name, diff)
  y <- cbind(y, get(result_name))
}
#Calculating Beta
n <- nrow(y)
one_vec <- rep(1,n)
x <- NULL
x <- cbind(one_vec,data$mkt_excess)
beta_hat = solve(t(x)%*%x)%*%t(x) %*% y
beta_hat
##                     SPY           QQQ          EEM          IWM           EFA
## one_vec     0.011516072  0.0119783568  0.002191876  0.015013712  0.0028633091
## mkt_excess -0.001750035 -0.0009771542 -0.001029281 -0.003149498 -0.0004973766
##                    TLT          IYR          GLD
## one_vec    0.005430750  0.012371487  0.006062257
## mkt_excess 0.001633657 -0.001879846 -0.004148860
E_hat = y - x %*% beta_hat
# Excluding constant term
beta_hat = as.matrix(beta_hat[-1,])
diagD_hat = diag(t(E_hat) %*% E_hat)/(n-2)
# Covariance matrix by single factor model 
cov = as.numeric(var(data$mkt_excess))*beta_hat%*%t(beta_hat) + diag(diagD_hat)

Question 7: Compute covariance matrix for the 8-asset portfolio based on FF 3-factor model

#separate data to 2 groups:
t <- dim(data)[1]
markets <- data[,c(2,3,4)]
stocks <- data[,c(-1,-2,-3,-4)]
head(stocks)
##                    SPY          QQQ         EEM         IWM         EFA
## 2010-03-31  0.06087977  0.077109311  0.08110871  0.08230646  0.06385421
## 2010-04-30  0.01546992  0.022425137 -0.00166157  0.05678478 -0.02804561
## 2010-06-30 -0.05174072 -0.059757290 -0.01398617 -0.07743395 -0.02061963
## 2010-08-31 -0.04498073 -0.051298482 -0.03236697 -0.07443866 -0.03795033
## 2010-09-30  0.08955493  0.131727546  0.11757384  0.12445308  0.09971961
## 2010-11-30  0.00000000 -0.001725048 -0.02905442  0.03485089 -0.04823713
##                    TLT         IYR          GLD
## 2010-03-31 -0.02057328  0.09748511 -0.004386396
## 2010-04-30  0.03321763  0.06388090  0.058834363
## 2010-06-30  0.05797820 -0.04670071  0.023553189
## 2010-08-31  0.08390980 -0.01297181  0.057061253
## 2010-09-30 -0.02520427  0.04629667  0.047755584
## 2010-11-30 -0.01689359 -0.01582959  0.021112978
#Compute the beta
stocks <- as.matrix(stocks)
n <- dim(stocks)[2]
one_vec <- rep(1,t)
p <- as.matrix(cbind(one_vec,markets))
beta <- solve(t(p)%*%p)%*%t(p)%*%stocks
#Compute the residual
res <- stocks-p%*%beta
diag.d <- diag(t(res)%*%res)/(t-6)
diag.d
##         SPY         QQQ         EEM         IWM         EFA         TLT 
## 0.001630382 0.002268298 0.004019548 0.003558772 0.002644977 0.001882766 
##         IYR         GLD 
## 0.002277914 0.002220032
#Compute the R-square
retvar <- apply(stocks,2,var)
rsq <- 1-diag(t(res)%*%res)/((t-1)/retvar)
res.stdev <- sqrt(diag.d)
factor.cov <- var(stocks)*t(beta)%*%beta+diag(diag.d)
stdev <- sqrt(diag(factor.cov))
factor.cor <- factor.cov/(stdev%*%t(stdev))
factor.cor
##           SPY        QQQ       EEM       IWM       EFA         TLT        IYR
## SPY 1.0000000 0.18956066 0.4269597 0.4503113 0.4463893 0.124000898 0.36704794
## QQQ 0.1895607 1.00000000 0.2415772 0.2590624 0.2578596 0.067134237 0.20381438
## EEM 0.4269597 0.24157722 1.0000000 0.6117628 0.6983932 0.164988373 0.57866068
## IWM 0.4503113 0.25906240 0.6117628 1.0000000 0.5855178 0.172283837 0.54474306
## EFA 0.4463893 0.25785956 0.6983932 0.5855178 1.0000000 0.170944311 0.54231647
## TLT 0.1240009 0.06713424 0.1649884 0.1722838 0.1709443 1.000000000 0.08561446
## IYR 0.3670479 0.20381438 0.5786607 0.5447431 0.5423165 0.085614455 1.00000000
## GLD 0.1366439 0.09136662 0.3079442 0.2769033 0.1889609 0.002219785 0.25878430
##             GLD
## SPY 0.136643941
## QQQ 0.091366618
## EEM 0.307944200
## IWM 0.276903338
## EFA 0.188960921
## TLT 0.002219785
## IYR 0.258784296
## GLD 1.000000000
#Compute the MVP monthly return of 8 assets 
one <- rep(1,8)
top.mat <- solve(factor.cov)%*%one
bot.mat <- t(one)%*%top.mat
MVP_FF3 <- top.mat/as.numeric(bot.mat)
MVP_FF3
##             [,1]
## SPY  0.337667339
## QQQ  0.295044388
## EEM -0.055232522
## IWM -0.042329375
## EFA -0.008957536
## TLT  0.401070145
## IYR  0.059505929
## GLD  0.013231631

Question 8: Compute global minimum variance portfolio weights based on question 6 and 7.

# Compute global minimum variance portfolio weights based on CAPM model
cov_matrix_capm <- cov
dmat_capm <- 2 * cov_matrix_capm
dvec_capm <- rep(0, ncol(cov_matrix_capm))
Amat_capm <- matrix(1, nrow = 1, ncol = ncol(cov_matrix_capm))
bvec_capm <- 1

weights_capm <- solve.QP(Dmat = dmat_capm, dvec = dvec_capm, Amat = t(Amat_capm), bvec = bvec_capm)$solution

# Compute global minimum variance portfolio weights based on FF 3-factor model
cov_matrix_ff3 <- factor.cov
dmat_ff3 <- 2 * cov_matrix_ff3
dvec_ff3 <- rep(0, ncol(cov_matrix_ff3))
Amat_ff3 <- matrix(1, nrow = 1, ncol = ncol(cov_matrix_ff3))
bvec_ff3 <- 1

weights_ff3 <- solve.QP(Dmat = dmat_ff3, dvec = dvec_ff3, Amat = t(Amat_ff3), bvec = bvec_ff3)$solution

# Print the weights
weights_capm
## [1] 0.18350274 0.13750631 0.07107109 0.07686587 0.11449132 0.19168215 0.12989921
## [8] 0.09498132
weights_ff3
## [1]  0.337667339  0.295044388 -0.055232522 -0.042329375 -0.008957536
## [6]  0.401070145  0.059505929  0.013231631

Question 9: Using SIT package to backtest the performance of GMV (global minimum variance) portfolios based on CAPM model and Fama-French three-factor model. You have to use historical six monthly returns (t-6 to t-1) to estimate the optimal weights which will be assigned to assets to construct portfolios for investment in next month (t). By repeating this process every month, you can derive the monthly returns of GMV portfolios based on CAPM and Fama-French three-factor model.

# compute the covariance matrix based on the CAPM model
compute_covariance_capm <- function(data) {
  n <- nrow(data)
  one_vec <- rep(1, n)
  x <- cbind(one_vec, data$mkt_excess)
  y <- data[, -c(1:4)]
  beta_hat <- solve(t(x) %*% x) %*% t(x) %*% y
  E_hat <- y - x %*% beta_hat
  diagD_hat <- diag(t(E_hat) %*% E_hat) / (n - 2)
  cov_matrix_capm <- as.numeric(var(data$mkt_excess)) * t(beta_hat) %*% beta_hat + diag(diagD_hat)
  return(cov_matrix_capm)
}

cov_matrix_capm <- compute_covariance_capm(data)
head(cov_matrix_capm)
##             SPY         QQQ          EEM         IWM          EFA          TLT
## SPY 0.004958974 0.003569903 0.0012834055 0.004336892 0.0014263212 0.0019702398
## QQQ 0.003569903 0.005741219 0.0013114008 0.004433055 0.0014657846 0.0020547486
## EEM 0.001283406 0.001311401 0.0044943580 0.001603637 0.0005242440 0.0007120599
## IWM 0.004336892 0.004433055 0.0016036369 0.008627073 0.0017720490 0.0024113010
## EFA 0.001426321 0.001465785 0.0005242440 0.001772049 0.0031973149 0.0008186382
## TLT 0.001970240 0.002054749 0.0007120599 0.002411301 0.0008186382 0.0029256492
##             IYR          GLD
## SPY 0.003685655 0.0022706292
## QQQ 0.003778001 0.0022887402
## EEM 0.001358546 0.0008526075
## IWM 0.004590744 0.0028754286
## EFA 0.001509493 0.0009170364
## TLT 0.002083924 0.0011571318
# compute the covariance matrix based on the Fama-French three-factor model
compute_covariance_ff3 <- function(data) {
  markets <- data[, c(2, 3, 4)]
  stocks <- data[, c(-1, -2, -3, -4)]
  n <- nrow(stocks)
  one_vec <- rep(1, n)
  p <- cbind(one_vec, markets)
  beta <- solve(t(p) %*% p) %*% t(p) %*% stocks
  res <- stocks - p %*% beta
  diag_d <- diag(t(res) %*% res) / (n - 6)
  ret_var <- apply(stocks, 2, var)
  stdev <- sqrt(diag_d)
  factor_cov <- var(stocks) * t(beta) %*% beta + diag(diag_d)
  return(factor_cov)
}
cov_matrix_ff3 <- compute_covariance_ff3(data)
head(cov_matrix_ff3)
##              SPY          QQQ         EEM          IWM          EFA
## SPY 0.0024217521 0.0004756909 0.003395022 0.0025575684 0.0023895342
## QQQ 0.0004756909 0.0026003008 0.001990484 0.0015246344 0.0014303082
## EEM 0.0033950218 0.0019904838 0.026108487 0.0114083668 0.0122751112
## IWM 0.0025575684 0.0015246344 0.011408367 0.0133198423 0.0073506249
## EFA 0.0023895342 0.0014303082 0.012275111 0.0073506249 0.0118323000
## TLT 0.0002771951 0.0001555076 0.001210988 0.0009032124 0.0008446661
##              TLT          IYR          GLD
## SPY 0.0002771951 0.0017674247 0.0015162480
## QQQ 0.0001555076 0.0010169507 0.0010505443
## EEM 0.0012109884 0.0091488839 0.0112196138
## IWM 0.0009032124 0.0061516912 0.0072059751
## EFA 0.0008446661 0.0057721907 0.0046346998
## TLT 0.0020634389 0.0003805364 0.0000227364
# compute the GMV portfolio weights
compute_gmv_weights <- function(cov_matrix) {
  n <- nrow(cov_matrix)
  dvec <- rep(0, n)
  Amat <- matrix(1, 1, n)
  bvec <- 1
  gmv_weights <- solve.QP(Dmat = 2 * cov_matrix, dvec = dvec, Amat = t(Amat), bvec = bvec)$solution
  return(gmv_weights)
}

gmv_weights_capm <- compute_gmv_weights(cov_matrix_capm)
head(gmv_weights_capm)
## [1]  0.01248232 -0.01100338  0.22719217 -0.10019041  0.32583838  0.36169868
gmv_weights_ff3 <- compute_gmv_weights(cov_matrix_ff3)
head(gmv_weights_ff3)
## [1]  0.337667339  0.295044388 -0.055232522 -0.042329375 -0.008957536
## [6]  0.401070145
# Compute the GMV portfolio weights
compute_gmv_weights <- function(cov_matrix) {
  n <- nrow(cov_matrix)
  dvec <- rep(0, n)
  Amat <- matrix(1, 1, n)
  bvec <- 1
  gmv_weights <- solve.QP(Dmat = 2 * cov_matrix, dvec = dvec, Amat = t(Amat), bvec = bvec)$solution
  return(gmv_weights)
}

gmv_weights_capm <- compute_gmv_weights(cov_matrix_capm)
gmv_weights_ff3 <- compute_gmv_weights(cov_matrix_ff3)

# Calculate portfolio returns based on GMV weights
calculate_portfolio_returns <- function(gmv_weights, data) {
  returns <- xts()
  for (i in 1:(nrow(data) - 6)) {
    weights <- gmv_weights
    returns_month <- data[i:(i + 5), -(1:4)]
    portfolio_returns <- Return.portfolio(returns_month, weights = weights, geometric = TRUE)
    returns <- rbind(returns, portfolio_returns)
  }
  return(returns)
}

portfolio_returns_capm <- calculate_portfolio_returns(gmv_weights_capm, data)
portfolio_returns_ff3 <- calculate_portfolio_returns(gmv_weights_ff3, data)

# Print the results
head(gmv_weights_capm)
## [1]  0.01248232 -0.01100338  0.22719217 -0.10019041  0.32583838  0.36169868
head(gmv_weights_ff3)
## [1]  0.337667339  0.295044388 -0.055232522 -0.042329375 -0.008957536
## [6]  0.401070145
head(portfolio_returns_capm)
##                            [,1]
## 2010-03-31 08:00:00 0.019559229
## 2010-04-30 08:00:00 0.005734082
## 2010-04-30 08:00:00 0.007427358
## 2010-06-30 08:00:00 0.026060477
## 2010-06-30 08:00:00 0.026833074
## 2010-06-30 08:00:00 0.025309255
head(portfolio_returns_ff3)
##                             [,1]
## 2010-03-31 08:00:00  0.032263561
## 2010-04-30 08:00:00  0.027540167
## 2010-04-30 08:00:00  0.027681794
## 2010-06-30 08:00:00 -0.012113765
## 2010-06-30 08:00:00 -0.009685917
## 2010-06-30 08:00:00 -0.010081279