Before running the analysis, make sure all required packages are
installed. Run the install.packages lines
once in the R Console (not inside the Rmarkdown
file).
# Run these lines in the R Console once (not inside Rmarkdown)
install.packages("quantmod")
install.packages("PerformanceAnalytics")
install.packages("quadprog")
install.packages("tidyverse")
install.packages("xts")# Load all required libraries
library(quantmod) # Download ETF/stock data from Yahoo Finance
library(PerformanceAnalytics) # Portfolio performance analysis
library(quadprog) # Quadratic programming for MVP optimization
library(tidyverse) # Data manipulation
library(xts) # Time series objectsExplanation: We download daily price data for 8 ETFs
from Yahoo Finance using the getSymbols() function from the
quantmod library. We use adjusted prices,
which account for dividends and stock splits, making them more accurate
for return calculations.
The 8 ETFs represent different asset classes:
# Define the list of ETF tickers
tickers <- c("SPY", "QQQ", "EEM", "IWM", "EFA", "TLT", "IYR", "GLD")
# Download daily price data from Yahoo Finance
getSymbols(tickers,
src = "yahoo",
from = "2010-01-01",
to = "2025-12-31",
auto.assign = TRUE)## [1] "SPY" "QQQ" "EEM" "IWM" "EFA" "TLT" "IYR" "GLD"
# Extract only the Adjusted Price column from each ETF
prices_daily <- merge(Ad(SPY), Ad(QQQ), Ad(EEM), Ad(IWM),
Ad(EFA), Ad(TLT), Ad(IYR), Ad(GLD))
# Rename columns for clarity
colnames(prices_daily) <- tickers
# Preview the first few rows
head(prices_daily)## SPY QQQ EEM IWM EFA TLT IYR
## 2010-01-04 84.79637 40.29079 30.35151 51.36656 35.12844 56.13518 26.76810
## 2010-01-05 85.02084 40.29079 30.57180 51.18993 35.15939 56.49773 26.83237
## 2010-01-06 85.08071 40.04776 30.63577 51.14177 35.30801 55.74140 26.82070
## 2010-01-07 85.43986 40.07380 30.45811 51.51911 35.17179 55.83514 27.06027
## 2010-01-08 85.72417 40.40363 30.69973 51.80011 35.45044 55.81017 26.87912
## 2010-01-11 85.84389 40.23870 30.63577 51.59136 35.74147 55.50390 27.00768
## 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
# Check the dimensions of the dataset
cat("Dimensions of daily price data:", dim(prices_daily), "\n")## Dimensions of daily price data: 4023 8
cat("Data period:", as.character(index(prices_daily)[1]),
"to", as.character(tail(index(prices_daily), 1)), "\n")## Data period: 2010-01-04 to 2025-12-30
Explanation: Monthly returns are calculated using the discrete return formula:
\[R_t = \frac{P_t - P_{t-1}}{P_{t-1}}\]
Where: - \(P_t\) = Adjusted closing price at the end of month \(t\) - \(P_{t-1}\) = Adjusted closing price at the end of month \(t-1\)
Steps: 1. Convert daily prices to end-of-month prices 2. Compute the percentage change between consecutive months
# Step 1: Convert daily prices to end-of-month prices
prices_monthly <- do.call(merge, lapply(tickers, function(ticker) {
to.monthly(prices_daily[, ticker],
indexAt = "lastof", # Use the last trading day of each month
OHLC = FALSE) # Return only one price column (not OHLC)
}))
colnames(prices_monthly) <- tickers
# Step 2: Calculate discrete monthly returns
# Return = (Price this month / Price last month) - 1
returns_monthly <- na.omit(Return.calculate(prices_monthly, method = "discrete"))
# Preview the first few rows
head(returns_monthly)## SPY QQQ EEM IWM EFA
## 2010-02-28 0.03119461 0.04603911 0.017764126 0.04475137 0.002668209
## 2010-03-31 0.06088005 0.07710868 0.081108492 0.08230700 0.063853837
## 2010-04-30 0.01546981 0.02242583 -0.001661366 0.05678475 -0.028045888
## 2010-05-31 -0.07945424 -0.07392373 -0.093936287 -0.07536661 -0.111927916
## 2010-06-30 -0.05174109 -0.05975686 -0.013986430 -0.07743393 -0.020619291
## 2010-07-31 0.06830056 0.07258291 0.109324932 0.06730895 0.116103993
## TLT IYR GLD
## 2010-02-28 -0.003424499 0.05457064 0.032748219
## 2010-03-31 -0.020573285 0.09748471 -0.004386396
## 2010-04-30 0.033218146 0.06388077 0.058834363
## 2010-05-31 0.051083437 -0.05683525 0.030513147
## 2010-06-30 0.057978558 -0.04670072 0.023553189
## 2010-07-31 -0.009464108 0.09404729 -0.050871157
## Number of months: 191
cat("Period:", as.character(index(returns_monthly)[1]),
"to", as.character(tail(index(returns_monthly), 1)), "\n\n")## Period: 2010-02-28 to 2025-12-31
## Average Monthly Return (%):
## SPY QQQ EEM IWM EFA TLT IYR GLD
## 1.2135 1.6056 0.5004 1.0190 0.6712 0.2909 0.8069 0.7987
Explanation: The Fama-French 3-Factor model extends CAPM by adding two additional risk factors beyond the market return:
The data is downloaded directly from Kenneth French’s data library at Dartmouth, and all values are converted from percentages to decimals (divided by 100).
# URL of the Fama-French 3 Factors monthly data (zip file)
ff_url <- "https://mba.tuck.dartmouth.edu/pages/faculty/ken.french/ftp/F-F_Research_Data_Factors_CSV.zip"
# Download the zip file to a temporary location
temp_file <- tempfile(fileext = ".zip")
download.file(ff_url, destfile = temp_file, mode = "wb")
# Extract the CSV from the zip archive
unzip(temp_file, exdir = tempdir())
# Read the CSV file (skip the first 3 header lines)
ff_raw <- read.csv(file.path(tempdir(), "F-F_Research_Data_Factors.CSV"),
skip = 3,
header = TRUE,
stringsAsFactors = FALSE)
# Preview raw data
head(ff_raw, 10)## X Mkt.RF SMB HML RF
## 1 192607 2.89 -2.55 -2.39 0.22
## 2 192608 2.64 -1.14 3.81 0.25
## 3 192609 0.38 -1.36 0.05 0.23
## 4 192610 -3.27 -0.14 0.82 0.32
## 5 192611 2.54 -0.11 -0.61 0.31
## 6 192612 2.62 -0.07 0.06 0.28
## 7 192701 -0.05 -0.32 4.58 0.25
## 8 192702 4.17 0.07 2.72 0.26
## 9 192703 0.14 -1.77 -2.38 0.30
## 10 192704 0.47 0.39 0.65 0.25
# Clean the Fama-French data
# The first column contains dates in YYYYMM format
# Identify blank rows (separator between monthly and annual data)
blank_rows <- which(trimws(ff_raw[, 1]) == "" | is.na(ff_raw[, 1]) |
nchar(trimws(ff_raw[, 1])) != 6)
# Keep only monthly data (rows before the first blank row)
if (length(blank_rows) > 0) {
ff_monthly_raw <- ff_raw[1:(blank_rows[1] - 1), ]
} else {
ff_monthly_raw <- ff_raw
}
# Rename columns
colnames(ff_monthly_raw) <- c("Date", "Mkt_RF", "SMB", "HML", "RF")
# Convert Date column to proper Date format
ff_monthly_raw$Date <- as.Date(paste0(ff_monthly_raw$Date, "01"), format = "%Y%m%d")
# Convert values from percentage to decimal (divide by 100)
ff_monthly_raw$Mkt_RF <- as.numeric(ff_monthly_raw$Mkt_RF) / 100
ff_monthly_raw$SMB <- as.numeric(ff_monthly_raw$SMB) / 100
ff_monthly_raw$HML <- as.numeric(ff_monthly_raw$HML) / 100
ff_monthly_raw$RF <- as.numeric(ff_monthly_raw$RF) / 100
# Remove rows with missing values
ff_monthly_raw <- na.omit(ff_monthly_raw)
# Convert to xts object for merging with ETF return data
ff_xts <- xts(ff_monthly_raw[, c("Mkt_RF", "SMB", "HML", "RF")],
order.by = ff_monthly_raw$Date)
# Preview cleaned data
head(ff_xts)## Mkt_RF SMB HML RF
## 1926-07-01 0.0289 -0.0255 -0.0239 0.0022
## 1926-08-01 0.0264 -0.0114 0.0381 0.0025
## 1926-09-01 0.0038 -0.0136 0.0005 0.0023
## 1926-10-01 -0.0327 -0.0014 0.0082 0.0032
## 1926-11-01 0.0254 -0.0011 -0.0061 0.0031
## 1926-12-01 0.0262 -0.0007 0.0006 0.0028
## Mkt_RF SMB HML RF
## 2025-09-01 0.0339 -0.0185 -0.0105 0.0033
## 2025-10-01 0.0196 -0.0055 -0.0310 0.0037
## 2025-11-01 -0.0013 0.0038 0.0376 0.0030
## 2025-12-01 -0.0036 -0.0106 0.0242 0.0034
## 2026-01-01 0.0102 0.0220 0.0372 0.0030
## 2026-02-01 -0.0117 0.0014 0.0283 0.0028
Explanation: We merge the monthly ETF returns (from Question 2) with the Fama-French factor data (from Question 3) by matching dates. Since the two datasets may use different date formats (end-of-month vs. beginning-of-month), we first align the date indices before merging.
# Align date indices: convert ETF return dates to beginning-of-month format
# to match the Fama-French date format
index(returns_monthly) <- as.Date(format(index(returns_monthly), "%Y-%m-01"))
# Merge the two datasets using inner join (keep only dates present in both)
data_merged <- merge(returns_monthly, ff_xts, join = "inner")
# Check the merged dataset
cat("Dimensions of merged dataset:", dim(data_merged), "\n")## Dimensions of merged dataset: 191 12
## Column names: SPY QQQ EEM IWM EFA TLT IYR GLD Mkt_RF SMB HML RF
## SPY QQQ EEM IWM EFA
## 2010-02-01 0.03119461 0.04603911 0.017764126 0.04475137 0.002668209
## 2010-03-01 0.06088005 0.07710868 0.081108492 0.08230700 0.063853837
## 2010-04-01 0.01546981 0.02242583 -0.001661366 0.05678475 -0.028045888
## 2010-05-01 -0.07945424 -0.07392373 -0.093936287 -0.07536661 -0.111927916
## 2010-06-01 -0.05174109 -0.05975686 -0.013986430 -0.07743393 -0.020619291
## 2010-07-01 0.06830056 0.07258291 0.109324932 0.06730895 0.116103993
## TLT IYR GLD Mkt_RF SMB HML RF
## 2010-02-01 -0.003424499 0.05457064 0.032748219 0.0339 0.0118 0.0318 0e+00
## 2010-03-01 -0.020573285 0.09748471 -0.004386396 0.0630 0.0146 0.0219 1e-04
## 2010-04-01 0.033218146 0.06388077 0.058834363 0.0199 0.0484 0.0296 1e-04
## 2010-05-01 0.051083437 -0.05683525 0.030513147 -0.0790 0.0013 -0.0248 1e-04
## 2010-06-01 0.057978558 -0.04670072 0.023553189 -0.0556 -0.0179 -0.0473 1e-04
## 2010-07-01 -0.009464108 0.09404729 -0.050871157 0.0692 0.0022 -0.0050 1e-04
## Merged data period:
## Start: 2010-02-01
## End : 2025-12-01
Explanation:
The Minimum Variance Portfolio (MVP) is the portfolio that minimizes total risk (variance) given a set of assets. Under the CAPM approach, the covariance matrix is estimated directly from the historical returns of the 8 ETFs over the past 60 months (March 2020 – February 2025).
The optimization problem is: \[\min_w \; w^T \Sigma w \quad \text{subject to} \quad \sum_{i=1}^{8} w_i = 1, \quad w_i \geq 0\]
Where: - \(w\) = vector of portfolio weights - \(\Sigma\) = estimated covariance matrix of returns
We solve this using quadratic programming
(quadprog package).
# ============================================================
# FUNCTION: Compute MVP weights using quadratic programming
# ============================================================
compute_mvp_weights <- function(cov_matrix) {
n <- ncol(cov_matrix) # Number of assets
# Quadprog setup:
# Minimize: 0.5 * w' D w (portfolio variance)
# Subject to: A' w >= b
Dmat <- 2 * cov_matrix # Covariance matrix (multiplied by 2 per quadprog convention)
dvec <- rep(0, n) # No linear term in the objective
# Constraint 1: sum(w) = 1 (fully invested portfolio)
# Constraint 2: w_i >= 0 (no short selling)
Amat <- cbind(rep(1, n), diag(n)) # Equality + inequality constraints
bvec <- c(1, rep(0, n)) # RHS: sum = 1, each w >= 0
meq <- 1 # Number of equality constraints
# Solve the quadratic program
result <- solve.QP(Dmat, dvec, Amat, bvec, meq = meq)
weights <- result$solution
weights[weights < 1e-6] <- 0 # Round near-zero weights to 0
weights <- weights / sum(weights) # Normalize to ensure sum = 1
return(weights)
}
# ============================================================
# Define the 60-month estimation window: March 2020 – February 2025
# ============================================================
window_start <- as.Date("2020-03-01")
window_end <- as.Date("2025-02-01")
# Filter the merged dataset for the estimation window
data_window <- data_merged[index(data_merged) >= window_start &
index(data_merged) <= window_end, ]
cat("Number of months in estimation window:", nrow(data_window), "\n")## Number of months in estimation window: 60
# Extract only ETF returns (first 8 columns)
returns_window <- data_window[, tickers]
# ============================================================
# CAPM Approach: Estimate covariance matrix from actual returns
# ============================================================
cov_capm <- cov(as.matrix(returns_window))
cat("\nCovariance Matrix (CAPM) – 8 x 8:\n")##
## Covariance Matrix (CAPM) – 8 x 8:
## SPY QQQ EEM IWM EFA TLT IYR GLD
## SPY 0.002622 0.002894 0.001869 0.003087 0.002265 0.000932 0.002776 0.000532
## QQQ 0.002894 0.003795 0.002038 0.003170 0.002301 0.001336 0.002781 0.000596
## EEM 0.001869 0.002038 0.002715 0.002481 0.002194 0.000981 0.002237 0.000911
## IWM 0.003087 0.003170 0.002481 0.004968 0.002847 0.000918 0.003550 0.000301
## EFA 0.002265 0.002301 0.002194 0.002847 0.002622 0.001014 0.002667 0.000688
## TLT 0.000932 0.001336 0.000981 0.000918 0.001014 0.001949 0.001394 0.000665
## IYR 0.002776 0.002781 0.002237 0.003550 0.002667 0.001394 0.003879 0.000750
## GLD 0.000532 0.000596 0.000911 0.000301 0.000688 0.000665 0.000750 0.001757
# Compute MVP weights using the CAPM covariance matrix
weights_capm <- compute_mvp_weights(cov_capm)
names(weights_capm) <- tickers
cat("MVP Weights (CAPM Approach):\n")## MVP Weights (CAPM Approach):
## SPY QQQ EEM IWM EFA TLT IYR GLD
## 0.2222 0.0000 0.0000 0.0036 0.0000 0.3175 0.0000 0.4567
##
## Sum of weights: 1
# Visualize weights
barplot(weights_capm,
main = "MVP Weights – CAPM Approach",
col = "steelblue",
ylab = "Weight (w)",
xlab = "ETF",
las = 2)
abline(h = 0, lty = 2)Explanation:
Under the Fama-French 3-Factor model, the covariance matrix is not computed directly from raw returns. Instead, it is built through a factor model decomposition:
Each ETF’s return is regressed on the 3 Fama-French factors: \[R_i = \alpha_i + \beta_{i,Mkt} \cdot MktRF + \beta_{i,SMB} \cdot SMB + \beta_{i,HML} \cdot HML + \epsilon_i\]
The covariance matrix is then decomposed as: \[\Sigma_{FF} = B \cdot \Sigma_F \cdot B^T + D\]
Where: - \(B\) = matrix of factor loadings (betas) — shape: 8 assets × 3 factors - \(\Sigma_F\) = covariance matrix of the 3 Fama-French factors - \(D\) = diagonal matrix of idiosyncratic (residual) variances
This approach tends to produce a more stable covariance matrix by reducing estimation error, especially when the number of assets is large relative to the sample size.
# ============================================================
# Extract Fama-French factors and ETF returns from the window
# ============================================================
factors_window <- as.matrix(data_window[, c("Mkt_RF", "SMB", "HML")])
returns_mat <- as.matrix(returns_window)
n_assets <- length(tickers)
n_factors <- 3
# Matrix to store factor loadings (beta coefficients)
B <- matrix(NA, nrow = n_assets, ncol = n_factors)
rownames(B) <- tickers
colnames(B) <- c("Beta_Mkt", "Beta_SMB", "Beta_HML")
# Vector to store residual variances (idiosyncratic risk)
residual_var <- numeric(n_assets)
# Run OLS regression for each ETF against the 3 FF factors
for (i in seq_along(tickers)) {
model <- lm(returns_mat[, i] ~ factors_window)
B[i, ] <- coef(model)[2:4] # Betas (skip intercept at index 1)
residual_var[i] <- var(residuals(model)) # Idiosyncratic variance
}
cat("Factor Loadings (Beta) for each ETF:\n")## Factor Loadings (Beta) for each ETF:
## Beta_Mkt Beta_SMB Beta_HML
## SPY 0.9863 -0.1583 0.0151
## QQQ 1.0824 -0.0986 -0.4036
## EEM 0.6804 0.0737 0.1434
## IWM 1.0068 0.8799 0.2617
## EFA 0.8487 -0.1248 0.2127
## TLT 0.3453 -0.0754 -0.2664
## IYR 0.9963 0.0312 0.1990
## GLD 0.2430 -0.3426 -0.0239
##
## Idiosyncratic (Residual) Variance for each ETF:
## [1] 0.000013 0.000223 0.001291 0.000076 0.000590 0.001478 0.000938 0.001560
# ============================================================
# Build the FF 3-Factor Covariance Matrix
# Sigma_FF = B * Sigma_F * B' + D
# ============================================================
# Covariance matrix of the 3 Fama-French factors
Sigma_F <- cov(factors_window)
cat("Covariance Matrix of FF Factors (3 x 3):\n")## Covariance Matrix of FF Factors (3 x 3):
## Mkt_RF SMB HML
## Mkt_RF 0.002839 0.000575 0.000000
## SMB 0.000575 0.001042 0.000092
## HML 0.000000 0.000092 0.002154
# Diagonal matrix of residual variances
D <- diag(residual_var)
# FF 3-Factor covariance matrix
cov_ff <- B %*% Sigma_F %*% t(B) + D
cat("\nFF 3-Factor Covariance Matrix (8 x 8):\n")##
## FF 3-Factor Covariance Matrix (8 x 8):
## SPY QQQ EEM IWM EFA TLT IYR GLD
## SPY 0.002622 0.002885 0.001876 0.003087 0.002253 0.000901 0.002715 0.000520
## QQQ 0.002885 0.003795 0.001962 0.003231 0.002313 0.001239 0.002845 0.000589
## EEM 0.001876 0.001962 0.002715 0.002493 0.001682 0.000561 0.002045 0.000307
## IWM 0.003087 0.003231 0.002493 0.004968 0.002802 0.000875 0.003527 0.000281
## EFA 0.002253 0.002313 0.001682 0.002802 0.002622 0.000660 0.002430 0.000428
## TLT 0.000901 0.001239 0.000561 0.000875 0.000660 0.001949 0.000821 0.000209
## IYR 0.002715 0.002845 0.002045 0.003527 0.002430 0.000821 0.003879 0.000468
## GLD 0.000520 0.000589 0.000307 0.000281 0.000428 0.000209 0.000468 0.001757
# Compute MVP weights using the FF 3-Factor covariance matrix
weights_ff <- compute_mvp_weights(cov_ff)
names(weights_ff) <- tickers
cat("MVP Weights (FF 3-Factor Approach):\n")## MVP Weights (FF 3-Factor Approach):
## SPY QQQ EEM IWM EFA TLT IYR GLD
## 0.0000 0.0000 0.1584 0.0000 0.0848 0.3380 0.0000 0.4187
##
## Sum of weights: 1
# Compare both sets of weights side by side
par(mfrow = c(1, 2))
barplot(weights_capm,
main = "MVP Weights – CAPM",
col = "steelblue",
ylab = "Weight (w)",
las = 2,
ylim = c(0, 1))
barplot(weights_ff,
main = "MVP Weights – FF 3-Factor",
col = "coral",
ylab = "Weight (w)",
las = 2,
ylim = c(0, 1))Explanation:
Using the MVP weights estimated from the 60-month window (March 2020 – February 2025), we now compute the realized portfolio return in March 2025 — the first out-of-sample month.
The portfolio return is a weighted average of individual ETF returns: \[R_{portfolio,t} = \sum_{i=1}^{8} w_i \cdot R_{i,t}\]
This evaluates how well each model’s optimal weights perform on actual (unseen) data.
# Retrieve actual ETF returns for March 2025
march_2025 <- as.Date("2025-03-01")
returns_march <- data_merged[index(data_merged) == march_2025, tickers]
cat("Actual ETF Returns – March 2025 (%):\n")## Actual ETF Returns – March 2025 (%):
## [1] -5.5719 -7.5862 1.1340 -6.8541 0.1839 -1.2047 -2.3382 9.4466
# Compute realized portfolio returns
r_march_capm <- as.numeric(returns_march %*% weights_capm)
r_march_ff <- as.numeric(returns_march %*% weights_ff)
cat("\n================================================\n")##
## ================================================
## Realized MVP Portfolio Return – March 2025
## ================================================
## CAPM Approach : +2.6695% per month
## FF 3-Factor Approach : +3.7436% per month
## ================================================
Explanation:
For April 2025, we apply a rolling window approach: the estimation window shifts forward by one month to April 2020 – March 2025. We re-estimate the covariance matrix and recompute the MVP weights using this updated window, then apply the new weights to the actual ETF returns in April 2025.
This reflects how portfolio managers update their portfolios periodically using the most recent available data.
# ============================================================
# New estimation window: April 2020 – March 2025
# ============================================================
window_start_apr <- as.Date("2020-04-01")
window_end_apr <- as.Date("2025-03-01")
data_window_apr <- data_merged[index(data_merged) >= window_start_apr &
index(data_merged) <= window_end_apr, ]
cat("Number of months in new window (Apr 2020 – Mar 2025):",
nrow(data_window_apr), "\n")## Number of months in new window (Apr 2020 – Mar 2025): 60
# ============================================================
# CAPM: Re-estimate covariance matrix from the new window
# ============================================================
returns_window_apr <- data_window_apr[, tickers]
cov_capm_apr <- cov(as.matrix(returns_window_apr))
weights_capm_apr <- compute_mvp_weights(cov_capm_apr)
names(weights_capm_apr) <- tickers
cat("\nUpdated MVP Weights – CAPM (Apr window):\n")##
## Updated MVP Weights – CAPM (Apr window):
## SPY QQQ EEM IWM EFA TLT IYR GLD
## 0.2495 0.0000 0.0028 0.0149 0.0000 0.2868 0.0000 0.4460
# ============================================================
# FF 3-Factor: Re-estimate using the new window
# ============================================================
factors_window_apr <- as.matrix(data_window_apr[, c("Mkt_RF", "SMB", "HML")])
returns_mat_apr <- as.matrix(returns_window_apr)
B_apr <- matrix(NA, nrow = n_assets, ncol = n_factors)
residual_var_apr <- numeric(n_assets)
for (i in seq_along(tickers)) {
model_apr <- lm(returns_mat_apr[, i] ~ factors_window_apr)
B_apr[i, ] <- coef(model_apr)[2:4]
residual_var_apr[i] <- var(residuals(model_apr))
}
Sigma_F_apr <- cov(factors_window_apr)
D_apr <- diag(residual_var_apr)
cov_ff_apr <- B_apr %*% Sigma_F_apr %*% t(B_apr) + D_apr
weights_ff_apr <- compute_mvp_weights(cov_ff_apr)
names(weights_ff_apr) <- tickers
cat("\nUpdated MVP Weights – FF 3-Factor (Apr window):\n")##
## Updated MVP Weights – FF 3-Factor (Apr window):
## SPY QQQ EEM IWM EFA TLT IYR GLD
## 0.0000 0.0000 0.1972 0.0000 0.1098 0.3038 0.0000 0.3892
# ============================================================
# Retrieve actual ETF returns for April 2025
# ============================================================
april_2025 <- as.Date("2025-04-01")
returns_april <- data_merged[index(data_merged) == april_2025, tickers]
cat("\nActual ETF Returns – April 2025 (%):\n")##
## Actual ETF Returns – April 2025 (%):
## [1] -0.8670 1.3968 0.1373 -2.3209 3.6951 -1.3605 -2.1514 5.4244
# Compute realized portfolio returns for April 2025
r_april_capm <- as.numeric(returns_april %*% weights_capm_apr)
r_april_ff <- as.numeric(returns_april %*% weights_ff_apr)
cat("\n================================================\n")##
## ================================================
## Realized MVP Portfolio Return – April 2025
## ================================================
## CAPM Approach : +1.7789% per month
## FF 3-Factor Approach : +2.1308% per month
## ================================================
# Summary table of MVP weights (in %)
weight_summary <- data.frame(
ETF = tickers,
CAPM_Mar2025 = round(weights_capm * 100, 2),
FF3F_Mar2025 = round(weights_ff * 100, 2),
CAPM_Apr2025 = round(weights_capm_apr * 100, 2),
FF3F_Apr2025 = round(weights_ff_apr * 100, 2)
)
cat("MVP Portfolio Weights (%):\n")## MVP Portfolio Weights (%):
## ETF CAPM_Mar2025 FF3F_Mar2025 CAPM_Apr2025 FF3F_Apr2025
## SPY SPY 22.22 0.00 24.95 0.00
## QQQ QQQ 0.00 0.00 0.00 0.00
## EEM EEM 0.00 15.84 0.28 19.72
## IWM IWM 0.36 0.00 1.49 0.00
## EFA EFA 0.00 8.48 0.00 10.98
## TLT TLT 31.75 33.80 28.68 30.38
## IYR IYR 0.00 0.00 0.00 0.00
## GLD GLD 45.67 41.87 44.60 38.92
# Summary table of realized returns
return_summary <- data.frame(
Month = c("March 2025", "April 2025"),
CAPM_Return = round(c(r_march_capm, r_april_capm) * 100, 4),
FF3F_Return = round(c(r_march_ff, r_april_ff) * 100, 4)
)
cat("\nRealized MVP Portfolio Returns (%):\n")##
## Realized MVP Portfolio Returns (%):
## Month CAPM_Return FF3F_Return
## 1 March 2025 2.6695 3.7436
## 2 April 2025 1.7789 2.1308
Question: Visit Professor Kenneth French’s data library and download the monthly returns of “6 portfolios formed on size and book-to-market (2 × 3).” Choose the value-weighted series for January 1930 – December 2018. Split the sample in half and compute the average, SD, skew, and kurtosis for each of the six portfolios for the two halves. Do the split-halves statistics suggest that returns come from the same distribution over the entire period?
Answer:
The six portfolios from the Fama-French data library are sorted by size (Small/Big) and book-to-market ratio (Value/Neutral/Growth):
| Portfolio | Size | Style |
|---|---|---|
| SV | Small | Value |
| SN | Small | Neutral |
| SG | Small | Growth |
| BV | Big | Value |
| BN | Big | Neutral |
| BG | Big | Growth |
Methodology:
Interpretation of Expected Results:
Mean Returns: Small-cap and Value portfolios generally earn higher average returns than Large-cap and Growth portfolios (consistent with the Fama-French size and value premiums). However, these premiums may vary across sub-periods.
Standard Deviation: Small-cap portfolios (SV, SN, SG) typically exhibit higher volatility than Big-cap portfolios (BV, BN, BG), especially in the first half which includes the Great Depression era.
Skewness: Returns are generally expected to be negatively skewed (left-skewed), particularly in the first half of the sample which includes the Great Depression (1929–1933) and World War II. Negative skewness means occasional large negative returns pull the distribution to the left.
Kurtosis: Excess kurtosis > 0 (fat tails) is expected for all portfolios, especially in the first half. High kurtosis indicates that extreme return events (both positive and negative) occur more frequently than a normal distribution would predict.
Conclusion – Do Returns Come from the Same Distribution?
No, the statistics do not strongly suggest that returns come from the same distribution over the entire period. Key evidence:
This finding highlights the non-stationarity of financial return distributions — a key challenge in using historical data to forecast future expected returns.
Question: Consider the following information about a risky portfolio you manage and a risk-free asset: - E(r_P) = 11%, σ_P = 15%, r_f = 5%
(a) Your client wants to invest a proportion of her total investment budget in your risky fund to provide an expected return on her overall portfolio equal to 8%. What proportion should she invest in the risky portfolio, P, and what proportion in the risk-free asset?
(b) What will be the standard deviation of the rate of return on her portfolio?
(c) Another client wants the highest return possible subject to the constraint that standard deviation ≤ 12%. Which client is more risk averse?
Answer:
Part (a): Finding Proportion y
The expected return of a complete portfolio C, combining proportion y in risky portfolio P and (1−y) in risk-free asset:
\[E(r_C) = r_f + y \cdot [E(r_P) - r_f]\]
Solving for y:
\[y = \frac{E(r_C) - r_f}{E(r_P) - r_f} = \frac{8\% - 5\%}{11\% - 5\%} = \frac{3\%}{6\%} = 0.50\]
Client 1 invests 50% in the risky portfolio P and 50% in the risk-free asset.
Part (b): Standard Deviation of the Portfolio
\[\sigma_C = y \times \sigma_P = 0.50 \times 15\% = 7.5\%\]
The standard deviation of Client 1’s portfolio is 7.5%.
Part (c): Which Client is More Risk Averse?
Client 2 wants σ_C ≤ 12%. The proportion y for Client 2:
\[y = \frac{\sigma_C}{\sigma_P} = \frac{12\%}{15\%} = 0.80\]
Client 2’s expected return:
\[E(r_C) = 5\% + 0.80 \times (11\% - 5\%) = 5\% + 4.8\% = 9.8\%\]
| Client 1 | Client 2 | |
|---|---|---|
| Proportion in P (y) | 50% | 80% |
| Expected Return | 8.0% | 9.8% |
| Standard Deviation | 7.5% | 12.0% |
Client 1 is more risk averse. She accepts a lower expected return (8%) with lower risk (7.5% SD), whereas Client 2 is willing to take more risk (12% SD) in exchange for a higher expected return (9.8%). A higher allocation to the risky portfolio signals lower risk aversion.
Question: Investment Management Inc. (IMI) uses the Capital Market Line (CML) with the following forecasts: - Expected return on market portfolio: 12% - Standard deviation on market portfolio: 20% - Risk-free rate: 5%
Samuel Johnson wants the standard deviation of his portfolio to equal half the market portfolio’s standard deviation. Using the CML, what expected return can IMI provide?
Answer:
Step 1: Define Johnson’s target standard deviation
\[\sigma_C = \frac{1}{2} \times \sigma_M = \frac{1}{2} \times 20\% = 10\%\]
Step 2: Find proportion y along the CML
\[\sigma_C = y \times \sigma_M \implies y = \frac{\sigma_C}{\sigma_M} = \frac{10\%}{20\%} = 0.50\]
Step 3: Apply the CML to find expected return
\[E(r_C) = r_f + y \cdot [E(r_M) - r_f]\]
\[E(r_C) = 5\% + 0.50 \times (12\% - 5\%) = 5\% + 3.5\% = 8.5\%\]
IMI can offer Samuel Johnson an expected return of 8.5% per year, subject to his risk constraint of σ = 10% (half the market standard deviation).
This portfolio consists of 50% invested in the market portfolio and 50% in risk-free T-bills, lying exactly on the Capital Market Line.
Question: Refer to the graph with indifference curves (1, 2, 3, 4) and points (E, F, G, H) and the Capital Allocation Line (CAL). Which indifference curve represents the greatest level of utility that can be achieved by the investor?
Answer:
Indifference Curve 4 (the highest curve in the graph) does NOT represent the greatest achievable utility. The greatest achievable utility is represented by the highest indifference curve that is still tangent to (touches) the Capital Allocation Line (CAL).
Explanation:
Based on the graph description, the optimal curve is Curve 2, which is tangent to the CAL at point E. Curves 3 and 4 lie entirely above the CAL and are unachievable.
Answer: Indifference Curve 2 represents the greatest level of utility that can actually be achieved by the investor, as it is the highest curve that is tangent to the CAL.
Question: Which point designates the optimal portfolio of risky assets?
Answer:
Point E designates the optimal portfolio of risky assets.
Explanation:
The optimal portfolio of risky assets is the tangency portfolio — the point on the efficient frontier where the Capital Allocation Line (CAL) is tangent. This is the risky portfolio that, when combined with the risk-free asset, produces the highest possible Sharpe ratio (reward-to-volatility ratio):
\[\text{Sharpe Ratio} = \frac{E(r_P) - r_f}{\sigma_P}\]
Point E lies on the efficient frontier at the exact point where the CAL (drawn from the risk-free rate) touches the frontier. All other points on the frontier would produce a lower Sharpe ratio than Point E.
The optimal risky portfolio is always the tangency point between the CAL and the efficient frontier of risky assets — this is independent of the investor’s risk aversion. Risk aversion only determines how much of one’s budget is allocated to the risky portfolio vs. the risk-free asset.
Question: You manage an equity fund with: - Expected risk premium: 10% - Expected standard deviation: 14% - T-bill rate: 6%
Your client invests $60,000 in your equity fund and $40,000 in a T-bill money market fund (total: $100,000).
What are the expected return and standard deviation of return on your client’s portfolio?
Answer:
Step 1: Calculate portfolio weights
\[y = \frac{\$60,000}{\$100,000} = 0.60 \quad \text{(proportion in equity fund)}\]
\[1 - y = \frac{\$40,000}{\$100,000} = 0.40 \quad \text{(proportion in T-bills)}\]
Step 2: Expected return of the complete portfolio
The equity fund’s expected total return = risk premium + risk-free rate = 10% + 6% = 16%
\[E(r_C) = y \cdot E(r_P) + (1-y) \cdot r_f\]
\[E(r_C) = 0.60 \times 16\% + 0.40 \times 6\% = 9.6\% + 2.4\% = 12\%\]
Step 3: Standard deviation of the complete portfolio
Since T-bills have zero standard deviation:
\[\sigma_C = y \times \sigma_P = 0.60 \times 14\% = 8.4\%\]
Expected return = 12% | Standard deviation = 8.4%
Question: Stocks offer an expected rate of return of 18% with a standard deviation of 22%. Gold offers an expected return of 10% with a standard deviation of 30%.
(a) In light of the apparent inferiority of gold with respect to both mean return and volatility, would anyone hold gold? If so, demonstrate graphically why.
(b) Reanswer (a) with the additional assumption that the correlation coefficient between gold and stocks equals 1. Would one hold gold?
(c) Could the assumptions in part (b) represent an equilibrium?
Answer:
Part (a): Would Anyone Hold Gold?
Yes — investors would still hold gold if the correlation between gold and stocks is sufficiently low (or negative).
Although gold has both a lower expected return (10% vs. 18%) and higher standard deviation (30% vs. 22%) than stocks, gold’s key contribution to a portfolio is diversification through its low or negative correlation with stocks.
When two assets are not perfectly positively correlated (ρ < 1), combining them creates a portfolio with lower risk than holding either asset alone. Specifically, if gold and stocks have a low or negative correlation:
\[\sigma_P^2 = w_S^2 \sigma_S^2 + w_G^2 \sigma_G^2 + 2 w_S w_G \text{Cov}(r_S, r_G)\]
The opportunity set (investment frontier) will bend to the left — meaning some portfolios containing gold achieve lower risk than a pure stock portfolio for the same level of return, or higher return for the same level of risk.
Graphically: The efficient frontier of stocks-only is a single point. Adding gold creates a curved frontier that extends to the left. The minimum-variance portfolio will include some gold, and any investor (regardless of risk aversion) would include gold in their efficient portfolio.
Part (b): Correlation = +1
If ρ = +1, the opportunity set becomes a straight line connecting the two assets. In this case:
\[\sigma_P = w_S \sigma_S + w_G \sigma_G\]
The return-risk tradeoff is linear. Since stocks dominate gold in both expected return and (for any combination) a lower variance portfolio can be achieved without gold, no investor would hold gold when ρ = +1.
With correlation = 1, gold provides no diversification benefit. Rational investors would hold 100% stocks.
Part (c): Can ρ = +1 Represent an Equilibrium?
No — this cannot represent an equilibrium.
If ρ(gold, stocks) = 1, then gold and stocks are essentially the same asset (perfect substitutes). In equilibrium, the law of one price and the absence of arbitrage opportunities require that assets with identical risk characteristics must offer the same expected return.
Since gold has σ = 30% (higher than stocks’ σ = 22%) and ρ = 1, a portfolio of stocks and borrowing at the risk-free rate can replicate any risk level achievable with gold, but with a higher expected return. Gold would be dominated and no one would hold it. This would drive gold’s price down and its expected return up until an equilibrium is reached — which cannot coexist with ρ = 1 and lower expected return for gold.
Conclusion: ρ = 1 with gold offering lower return than stocks cannot be an equilibrium. Market forces would adjust prices until either the correlation drops below 1 or gold’s expected return rises to compensate for its risk.
Question: Suppose that there are many stocks and that stocks A and B have the following characteristics:
| Stock | Expected Return | Standard Deviation |
|---|---|---|
| A | 10% | 5% |
| B | 15% | 10% |
| Correlation | –1 |
Suppose it is possible to borrow at the risk-free rate r_f. What must be the value of the risk-free rate?
Answer:
Key Insight: When two assets have a correlation of –1, it is possible to construct a risk-free portfolio (σ_P = 0) by combining them with the right weights.
Step 1: Find portfolio weights that eliminate all risk
With ρ = –1:
\[\sigma_P = |w_A \sigma_A - w_B \sigma_B| = 0\]
\[w_A \sigma_A = w_B \sigma_B\]
\[w_A \times 5\% = w_B \times 10\%\]
\[w_A = 2 w_B\]
Since \(w_A + w_B = 1\):
\[2w_B + w_B = 1 \implies w_B = \frac{1}{3}, \quad w_A = \frac{2}{3}\]
Step 2: Compute the expected return of this risk-free portfolio
\[E(r_P) = w_A \times E(r_A) + w_B \times E(r_B)\]
\[E(r_P) = \frac{2}{3} \times 10\% + \frac{1}{3} \times 15\% = 6.67\% + 5\% = 11.67\%\]
Step 3: Apply no-arbitrage condition
Since this portfolio has zero risk (σ = 0), it must earn exactly the risk-free rate in equilibrium. Otherwise, there would be a riskless arbitrage opportunity.
\[\boxed{r_f = 11.67\%}\]
The risk-free rate must equal 11.67%. This is because the combination of stocks A (w = 2/3) and B (w = 1/3) creates a perfectly hedged, risk-free portfolio. Any deviation from this rate would allow investors to earn a riskless profit, which cannot persist in an efficient market.
Question: Abigail Grace has a $900,000 fully diversified portfolio. She inherits ABC Company stock worth $100,000. Given data:
| Expected Monthly Return | SD of Monthly Returns | |
|---|---|---|
| Original Portfolio | 0.67% | 2.37% |
| ABC Company | 1.25% | 2.95% |
Correlation of ABC with original portfolio = 0.40.
(a) Calculate: (i) Expected return of new portfolio, (ii) Covariance of ABC with portfolio, (iii) SD of new portfolio.
(b) If she sells ABC and buys risk-free bonds at 0.42%/month, calculate the same three statistics.
(c) Determine whether systematic risk increases or decreases when ABC is replaced with government securities.
(d) Her husband says it doesn’t matter whether she keeps ABC or replaces it with XYZ (same E(r) and SD as ABC). Is this correct?
(e) Grace says she doesn’t want to lose money. Identify a weakness of SD as a risk measure and suggest an alternative.
Answer:
Part (a): Keep ABC Stock
Weights: - Original portfolio: \(w_O = \frac{900,000}{1,000,000} = 0.90\) - ABC: \(w_{ABC} = \frac{100,000}{1,000,000} = 0.10\)
(i) Expected return of new portfolio:
\[E(r_{New}) = 0.90 \times 0.67\% + 0.10 \times 1.25\% = 0.603\% + 0.125\% = 0.728\%\]
(ii) Covariance of ABC with original portfolio:
\[\text{Cov}(r_{ABC}, r_O) = \rho \times \sigma_{ABC} \times \sigma_O = 0.40 \times 2.95\% \times 2.37\%\]
\[= 0.40 \times 0.0295 \times 0.0237 = 0.000280 = 0.280\%^2 \text{ (or 2.80 in \%²)}\]
(iii) Standard deviation of new portfolio:
\[\sigma_{New}^2 = w_O^2 \sigma_O^2 + w_{ABC}^2 \sigma_{ABC}^2 + 2 w_O w_{ABC} \text{Cov}\]
\[= (0.90)^2(2.37)^2 + (0.10)^2(2.95)^2 + 2(0.90)(0.10)(2.80)\]
\[= 0.81 \times 5.6169 + 0.01 \times 8.7025 + 2 \times 0.09 \times 2.80\]
\[= 4.5497 + 0.0870 + 0.5040 = 5.1407\%^2\]
\[\sigma_{New} = \sqrt{5.1407} = 2.268\%\]
Part (b): Sell ABC, Buy Risk-Free Securities (0.42%/month)
Weights: - Original portfolio: \(w_O = 0.90\) - Risk-free: \(w_{RF} = 0.10\)
(i) Expected return:
\[E(r_{New}) = 0.90 \times 0.67\% + 0.10 \times 0.42\% = 0.603\% + 0.042\% = 0.645\%\]
(ii) Covariance with risk-free:
\[\text{Cov}(r_{RF}, r_O) = 0\]
(Risk-free assets have no covariance with any risky asset.)
(iii) Standard deviation:
\[\sigma_{New}^2 = w_O^2 \sigma_O^2 + w_{RF}^2 \times 0 + 0 = (0.90)^2 (2.37)^2 = 0.81 \times 5.6169 = 4.5497\]
\[\sigma_{New} = \sqrt{4.5497} = 2.133\%\]
Part (c): Effect on Systematic Risk
When Grace sells ABC (a risky stock with β > 0) and replaces it with risk-free government securities (β = 0), the systematic risk (beta) of her new portfolio will DECREASE.
Systematic risk decreases because she replaces a risky asset that covaries with the market (ABC, β > 0) with a risk-free asset (β = 0), reducing the portfolio’s overall market exposure.
Part (d): Does It Matter — ABC vs. XYZ?
Grace’s husband’s comment is INCORRECT.
Even though XYZ has the same expected return and standard deviation as ABC, what matters for portfolio risk is the correlation (covariance) between the new stock and the existing portfolio — not just the new stock’s individual characteristics.
If the correlation of XYZ with the original portfolio differs from that of ABC (ρ = 0.40), the resulting portfolio standard deviation will be different. In general:
\[\sigma_{New} = f(\text{Cov}(r_{new stock}, r_O))\]
Since the problem does not specify the correlation of XYZ with the original portfolio, we cannot assume the portfolios are equivalent.
Conclusion: It does matter which stock she chooses. The diversification benefit depends on the covariance with the existing portfolio, not just the individual stock’s risk and return.
Part (e): Weakness of Standard Deviation and Alternative Risk Measure
(i) Weakness of Standard Deviation:
Standard deviation is a symmetric risk measure — it penalizes both upside (gains above mean) and downside (losses below mean) deviations equally. However, Grace is only concerned about losses, not excessive gains.
Standard deviation is inappropriate for Grace because it treats positive deviations (good outcomes) as equally risky as negative deviations (bad outcomes). It overstates risk from her perspective by including upside volatility she does not care about.
(ii) More Appropriate Alternative Risk Measure:
Semi-variance (or Downside Deviation / Value at Risk / Conditional Value at Risk) is more appropriate.
These measures better reflect Grace’s concern of “not losing money” since they focus exclusively on the downside portion of the return distribution.
Question: A portfolio manager has the following inputs from macro and micro forecasters:
Micro Forecasts:
| Asset | E(r) % | Beta | Residual SD % |
|---|---|---|---|
| Stock A | 20 | 1.3 | 58 |
| Stock B | 18 | 1.8 | 71 |
| Stock C | 17 | 0.7 | 60 |
| Stock D | 12 | 1.0 | 55 |
Macro Forecasts:
| Asset | E(r) % | SD % |
|---|---|---|
| T-bills | 8 | 0 |
| Passive equity portfolio | 16 | 23 |
Questions: (a) Calculate expected excess returns, alpha values, and residual variances. (b) Construct the optimal risky portfolio. (c) What is the Sharpe ratio for the optimal portfolio? (d) By how much did the active portfolio improve the Sharpe ratio vs. passive? (e) What is the complete portfolio makeup for an investor with A = 2.8?
Answer:
Part (a): Excess Returns, Alpha, and Residual Variances
The CAPM expected return for each stock:
\[E(r_i)_{CAPM} = r_f + \beta_i \times [E(r_M) - r_f] = 8\% + \beta_i \times (16\% - 8\%) = 8\% + 8\beta_i\]
Alpha = Analyst’s forecast − CAPM expected return:
\[\alpha_i = E(r_i)_{forecast} - E(r_i)_{CAPM}\]
| Stock | E(r) Forecast | CAPM E(r) | Alpha (α) | Residual Var σ²(e) |
|---|---|---|---|---|
| A | 20% | 8+1.3×8 = 18.4% | +1.6% | 58² = 3,364 |
| B | 18% | 8+1.8×8 = 22.4% | −4.4% | 71² = 5,041 |
| C | 17% | 8+0.7×8 = 13.6% | +3.4% | 60² = 3,600 |
| D | 12% | 8+1.0×8 = 16.0% | −4.0% | 55² = 3,025 |
Stocks B and D have negative alpha (overvalued) — in an unconstrained portfolio, they would be sold short.
Part (b): Construct the Optimal Risky Portfolio
Step 1: Compute initial active portfolio weights (proportional to α/σ²(e)):
\[w_i^0 = \frac{\alpha_i / \sigma^2(e_i)}{\sum_j |\alpha_j / \sigma^2(e_j)|}\]
| Stock | α/σ²(e) | Sign |
|---|---|---|
| A | 1.6/3364 = 0.000476 | + |
| B | −4.4/5041 = −0.000873 | − |
| C | 3.4/3600 = 0.000944 | + |
| D | −4.0/3025 = −0.001322 | − |
Sum of |α/σ²(e)| = 0.000476 + 0.000873 + 0.000944 + 0.001322 = 0.003615
Initial weights:
| Stock | w⁰ |
|---|---|
| A | +0.000476/0.003615 = +0.1317 |
| B | −0.000873/0.003615 = −0.2414 |
| C | +0.000944/0.003615 = +0.2611 |
| D | −0.001322/0.003615 = −0.3658 |
Step 2: Compute Active Portfolio Parameters
\[\alpha_A^* = \sum w_i \alpha_i = (0.1317)(1.6) + (−0.2414)(−4.4) + (0.2611)(3.4) + (−0.3658)(−4.0)\] \[= 0.2107 + 1.0622 + 0.8877 + 1.4632 = 3.624\%\]
\[\beta_A^* = \sum w_i \beta_i = (0.1317)(1.3) + (−0.2414)(1.8) + (0.2611)(0.7) + (−0.3658)(1.0)\] \[= 0.1712 − 0.4345 + 0.1828 − 0.3658 = −0.4463\]
\[\sigma^2(e_A^*) = \sum w_i^2 \sigma^2(e_i)\] \[= (0.1317)^2(3364) + (−0.2414)^2(5041) + (0.2611)^2(3600) + (−0.3658)^2(3025)\] \[= 58.35 + 293.85 + 245.43 + 404.90 = 1,002.5\]
Step 3: Combine active portfolio (A) with passive market portfolio (M)
\[w_A^0 = \frac{\alpha_A^* / \sigma^2(e_A^*)}{[E(r_M) - r_f] / \sigma_M^2} = \frac{3.624 / 1002.5}{8 / 529} = \frac{0.003615}{0.01512} = 0.2391\]
Adjust for active portfolio beta:
\[w_A^* = \frac{w_A^0}{1 + (1 - \beta_A^*) w_A^0} = \frac{0.2391}{1 + (1-(-0.4463)) \times 0.2391} = \frac{0.2391}{1 + 0.3456} = \frac{0.2391}{1.3456} = 0.1777\]
\[w_M^* = 1 - w_A^* = 1 - 0.1777 = 0.8223\]
Part (c): Sharpe Ratio of Optimal Portfolio
\[\text{Sharpe}_P^2 = \text{Sharpe}_M^2 + \left(\frac{\alpha_A^*}{\sigma(e_A^*)}\right)^2\]
\[\text{Sharpe}_M = \frac{E(r_M) - r_f}{\sigma_M} = \frac{8\%}{23\%} = 0.3478\]
\[\left(\frac{\alpha_A^*}{\sigma(e_A^*)}\right)^2 = \left(\frac{3.624}{\sqrt{1002.5}}\right)^2 = \left(\frac{3.624}{31.66}\right)^2 = (0.1144)^2 = 0.01310\]
\[\text{Sharpe}_P = \sqrt{(0.3478)^2 + 0.01310} = \sqrt{0.12096 + 0.01310} = \sqrt{0.13406} = 0.3662\]
Sharpe ratio of optimal portfolio ≈ 0.366
Part (d): Improvement in Sharpe Ratio vs. Passive
| Portfolio | Sharpe Ratio |
|---|---|
| Passive (M only) | 0.3478 |
| Optimal (Active + Passive) | 0.3662 |
| Improvement | +0.0184 |
The active portfolio improved the Sharpe ratio from 0.3478 to 0.3662, an increase of approximately 0.018 — representing a meaningful but modest gain from active management.
Part (e): Complete Portfolio for Investor with A = 2.8
The proportion invested in the optimal risky portfolio P:
\[y^* = \frac{E(r_P) - r_f}{A \cdot \sigma_P^2}\]
Compute E(r_P) and σ_P:
\[E(r_P) = r_f + w_A^* \alpha_A^* + [w_A^* \beta_A^* + w_M^*] \times [E(r_M) - r_f]\]
\[= 8\% + 0.1777 \times 3.624\% + [0.1777 \times (-0.4463) + 0.8223] \times 8\%\]
\[= 8\% + 0.644\% + [−0.0793 + 0.8223] \times 8\% = 8\% + 0.644\% + 5.944\% = 14.59\%\]
\[\sigma_P^2 = (w_A^* \beta_A^* + w_M^*)^2 \sigma_M^2 + (w_A^*)^2 \sigma^2(e_A^*)\]
\[= (0.7430)^2 \times 529 + (0.1777)^2 \times 1002.5\]
\[= 0.5520 \times 529 + 0.03158 \times 1002.5 = 291.9 + 31.66 = 323.6\%^2\]
\[y^* = \frac{14.59\% - 8\%}{2.8 \times 323.6} = \frac{6.59\%}{906.1} = 0.00727 \approx 72.7\%\]
The investor should allocate approximately 72.7% to the optimal risky portfolio P and 27.3% to T-bills.
The exact stock allocations within the risky portfolio:
| Asset | Weight in P | Weight in Complete Portfolio |
|---|---|---|
| Stock A | 0.1777 × 0.1317 = 2.34% | 2.34% × 72.7% = 1.70% |
| Stock B | 0.1777 × (−0.2414) = −4.29% | −4.29% × 72.7% = −3.12% |
| Stock C | 0.1777 × 0.2611 = 4.64% | 4.64% × 72.7% = 3.37% |
| Stock D | 0.1777 × (−0.3658) = −6.50% | −6.50% × 72.7% = −4.73% |
| Market (M) | 82.23% | 82.23% × 72.7% = 59.78% |
| T-bills | — | 27.3% |
Question: When the annualized monthly percentage excess returns for a stock market index were regressed against the excess returns for ABC and XYZ stocks over the most recent 5-year period, the following results were obtained:
| Statistic | ABC | XYZ |
|---|---|---|
| Alpha | −3.20% | 7.3% |
| Beta | 0.60 | 0.97 |
| R² | 0.35 | 0.17 |
| Residual SD | 13.02% | 21.45% |
Additional data from two brokerage houses (last 2 years of weekly returns):
| Brokerage | Beta of ABC | Beta of XYZ |
|---|---|---|
| A | 0.62 | 1.45 |
| B | 0.71 | 1.25 |
Explain what these regression results tell an analyst about risk-return relationships for each stock. Comment on implications for future risk-return relationships.
Answer:
Analysis of ABC Stock:
Alpha = −3.20%: ABC generated a negative abnormal return of −3.20% per year during the sample period, underperforming what the CAPM would predict given its level of market risk. This suggests ABC was a poor investment on a risk-adjusted basis over this period.
Beta = 0.60: ABC has low systematic risk (market sensitivity). It moves about 60% as much as the market — it is a defensive stock that is less sensitive to market swings.
R² = 0.35: Only 35% of ABC’s return variability is explained by market movements. The remaining 65% is firm-specific (idiosyncratic) risk.
Residual SD = 13.02%: ABC has a substantial level of firm-specific risk, meaning company-specific events heavily influence its returns.
Future Implication for ABC: The negative alpha suggests poor past performance, but alpha tends to mean-revert over time. The fact that brokerage houses estimate beta at 0.62 and 0.71 (slightly above the 5-year estimate of 0.60) suggests the true beta may be somewhat higher. An adjusted beta (using the Blume adjustment: β_adj = 0.33 + 0.67 × β_observed) would push beta upward, implying slightly higher expected return going forward.
Analysis of XYZ Stock:
Alpha = +7.3%: XYZ generated a large positive abnormal return of 7.3% per year — significantly outperforming CAPM expectations. This is an impressive result, though it may not persist.
Beta = 0.97: XYZ has near-market-level systematic risk, moving almost in lockstep with the market index.
R² = 0.17: Only 17% of XYZ’s variance is explained by the market — the remaining 83% is firm-specific risk. This is very high idiosyncratic risk.
Residual SD = 21.45%: XYZ has very high firm-specific risk, making it a poor choice for a concentrated position but potentially valuable in a well-diversified portfolio.
Future Implication for XYZ: The large positive alpha is noteworthy, but brokerage house betas differ dramatically (1.45 vs. 1.25) from the 5-year estimate of 0.97. This suggests that XYZ’s systematic risk has increased significantly in recent years — perhaps due to changes in business operations, leverage, or market conditions. Going forward, the higher beta estimates imply higher required returns, meaning the positive alpha may be smaller than it appears once current systematic risk is properly accounted for.
Key Takeaways:
Past alpha is not necessarily predictive. ABC’s negative alpha and XYZ’s positive alpha reflect historical performance that may or may not repeat.
Beta estimation is unstable over time. The discrepancy between the 5-year beta and the 2-year brokerage estimates — especially for XYZ — highlights the importance of using the most recent data and applying beta adjustments (e.g., Blume or Vasicek) for forward-looking analysis.
In a diversified portfolio: Both stocks’ firm-specific risks (high residual SD) are largely diversified away. The relevant risk measure becomes their beta — and the significantly revised beta estimates for XYZ warrant reconsideration of its risk-return profile going forward.