Portfolio Management Assignment
## 1. Data Collection and Processing.
```{r} #| message: false #| warning: false library(quantmod) library(PerformanceAnalytics)
library(frenchdata)install.packages(“quantmod”) library(quantmod) tickers <- c(“SPY”, “QQQ”, “EEM”, “IWM”, “EFA”, “TLT”, “IYR”, “GLD”) getSymbols(tickers, src = “yahoo”, from = “2010-01-01”, to = “2025-04-01”) prices <- do.call(merge, lapply(tickers, function(x) Ad(get(x)))) if(!require(PerformanceAnalytics)) install.packages(“PerformanceAnalytics”) library(PerformanceAnalytics) monthly_prices <- to.monthly(prices, indexAt = “lastof”, OHLC = FALSE) monthly_returns <- Return.calculate(monthly_prices, method = “discrete”) monthly_returns <- na.omit(monthly_returns) head(monthly_returns) install.packages(“frenchdata”) library(frenchdata) ff_download <- download_french_data(“Fama/French 3 Factors”) ff_raw <- ff_download\(subsets\)data[[1]] ff_raw\(date <- as.Date(paste0(ff_raw\)date, “01”), format = “%Y%m%d”) ff_factors <- ff_raw ff_factors[, c(“Mkt-RF”, “SMB”, “HML”, “RF”)] <- ff_raw[, c(“Mkt-RF”, “SMB”, “HML”, “RF”)] / 100 head(ff_factors) etf_ret_df <- data.frame(date = index(monthly_returns), coredata(monthly_returns)) final_data <- merge(etf_ret_df, ff_factors, by = “date”) head(final_data) etf_ret_df\(date <- as.Date(index(monthly_returns)) ff_factors\)date <- as.Date(ff_factors\(date) final_data <- merge(etf_ret_df, ff_factors, by = "date") nrow(final_data) head(final_data) etf_ret_df\)month_yr <- format(etf_ret_df\(date, "%Y-%m") ff_factors\)month_yr <- format(ff_factors\(date, "%Y-%m") final_data <- merge(etf_ret_df, ff_factors, by = "month_yr") nrow(final_data) head(final_data) train_data <- final_data[final_data\)month_yr >= “2020-03” & final_data\(month_yr <= "2025-02", ] returns_matrix <- as.matrix(train_data[, 3:10]) cov_matrix_capm <- cov(returns_matrix) ones <- rep(1, ncol(returns_matrix)) inv_cov <- solve(cov_matrix_capm) weights_capm <- (inv_cov %*% ones) / as.numeric(t(ones) %*% inv_cov %*% ones) print(weights_capm) betas <- matrix(NA, nrow = 8, ncol = 3) for(i in 1:8) { excess_ret <- returns_matrix[,i] - train_data\)RF model <- lm(excess_ret ~ train_data\(`Mkt-RF` + train_data\)SMB + train_data\(HML) betas[i,] <- coef(model)[2:4] } factor_cov <- cov(train_data[, c("Mkt-RF", "SMB", "HML")]) resid_var <- diag(apply(returns_matrix, 2, var)) cov_matrix_ff <- betas %*% factor_cov %*% t(betas) + resid_var inv_cov_ff <- solve(cov_matrix_ff) weights_ff <- (inv_cov_ff %*% ones) / as.numeric(t(ones) %*% inv_cov_ff %*% ones) print(weights_ff) march_2025_rets <- as.numeric(final_data[final_data\)month_yr == “2025-03”, 3:10]) realized_ret_capm <- sum(march_2025_rets * weights_capm) realized_ret_ff <- sum(march_2025_rets * weights_ff) print(realized_ret_capm_mar) print(realized_ret_ff_mar) returns_mar_2025 <- as.numeric(final_data[final_data\(month_yr == "2025-03", 3:10]) realized_ret_capm_mar <- sum(returns_mar_2025 * weights_capm) realized_ret_ff_mar <- sum(returns_mar_2025 * weights_ff) print(realized_ret_capm_mar) print(realized_ret_ff_mar) train_data_apr <- final_data[final_data\)month_yr >= “2020-04” & final_data\(month_yr <= "2025-03", ] returns_train_apr <- as.matrix(train_data_apr[, 3:10]) cov_apr <- cov(returns_train_apr) inv_cov_apr <- solve(cov_apr) ones_vec <- rep(1, 8) weights_apr_new <- (inv_cov_apr %*% ones_vec) / as.numeric(t(ones_vec) %*% inv_cov_apr %*% ones_vec) returns_apr_2025 <- as.numeric(final_data[final_data\)month_yr == “2025-04”, 3:10]) realized_ret_apr <- sum(returns_apr_2025 * weights_apr_new) print(realized_ret_apr) getSymbols(tickers, src = “yahoo”, from = “2010-01-01”, to = “2025-05-01”) prices <- do.call(merge, lapply(tickers, function(x) Ad(get(x)))) monthly_prices <- to.monthly(prices, indexAt = “lastof”, OHLC = FALSE) monthly_returns <- na.omit(Return.calculate(monthly_prices, method = “discrete”)) etf_ret_df <- data.frame(date = index(monthly_returns), coredata(monthly_returns)) etf_ret_df\(month_yr <- format(etf_ret_df\)date, “%Y-%m”) final_data <- merge(etf_ret_df, ff_factors, by = “month_yr”) train_apr <- final_data[final_data\(month_yr >= "2020-04" & final_data\)month_yr <= “2025-03”, ] ret_matrix_apr <- as.matrix(train_apr[, 3:10]) cov_apr <- cov(ret_matrix_apr) inv_cov_apr <- solve(cov_apr) ones_v <- rep(1, 8) w_apr <- (inv_cov_apr %% ones_v) / sum(inv_cov_apr %% ones_v) actual_apr_rets <- as.numeric(final_data[final_data$month_yr == “2025-04”, 3:10]) realized_ret_apr <- sum(actual_apr_rets * w_apr) print(realized_ret_apr)
3. Theoretical Background and Discussion
Chapter 5: Risk, Return, and the Historical Record
In this section, we analyzed the historical performance of the selected 8 ETFs. The core objective was to understand the trade-off between risk and return. We calculated the Holding Period Return (HPR) and examined the distribution of returns using mean and standard deviation. The standard deviation serves as a proxy for total risk, representing the volatility of the assets over the given timeframe.
Chapter 6: Capital Allocation to Risky Assets
Capital allocation involves dividing the investment budget between risk-free assets (represented by the RF rate from the Fama-French data) and the risky portfolio (our 8 ETFs). According to the theory, the investor’s degree of risk aversion (\(A\)) determines the optimal position on the Capital Allocation Line (CAL). Our focus was on constructing the Minimum Variance Portfolio (MVP), which is the point on the efficient frontier with the lowest possible risk, regardless of the expected return.
Chapter 7: Optimal Risky Portfolios
The goal of diversification is to reduce firm-specific risk by combining assets that are not perfectly positively correlated. By using the covariance matrix (\(\Sigma\)), we solved for the MVP weights using the following optimization formula:
$$w_{MVP} = \frac{\Sigma^{-1} \mathbf{1}}{\mathbf{1}’ \Sigma^{-1} \mathbf{1}}$$
where \(\mathbf{1}\) is a vector of ones. This mathematical approach ensures that the resulting portfolio has the smallest variance among all possible combinations of the 8 ETFs.
Chapter 8: Index Models and the Fama-French 3-Factor Model
While the Capital Asset Pricing Model (CAPM) assumes that only market risk (systematic risk) affects returns, the Fama-French 3-Factor Model provides a more comprehensive framework. It incorporates three factors:
Market Risk (Mkt-RF): The excess return of the market.
Size (SMB - Small Minus Big): The historical outperformance of small-cap stocks over large-cap stocks.
Value (HML - High Minus Low): The outperformance of high book-to-market (value) stocks over low book-to-market (growth) stocks.
By running regressions for each ETF against these factors, we estimated the factor loadings (\(\beta\)) and constructed a more robust covariance matrix, leading to a more stable MVP.