# tickers and timeframe
tickers <- c("SPY", "QQQ", "EEM", "IWM", "EFA", "TLT", "IYR", "GLD")
start_date <- "2010-01-01"
end_date <- "2025-12-31"
# daily data
getSymbols(tickers, src = "yahoo", from = start_date, to = end_date)
## [1] "SPY" "QQQ" "EEM" "IWM" "EFA" "TLT" "IYR" "GLD"
prices_list <- lapply(tickers, function(x) Ad(get(x)))
etf_prices <- do.call(merge, prices_list)
colnames(etf_prices) <- tickers
etf_prices <- na.locf(etf_prices)
head(etf_prices)
## SPY QQQ EEM IWM EFA TLT IYR
## 2010-01-04 84.79637 40.29079 30.35151 51.36657 35.12844 56.13517 26.76811
## 2010-01-05 85.02083 40.29079 30.57180 51.18993 35.15940 56.49766 26.83239
## 2010-01-06 85.08071 40.04776 30.63576 51.14177 35.30801 55.74141 26.82070
## 2010-01-07 85.43986 40.07380 30.45810 51.51911 35.17179 55.83518 27.06027
## 2010-01-08 85.72420 40.40363 30.69973 51.80011 35.45044 55.81018 26.87914
## 2010-01-11 85.84389 40.23870 30.63576 51.59138 35.74147 55.50392 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
To calculate monthly returns, we first need to extract the closing prices on the last trading day of each month. Then, we apply the Rate of Change calculation across our dataset to find the discrete (simple) returns.
month_ends <- endpoints(etf_prices, on = "months")
monthly_prices <- etf_prices[month_ends, ]
monthly_returns <- ROC(monthly_prices, type = "discrete")
monthly_returns <- na.omit(monthly_returns)
head(monthly_returns)
## SPY QQQ EEM IWM EFA
## 2010-02-26 0.03119470 0.04603857 0.017764127 0.04475145 0.002667738
## 2010-03-31 0.06087975 0.07710947 0.081108773 0.08230654 0.063854321
## 2010-04-30 0.01546981 0.02242508 -0.001661748 0.05678449 -0.028046102
## 2010-05-28 -0.07945415 -0.07392400 -0.093935922 -0.07536592 -0.111927633
## 2010-06-30 -0.05174090 -0.05975660 -0.013986709 -0.07743442 -0.020619284
## 2010-07-30 0.06830005 0.07258292 0.109325297 0.06730967 0.116104021
## TLT IYR GLD
## 2010-02-26 -0.003424235 0.05457094 0.032748219
## 2010-03-31 -0.020573681 0.09748433 -0.004386396
## 2010-04-30 0.033217884 0.06388103 0.058834363
## 2010-05-28 0.051083857 -0.05683536 0.030513147
## 2010-06-30 0.057978316 -0.04670111 0.023553189
## 2010-07-30 -0.009463286 0.09404800 -0.050871157
Using the frenchdata library to pull the “Fama/French 3
Factors” dataset. This file contains both monthly and annual data; we
will extract the monthly subset.
ff_data_raw <- download_french_data("Fama/French 3 Factors")
ff_monthly <- ff_data_raw$subsets$data[[1]]
ff_factors <- ff_monthly %>%
mutate(
date = ym(date),
across(c(`Mkt-RF`, SMB, HML, RF), ~ . / 100)
) %>%
filter(date >= "2010-01-01" & date <= "2025-12-31")
head(ff_factors)
## # A tibble: 6 × 5
## date `Mkt-RF` SMB HML RF
## <date> <dbl> <dbl> <dbl> <dbl>
## 1 2010-01-01 -0.0335 0.0043 0.0033 0
## 2 2010-02-01 0.0339 0.0118 0.0318 0
## 3 2010-03-01 0.063 0.0146 0.0219 0.0001
## 4 2010-04-01 0.0199 0.0484 0.0296 0.0001
## 5 2010-05-01 -0.079 0.0013 -0.0248 0.0001
## 6 2010-06-01 -0.0556 -0.0179 -0.0473 0.0001
To perform regressions later, we need all variables in a single table. We will convert our ETF returns into a data frame, align the dates to the first of each month, and then join them with the Fama-French factors.
etf_df <- as.data.frame(monthly_returns)
etf_df$date <- index(monthly_returns)
etf_df <- etf_df %>%
mutate(date = floor_date(date, "month"))
merged_data <- etf_df %>%
left_join(ff_factors, by = "date") %>%
relocate(date)
merged_data <- na.omit(merged_data)
head(merged_data)
## date SPY QQQ EEM IWM EFA
## 1 2010-02-01 0.03119470 0.04603857 0.017764127 0.04475145 0.002667738
## 2 2010-03-01 0.06087975 0.07710947 0.081108773 0.08230654 0.063854321
## 3 2010-04-01 0.01546981 0.02242508 -0.001661748 0.05678449 -0.028046102
## 4 2010-05-01 -0.07945415 -0.07392400 -0.093935922 -0.07536592 -0.111927633
## 5 2010-06-01 -0.05174090 -0.05975660 -0.013986709 -0.07743442 -0.020619284
## 6 2010-07-01 0.06830005 0.07258292 0.109325297 0.06730967 0.116104021
## TLT IYR GLD Mkt-RF SMB HML RF
## 1 -0.003424235 0.05457094 0.032748219 0.0339 0.0118 0.0318 0e+00
## 2 -0.020573681 0.09748433 -0.004386396 0.0630 0.0146 0.0219 1e-04
## 3 0.033217884 0.06388103 0.058834363 0.0199 0.0484 0.0296 1e-04
## 4 0.051083857 -0.05683536 0.030513147 -0.0790 0.0013 -0.0248 1e-04
## 5 0.057978316 -0.04670111 0.023553189 -0.0556 -0.0179 -0.0473 1e-04
## 6 -0.009463286 0.09404800 -0.050871157 0.0692 0.0022 -0.0050 1e-04
First, we filter our merged dataset to the specified 60-month window: March 2020 through February 2025.
window_data <- merged_data %>%
filter(date >= "2020-03-01" & date <= "2025-02-28")
mkt_excess <- window_data[["Mkt-RF"]]
betas <- numeric(length(tickers))
resid_vars <- numeric(length(tickers))
for (i in 1:length(tickers)) {
ticker <- tickers[i]
etf_excess <- window_data[[ticker]] - window_data[["RF"]]
capm_fit <- lm(etf_excess ~ mkt_excess)
betas[i] <- coef(capm_fit)[2]
resid_vars[i] <- var(resid(capm_fit))
}
var_mkt <- var(mkt_excess)
beta_matrix <- matrix(betas, ncol = 1)
cov_matrix_capm <- (beta_matrix %*% t(beta_matrix)) * var_mkt + diag(resid_vars)
rownames(cov_matrix_capm) <- tickers
colnames(cov_matrix_capm) <- tickers
inv_cov <- solve(cov_matrix_capm)
ones <- rep(1, length(tickers))
num <- inv_cov %*% ones
den <- as.numeric(t(ones) %*% inv_cov %*% ones)
mvp_weights <- num / den
mvp_weights <- as.vector(mvp_weights)
names(mvp_weights) <- tickers
etf_returns_matrix <- as.matrix(window_data %>% select(all_of(tickers)))
mvp_monthly_returns <- etf_returns_matrix %*% mvp_weights
mvp_results <- data.frame(
date = window_data$date,
MVP_Return = as.numeric(mvp_monthly_returns)
)
print(round(mvp_weights, 4))
## SPY QQQ EEM IWM EFA TLT IYR GLD
## 0.2744 -0.1429 0.1719 -0.1891 0.1748 0.3330 -0.0312 0.4092
head(mvp_results)
## date MVP_Return
## 1 2020-03-01 -0.008541199
## 2 2020-04-01 0.040925826
## 3 2020-05-01 0.009933717
## 4 2020-06-01 0.018435373
## 5 2020-07-01 0.075344956
## 6 2020-08-01 -0.011832689
We will use the same window_data (March 2020 - Feb 2025)
as the previous question. This time, we run a multiple regression for
each ETF against the Market, SMB, and HML factors.
B_matrix <- matrix(NA, nrow = length(tickers), ncol = 3)
rownames(B_matrix) <- tickers
colnames(B_matrix) <- c("Mkt_Beta", "SMB_Beta", "HML_Beta")
resid_vars_ff3 <- numeric(length(tickers))
for (i in 1:length(tickers)) {
ticker <- tickers[i]
etf_excess <- window_data[[ticker]] - window_data[["RF"]]
ff3_fit <- lm(etf_excess ~ window_data[["Mkt-RF"]] + window_data[["SMB"]] + window_data[["HML"]])
B_matrix[i, ] <- coef(ff3_fit)[2:4]
resid_vars_ff3[i] <- var(resid(ff3_fit))
}
round(head(B_matrix), 4)
## Mkt_Beta SMB_Beta HML_Beta
## SPY 0.9853 -0.1487 0.0194
## QQQ 1.0813 -0.0890 -0.3994
## EEM 0.6794 0.0834 0.1476
## IWM 1.0058 0.8895 0.2660
## EFA 0.8477 -0.1152 0.2169
## TLT 0.3443 -0.0658 -0.2622
Next, we calculate the covariance matrix of the factors themselves, and apply the Multi-Index formula to generate our ETF covariance matrix.
factor_data <- window_data[, c("Mkt-RF", "SMB", "HML")]
Sigma_F <- cov(factor_data)
cov_matrix_ff3 <- (B_matrix %*% Sigma_F %*% t(B_matrix)) + diag(resid_vars_ff3)
rownames(cov_matrix_ff3) <- tickers
colnames(cov_matrix_ff3) <- tickers
Finally, we use the same Minimum Variance Portfolio formula as before, but substitute our newly generated FF3 covariance matrix.
inv_cov_ff3 <- solve(cov_matrix_ff3)
ones <- rep(1, length(tickers))
num_ff3 <- inv_cov_ff3 %*% ones
den_ff3 <- as.numeric(t(ones) %*% inv_cov_ff3 %*% ones)
mvp_weights_ff3 <- as.vector(num_ff3 / den_ff3)
names(mvp_weights_ff3) <- tickers
etf_returns_matrix <- as.matrix(window_data[, tickers])
mvp_monthly_returns_ff3 <- etf_returns_matrix %*% mvp_weights_ff3
mvp_results_ff3 <- data.frame(
date = window_data$date,
MVP_FF3_Return = as.numeric(mvp_monthly_returns_ff3)
)
print("FF3 Estimated MVP Weights:")
## [1] "FF3 Estimated MVP Weights:"
print(round(mvp_weights_ff3, 4))
## SPY QQQ EEM IWM EFA TLT IYR GLD
## 0.1399 -0.2280 0.1988 -0.0563 0.1810 0.3777 -0.0138 0.4007
To find the realized returns, we isolate the actual ETF returns for March 2025 and multiply them by the respective optimal weights we calculated in the previous steps.
march_data <- merged_data %>%
filter(date == "2025-03-01")
march_returns <- as.numeric(march_data[ , tickers])
names(march_returns) <- tickers
realized_return_capm <- sum(march_returns * mvp_weights)
realized_return_ff3 <- sum(march_returns * mvp_weights_ff3)
results_comparison <- data.frame(
Model = c("CAPM (Single-Index) MVP", "FF3 (Multi-Index) MVP"),
Realized_Return_March_2025 = c(realized_return_capm, realized_return_ff3)
)
print(results_comparison)
## Model Realized_Return_March_2025
## 1 CAPM (Single-Index) MVP 0.04615948
## 2 FF3 (Multi-Index) MVP 0.04957562
We shift our 60-month window forward by one month (April 2020 through March 2025) to re-estimate our covariance matrices and update our portfolio weights before April begins.
window_data_apr <- merged_data %>%
filter(date >= "2020-04-01" & date <= "2025-03-31")
mkt_excess_apr <- window_data_apr[["Mkt-RF"]]
betas_apr <- numeric(length(tickers))
resid_vars_apr <- numeric(length(tickers))
for (i in 1:length(tickers)) {
ticker <- tickers[i]
etf_excess <- window_data_apr[[ticker]] - window_data_apr[["RF"]]
capm_fit <- lm(etf_excess ~ mkt_excess_apr)
betas_apr[i] <- coef(capm_fit)[2]
resid_vars_apr[i] <- var(resid(capm_fit))
}
var_mkt_apr <- var(mkt_excess_apr)
beta_matrix_apr <- matrix(betas_apr, ncol = 1)
cov_capm_apr <- (beta_matrix_apr %*% t(beta_matrix_apr)) * var_mkt_apr + diag(resid_vars_apr)
inv_cov_capm_apr <- solve(cov_capm_apr)
ones <- rep(1, length(tickers))
mvp_weights_capm_apr <- as.vector((inv_cov_capm_apr %*% ones) / as.numeric(t(ones) %*% inv_cov_capm_apr %*% ones))
B_matrix_apr <- matrix(NA, nrow = length(tickers), ncol = 3)
resid_vars_ff3_apr <- numeric(length(tickers))
for (i in 1:length(tickers)) {
ticker <- tickers[i]
etf_excess <- window_data_apr[[ticker]] - window_data_apr[["RF"]]
ff3_fit <- lm(etf_excess ~ window_data_apr[["Mkt-RF"]] + window_data_apr[["SMB"]] + window_data_apr[["HML"]])
B_matrix_apr[i, ] <- coef(ff3_fit)[2:4]
resid_vars_ff3_apr[i] <- var(resid(ff3_fit))
}
Sigma_F_apr <- cov(window_data_apr[, c("Mkt-RF", "SMB", "HML")])
cov_ff3_apr <- (B_matrix_apr %*% Sigma_F_apr %*% t(B_matrix_apr)) + diag(resid_vars_ff3_apr)
inv_cov_ff3_apr <- solve(cov_ff3_apr)
mvp_weights_ff3_apr <- as.vector((inv_cov_ff3_apr %*% ones) / as.numeric(t(ones) %*% inv_cov_ff3_apr %*% ones))
Now that we have our newly rebalanced portfolio weights based strictly on data prior to April, we can apply them to the actual April 2025 market returns.
april_data <- merged_data %>%
filter(date == "2025-04-01")
april_returns <- as.numeric(april_data[ , tickers])
names(april_returns) <- tickers
realized_return_capm_apr <- sum(april_returns * mvp_weights_capm_apr)
realized_return_ff3_apr <- sum(april_returns * mvp_weights_ff3_apr)
results_april <- data.frame(
Model = c("CAPM (Single-Index) MVP", "FF3 (Multi-Index) MVP"),
Realized_Return_April_2025 = c(realized_return_capm_apr, realized_return_ff3_apr)
)
print(results_april)
## Model Realized_Return_April_2025
## 1 CAPM (Single-Index) MVP 0.02052597
## 2 FF3 (Multi-Index) MVP 0.01846650
Visit Professor Kenneth French’s data library Web site: http://mba.tuck.dartmouth.edu/pages/faculty/ken.french/data_library.html and download the monthly returns of “6 portfolios formed on size and book-to-market (2 × 3).” Choose the value-weighted series for the period from 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 six split-halves statistics suggest to you that returns come from the same distribution over the entire period?
ff_portfolios <- download_french_data("6 Portfolios Formed on Size and Book-to-Market (2 x 3)")
vw_returns <- ff_portfolios$subsets$data[[1]]
clean_data <- vw_returns %>%
mutate(
date = ym(date),
across(-date, ~ . / 100)
) %>%
filter(date >= "1930-01-01" & date <= "2018-12-31")
split_data <- clean_data %>%
mutate(
Period = if_else(date <= "1974-06-01",
"1st Half (1930-1974)",
"2nd Half (1974-2018)")
)
summary_stats <- split_data %>%
pivot_longer(cols = -c(date, Period), names_to = "Portfolio", values_to = "Return") %>%
group_by(Period, Portfolio) %>%
summarise(
Average = mean(Return),
SD = sd(Return),
Skewness = skewness(Return),
Kurtosis = kurtosis(Return),
.groups = 'drop'
)
print(summary_stats, n = 12)
## # A tibble: 12 × 6
## Period Portfolio Average SD Skewness Kurtosis
## <chr> <chr> <dbl> <dbl> <dbl> <dbl>
## 1 1st Half (1930-1974) BIG HiBM 0.0119 0.0891 1.76 14.4
## 2 1st Half (1930-1974) BIG LoBM 0.00765 0.0571 0.178 6.86
## 3 1st Half (1930-1974) ME1 BM2 0.0117 0.0842 1.58 12.7
## 4 1st Half (1930-1974) ME2 BM2 0.00812 0.0673 1.71 17.5
## 5 1st Half (1930-1974) SMALL HiBM 0.0148 0.102 2.28 17.0
## 6 1st Half (1930-1974) SMALL LoBM 0.00971 0.0823 1.18 9.03
## 7 2nd Half (1974-2018) BIG HiBM 0.0114 0.0489 -0.516 2.78
## 8 2nd Half (1974-2018) BIG LoBM 0.00978 0.0470 -0.333 1.97
## 9 2nd Half (1974-2018) ME1 BM2 0.0135 0.0528 -0.531 3.40
## 10 2nd Half (1974-2018) ME2 BM2 0.0106 0.0434 -0.472 2.63
## 11 2nd Half (1974-2018) SMALL HiBM 0.0142 0.0550 -0.463 4.28
## 12 2nd Half (1974-2018) SMALL LoBM 0.00996 0.0669 -0.407 2.14
No, the split-halves statistics strongly suggest that the returns do NOT come from the same distribution over the entire period.
While the expected return (mean) remained somewhat consistent, the shape of the risk changed entirely. The shift from high-volatility, right-skewed, extreme fat-tailed returns (1930-1974) to lower-volatility, left-skewed, more normally distributed returns (1974-2018) proves these are two distinct statistical regimes.
Consider the following information about a risky portfolio that you manage and a risk-free asset: \(E(r_{P}) = 11%\), \(σ_{P} = 15%\), \(r_{f} = 5%\).
E_rp <- 0.11 # Expected return of the risky portfolio (11%)
sigma_p <- 0.15 # Standard deviation of the risky portfolio (15%)
r_f <- 0.05 # Risk-free rate (5%)
The expected return of a complete portfolio (\(C\)) is the weighted average of the risky asset and the risk-free asset: \[E(r_C) = y \cdot E(r_P) + (1 - y) \cdot r_f\] We can rearrange this formula to solve for the weight of the risky portfolio (\(y\)): \[y = \frac{E(r_C) - r_f}{E(r_P) - r_f}\]
E_rc_target <- 0.08
y_client1 <- (E_rc_target - r_f) / (E_rp - r_f)
w_rf_client1 <- 1 - y_client1
cat("Proportion in Risky Portfolio (y):", y_client1, "\n")
## Proportion in Risky Portfolio (y): 0.5
cat("Proportion in Risk-Free Asset (1-y):", w_rf_client1, "\n")
## Proportion in Risk-Free Asset (1-y): 0.5
Answer: The client should invest 50% of her budget in the risky portfolio and 50% in the risk-free asset.
Since the risk-free asset has a standard deviation of zero, the standard deviation of the complete portfolio is simply the proportion invested in the risky asset multiplied by its standard deviation: \[\sigma_C = y \cdot \sigma_P\]
sigma_c_client1 <- y_client1 * sigma_p
cat("Standard Deviation of Complete Portfolio:", sigma_c_client1, "\n")
## Standard Deviation of Complete Portfolio: 0.075
Answer: The standard deviation of the rate of return on her portfolio will be 7.5% (\(0.075\)).
To maximize return, the second client will push their risk allocation to the absolute maximum limit of 12%. Let’s calculate what proportion (\(y\)) that requires.
sigma_c_target <- 0.12
y_client2 <- sigma_c_target / sigma_p
cat("Client 2 Proportion in Risky Portfolio:", y_client2, "\n")
## Client 2 Proportion in Risky Portfolio: 0.8
Answer: The first client is more risk-averse. The first client is only comfortable taking on 7.5% volatility (allocating 50% to the risky asset), whereas the second client is willing to endure 12% volatility (allocating 80% to the risky asset) in pursuit of higher returns.
Investment Management Inc. (IMI) uses the capital market line to make asset allocation recommendations. IMI derives the following forecasts:
Samuel Johnson seeks IMI’s advice for a portfolio asset allocation. Johnson informs IMI that he wants the standard deviation of the portfolio to equal half of the standard deviation for the market portfolio. Using the capital market line, what expected return can IMI provide subject to Johnson’s risk constraint?
E_rm <- 0.12 # Expected return of the market (12%)
sigma_m <- 0.20 # Standard deviation of the market (20%)
r_f <- 0.05 # Risk-free rate (5%)
sigma_p <- 0.5 * sigma_m
The Capital Market Line formula is used to calculate the highest possible expected return for a given level of risk, assuming the risky asset is the broad market portfolio.The formula is: \(E(r_P) = r_f + \frac{E(r_M) - r_f}{\sigma_M} \times \sigma_P\)
sharpe_market <- (E_rm - r_f) / sigma_m
E_rp <- r_f + (sharpe_market * sigma_p)
cat("Target Portfolio Standard Deviation:", sigma_p, "\n")
## Target Portfolio Standard Deviation: 0.1
cat("Market Sharpe Ratio (Slope):", sharpe_market, "\n")
## Market Sharpe Ratio (Slope): 0.35
cat("Expected Portfolio Return:", E_rp, "\n")
## Expected Portfolio Return: 0.085
By allocating his assets to achieve a standard deviation of 10% (half of the market’s 20%), Samuel Johnson can expect a portfolio return of 8.5%. This is achieved by investing exactly 50% of his capital in the Market Portfolio and 50% in Risk-Free assets.
Which indifference curve represents the greatest level of utility that can be achieved by the investor?
In portfolio theory, indifference curves that are further to the “Northwest” (meaning higher expected return for the same or less risk) represent higher levels of utility.
While Curves 3 and 4 represent higher utility overall, they are physically unattainable because they lie completely above the Capital Allocation Line (CAL). Indifference Curve 2 is the highest curve the investor can actually reach, as it touches the CAL at exactly one point (Point F).
Answer: Indifference Curve 2
Which point designates the optimal portfolio of risky assets?
The Capital Allocation Line (CAL) is visually constructed by drawing a straight line from the risk-free asset (the starting dot on the y-axis) through the optimal portfolio of risky assets. In this standard textbook graph, Point E represents that specific 100% risky portfolio.
Answer: Point E
You manage an equity fund with an expected risk premium of 10% and an expected standard deviation of 14%. The rate on Treasury bills is 6%. Your client chooses to invest $60,000 of her portfolio in your equity fund and $40,000 in a T-bill money market fund. What are the expected return and standard deviation of return on your client’s portfolio?
risk_premium <- 0.10 # Expected risk premium of equity fund (10%)
sigma_p <- 0.14 # Standard deviation of equity fund (14%)
r_f <- 0.06 # Risk-free rate (6%)
# Investment amounts
invest_equity <- 60000
invest_tbill <- 40000
total_budget <- invest_equity + invest_tbill
First, we determine the weights of the assets in the portfolio. Then, we apply the portfolio expected return and standard deviation formulas.
y <- invest_equity / total_budget
expected_return_c <- r_f + y * risk_premium
sigma_c <- y * sigma_p
cat("Weight in Risky Fund (y):", y, "\n")
## Weight in Risky Fund (y): 0.6
cat("Expected Portfolio Return:", expected_return_c, "\n")
## Expected Portfolio Return: 0.12
cat("Portfolio Standard Deviation:", sigma_c, "\n")
## Portfolio Standard Deviation: 0.084
By investing $60,000 in the equity fund and $40,000 in T-bills, the client’s complete portfolio will have an expected return of 12% and a standard deviation of 8.4%.
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%.
Yes, an investor might still hold gold despite its lower expected return and higher volatility. This is due to the diversification benefits gold can offer.If the correlation coefficient (\(\rho\)) between the returns of stocks and gold is less than 1 (especially if it is low or negative), combining the two assets can reduce the overall risk (standard deviation) of the portfolio to a level lower than holding stocks alone. The portfolio variance formula demonstrates this: \[\sigma_p^2=w_s^2\sigma_s^2+w_g^2\sigma_g^2+2w_sw_g\sigma_s\sigma_g\rho\]
When \(\rho\) is sufficiently low, the covariance term pulls the total portfolio variance down, creating an “efficient frontier” that bows to the left.
library(ggplot2)
# Asset Data
er_s <- 0.18
sd_s <- 0.22
er_g <- 0.10
sd_g <- 0.30
rho <- 0 # Assuming 0 correlation for part A
# Portfolio weights for Gold from 0 to 1
w_g <- seq(0, 1, by = 0.01)
w_s <- 1 - w_g
# Expected Return and Standard Deviation
er_p <- w_s * er_s + w_g * er_g
sd_p <- sqrt((w_s * sd_s)^2 + (w_g * sd_g)^2 + 2 * w_s * w_g * sd_s * sd_g * rho)
df_a <- data.frame(Expected_Return = er_p, Standard_Deviation = sd_p, Weight_Gold = w_g)
# Plot
ggplot(df_a, aes(x = Standard_Deviation, y = Expected_Return)) +
geom_path(color = "blue", size = 1) +
geom_point(aes(x = sd_s, y = er_s), color = "black", size = 3) +
geom_point(aes(x = sd_g, y = er_g), color = "gold3", size = 3) +
annotate("text", x = sd_s, y = er_s + 0.005, label = "Stocks") +
annotate("text", x = sd_g, y = er_g + 0.005, label = "Gold") +
labs(title = "Investment Opportunity Set (Correlation = 0)",
x = "Portfolio Standard Deviation",
y = "Portfolio Expected Return") +
theme_minimal()
If the correlation between gold and stocks is exactly \(1\), no rational, risk-averse investor would hold a long position in gold.When \(\rho=1\), there are absolutely no diversification benefits. The portfolio standard deviation becomes a simple weighted average of the individual standard deviations: \[\sigma_p=w_s\sigma_s+w_g\sigma_g\]
Because stocks strictly dominate gold (they offer a higher return for less risk), combining them with a perfect positive correlation simply creates a straight line connecting the two assets on the risk-return graph. Moving any funds from stocks to gold will simultaneously decrease expected return and increase risk.
rho_b <- 1
sd_p_b <- sqrt((w_s * sd_s)^2 + (w_g * sd_g)^2 + 2 * w_s * w_g * sd_s * sd_g * rho_b)
df_b <- data.frame(Expected_Return = er_p, Standard_Deviation = sd_p_b)
# Plot
ggplot(df_b, aes(x = Standard_Deviation, y = Expected_Return)) +
geom_path(color = "red", size = 1) +
geom_point(aes(x = sd_s, y = er_s), color = "black", size = 3) +
geom_point(aes(x = sd_g, y = er_g), color = "gold3", size = 3) +
annotate("text", x = sd_s, y = er_s + 0.005, label = "Stocks") +
annotate("text", x = sd_g, y = er_g + 0.005, label = "Gold") +
labs(title = "Investment Opportunity Set (Correlation = 1)",
x = "Portfolio Standard Deviation",
y = "Portfolio Expected Return") +
theme_minimal()
No, this set of assumptions cannot represent a stable equilibrium for the security market.In a perfectly functioning market, if two assets are perfectly correlated (\(\rho=1\)) and one strictly dominates the other (Stocks have higher return and lower risk), an arbitrage or extreme substitution opportunity exists. Every rational investor would want to sell (or short) gold and buy stocks.
This massive selling pressure on gold would drive its current price down, which mathematically forces its expected future return to rise. Conversely, the high demand for stocks would drive their current price up, lowering their expected return. This price adjustment process would continue until gold is no longer strictly dominated by stocks, thereby restoring market equilibrium.
Suppose that there are many stocks in the security market and that the characteristics of stocks A and B are given as follows:
| Stock | Expected Return | Standard Deviation |
|---|---|---|
| A | 10% | 5% |
| B | 15 | 10 |
Correlation = \(-1\)
Suppose that it is possible to borrow at the risk-free rate, \(r_{f}\). What must be the value of the risk free rate? (Hint: Think about constructing a risk-free portfolio from stocks \(A\) and \(B\).)
er_a <- 0.10
sd_a <- 0.05
er_b <- 0.15
sd_b <- 0.10
# To achieve zero variance with a correlation of -1:
# w_a * sd_a = w_b * sd_b
# w_a * sd_a = (1 - w_a) * sd_b
# w_a * (sd_a + sd_b) = sd_b
w_a <- sd_b / (sd_a + sd_b)
w_b <- 1 - w_a
rf <- (w_a * er_a) + (w_b * er_b)
cat("Weight of Stock A for zero risk:", round(w_a * 100, 2), "%\n")
## Weight of Stock A for zero risk: 66.67 %
cat("Weight of Stock B for zero risk:", round(w_b * 100, 2), "%\n")
## Weight of Stock B for zero risk: 33.33 %
cat("Implied Risk-Free Rate (rf):", round(rf * 100, 2), "%\n")
## Implied Risk-Free Rate (rf): 11.67 %
The risk-free rate \(r_f\) must be 11.67%.
Abigail Grace has a $900,000 fully diversified portfolio. She subsequently inherits ABC Company common stock worth $100,000. Her financial adviser provided her with the following estimates:
| Portfolio/Company | Expected Monthly Returns | Standard Deviation of Monthly Returns |
|---|---|---|
| Original Portfolio | 0.67% | 2.37% |
| ABC Company | 1.25 | 2.95 |
The correlation coefficient of ABC stock returns with the original portfolio returns is .40.
Determine whether the systematic risk of her new portfolio, which includes the government securities, will be higher or lower than that of her original portfolio.
On the basis of conversations with her husband, Grace is considering selling the $100,000 of ABC stock and acquiring $100,000 of XYZ Company common stock instead. XYZ stock has the same expected return and standard deviation as ABC stock. Her husband comments, “It doesn’t matter whether you keep all of the ABC stock or replace it with $100,000 of XYZ stock.” State whether her husband’s comment is correct or incorrect. Justify your response.
In a recent discussion with her financial adviser, Grace commented, “If I just don’t lose money in my portfolio, I will be satisfied.” She went on to say, “I am more afraid of losing money than I am concerned about achieving high returns.”
Before calculating, we need to establish the portfolio weights. The total portfolio value is $1,000,000 ($900,000 original + $100,000 ABC).
Expected Return of the New Portfolio: The expected return is the weighted average of the individual expected returns: \[E(R_p) = w_oE(R_o) + w_{abc}E(R_{abc})\] \[E(R_p) = (0.90 \times 0.67\%) + (0.10 \times 1.25\%) = 0.603\% + 0.125\% = 0.728\%\]
Covariance of ABC Stock Returns with Original Portfolio:Covariance is calculated using the correlation coefficient (\(\rho\)) and the standard deviations (\(\sigma\)) of the two assets: \[Cov(o, abc) = \rho \times \sigma_o \times \sigma_{abc}\] \[Cov(o, abc) = 0.40 \times 0.0237 \times 0.0295 = 0.00027966\]
Standard Deviation of the New Portfolio:Using the two-asset portfolio variance formula: \[\sigma_p = \sqrt{w_o^2\sigma_o^2 + w_{abc}^2\sigma_{abc}^2 + 2w_ow_{abc}Cov(o, abc)}\] \[\sigma_p = \sqrt{(0.90^2 \times 2.37^2) + (0.10^2 \times 2.95^2) + (2 \times 0.90 \times 0.10 \times 2.7966)}\] \[\sigma_p = \sqrt{4.5497 + 0.0870 + 0.5034} = \sqrt{5.1401} = 2.267\%\]
The weights remain the same (90% original, 10% government securities). Risk-free assets have a standard deviation of 0%.
Expected Return of the New Portfolio: \[E(R_p) = (0.90 \times 0.67\%) + (0.10 \times 0.42\%) = 0.603\% + 0.042\% = 0.645\%\]
Covariance of Government Security Returns with Original Portfolio: The covariance is 0. By definition, a risk-free asset has no volatility (variance = 0). Since it doesn’t move at all, it cannot move in tandem with another asset. Therefore, its correlation and covariance with any risky asset are mathematically zero.
Standard Deviation of the New Portfolio: Because the standard deviation and covariance of the risk-free asset are zero, the portfolio variance formula simplifies dramatically: \[\sigma_p = \sqrt{w_o^2\sigma_o^2 + 0 + 0} = w_o \times \sigma_o\] \[\sigma_p = 0.90 \times 2.37\% = 2.133\%\]
The systematic risk of the new portfolio (with government securities) will be lower than that of her original portfolio. Systematic risk is measured by Beta. The original portfolio has a Beta of \(1.0\) relative to itself. A risk-free asset has a Beta of \(0\). By shifting 10% of her wealth into an asset with zero systematic risk, the overall systematic risk of her portfolio decreases to 90% of what it previously was.
Her husband’s comment is incorrect.
Even if XYZ stock has the exact same expected return and standard deviation as ABC stock, he is completely ignoring the correlation of XYZ stock with the original portfolio. If XYZ stock has a lower correlation with the original portfolio than ABC stock does (less than 0.40), it will provide better diversification benefits, resulting in a lower overall portfolio risk (standard deviation) without sacrificing any return.
Weakness of using Standard Deviation: Standard deviation is a symmetrical risk measure—it measures the dispersion of returns both above and below the mean. For an investor like Grace, who states she just doesn’t want to lose money, standard deviation is flawed because it penalizes unexpected massive gains (upside volatility) exactly the same way it penalizes massive losses (downside volatility).
Alternate Risk Measure: A more appropriate measure would be Semi-variance (or Downside Deviation), which only measures the volatility of returns below a specific target (in her case, below 0%). Alternatively, Value at Risk (VaR) or Expected Shortfall (Conditional VaR) would be excellent choices, as they specifically quantify the maximum expected loss over a given timeframe at a certain confidence level.
A portfolio manager summarizes the input from the macro and micro forecasters in the following table:
| Asset | Expected Return (%) | Beta | Residual Standard Deviation (%) |
|---|---|---|---|
| Stock A | 20% | 1.3 | 58% |
| Stock B | 18% | 1.8 | 71% |
| Stock C | 17% | 0.7 | 60% |
| Stock D | 12% | 1.0 | 55% |
| Asset | Expected Return (%) | Standard Deviation (%) |
|---|---|---|
| T-bills | 8% | 0% |
| Passive equity portfolio | 16% | 23% |
# --- Given Macro Data ---
rf <- 0.08
er_m <- 0.16
sd_m <- 0.23
var_m <- sd_m^2
rm_excess <- er_m - rf
A <- 2.8
# --- Given Micro Data ---
stocks <- c("Stock A", "Stock B", "Stock C", "Stock D")
er_i <- c(0.20, 0.18, 0.17, 0.12)
beta_i <- c(1.3, 1.8, 0.7, 1.0)
sd_e_i <- c(0.58, 0.71, 0.60, 0.55)
# ---------------------------------------------------------
# Part A: Excess Returns, Alphas, and Residual Variances
# ---------------------------------------------------------
r_i_excess <- er_i - rf
alpha_i <- r_i_excess - (beta_i * rm_excess)
var_e_i <- sd_e_i^2
part_a_results <- data.frame(
Stock = stocks,
Excess_Return = r_i_excess,
Alpha = alpha_i,
Residual_Var = var_e_i
)
cat("--- Part A: Stock Metrics ---\n")
## --- Part A: Stock Metrics ---
print(part_a_results, digits = 4)
## Stock Excess_Return Alpha Residual_Var
## 1 Stock A 0.12 0.016 0.3364
## 2 Stock B 0.10 -0.044 0.5041
## 3 Stock C 0.09 0.034 0.3600
## 4 Stock D 0.04 -0.040 0.3025
cat("\n")
# ---------------------------------------------------------
# Part B: Construct Optimal Risky Portfolio
# ---------------------------------------------------------
active_ratios <- alpha_i / var_e_i
w_active_initial <- active_ratios / sum(active_ratios)
alpha_A <- sum(w_active_initial * alpha_i)
beta_A <- sum(w_active_initial * beta_i)
var_e_A <- sum((w_active_initial^2) * var_e_i)
w0 <- (alpha_A / var_e_A) / (rm_excess / var_m)
wA_star <- w0 / (1 + (1 - beta_A) * w0)
wM_star <- 1 - wA_star
final_stock_weights <- wA_star * w_active_initial
cat("--- Part B: Optimal Risky Portfolio Weights ---\n")
## --- Part B: Optimal Risky Portfolio Weights ---
cat("Passive Market Index:", round(wM_star * 100, 2), "%\n")
## Passive Market Index: 104.86 %
cat("Active Portfolio Total:", round(wA_star * 100, 2), "%\n")
## Active Portfolio Total: -4.86 %
for(i in 1:4){
cat(" ->", stocks[i], ":", round(final_stock_weights[i] * 100, 2), "%\n")
}
## -> Stock A : 2.98 %
## -> Stock B : -5.47 %
## -> Stock C : 5.92 %
## -> Stock D : -8.28 %
cat("\n")
# ---------------------------------------------------------
# Part C & D: Sharpe Ratios
# ---------------------------------------------------------
sr_m <- rm_excess / sd_m
ir_sq <- sum((alpha_i / sd_e_i)^2)
sr_p <- sqrt(sr_m^2 + ir_sq)
cat("--- Part C & D: Sharpe Ratios ---\n")
## --- Part C & D: Sharpe Ratios ---
cat("Market Sharpe Ratio:", round(sr_m, 4), "\n")
## Market Sharpe Ratio: 0.3478
cat("Optimal Portfolio Sharpe Ratio (Part C):", round(sr_p, 4), "\n")
## Optimal Portfolio Sharpe Ratio (Part C): 0.3662
cat("Sharpe Ratio Improvement (Part D):", round(sr_p - sr_m, 4), "\n\n")
## Sharpe Ratio Improvement (Part D): 0.0183
# ---------------------------------------------------------
# Part E: Complete Portfolio Allocation (A = 2.8)
# ---------------------------------------------------------
er_p_excess <- (wM_star * rm_excess) + (wA_star * (alpha_A + beta_A * rm_excess))
beta_p <- (wM_star * 1) + (wA_star * beta_A)
var_p <- (beta_p^2 * var_m) + ((wA_star^2) * var_e_A)
y <- er_p_excess / (A * var_p)
weight_rf <- 1 - y
cat("--- Part E: Complete Portfolio Allocation (A = 2.8) ---\n")
## --- Part E: Complete Portfolio Allocation (A = 2.8) ---
cat("Risk-Free Asset (T-bills):", round(weight_rf * 100, 2), "%\n")
## Risk-Free Asset (T-bills): 42.99 %
cat("Optimal Risky Portfolio:", round(y * 100, 2), "%\n")
## Optimal Risky Portfolio: 57.01 %
cat(" -> Passive Market Index:", round(y * wM_star * 100, 2), "%\n")
## -> Passive Market Index: 59.77 %
for(i in 1:4){
cat(" ->", stocks[i], ":", round(y * final_stock_weights[i] * 100, 2), "%\n")
}
## -> Stock A : 1.7 %
## -> Stock B : -3.12 %
## -> Stock C : 3.37 %
## -> Stock D : -4.72 %
When the annualized monthly percentage excess rates of return for a stock market index were regressed against the excess returns for ABC and XYZ stocks over the most recent 5-year period, using an ordinary least squares regression, the following results were obtained:
| Statistic | ABC | XYZ |
|---|---|---|
| Alpha | −3.20% | 7.30% |
| Beta | 0.60 | 0.97 |
| R² | 0.35 | 0.17 |
| Residual Standard Deviation | 13.02% | 21.45% |
Explain what these regression results tell the analyst about risk–return relationships for each stock over the sample period. Comment on their implications for future risk–return relationships, assuming both stocks were included in a diversified common stock portfolio, especially in view of the following additional data obtained from two brokerage houses, which are based on the most recent two years of weekly returns.
| Brokerage House | Beta of ABC | Beta of XYZ |
|---|---|---|
| A | 0.62 | 1.45 |
| B | 0.71 | 1.25 |
Historical Risk-Return (5-Year Regression)
Implications for a Diversified Portfolio
Impact of New Brokerage Data