# ---- EDIT THESE ----
symbols <- c("SPY", "EFA", "IJS", "EEM", "AGG")
weights <- c(0.25, 0.25, 0.20, 0.20, 0.10)
market_symbol <- "SPY" # try "QQQ" for a tech-heavy portfolio
start_date <- "2012-12-01" # start one month early; slice(-1) drops it
end_date <- "2018-01-01"
# --------------------
stopifnot(length(symbols) == length(weights), abs(sum(weights) - 1) < 1e-8)
prices <- tq_get(
x = symbols,
get = "stock.prices",
from = start_date,
to = end_date
)
asset_returns_tbl <- prices %>%
group_by(symbol) %>%
tq_transmute(
select = adjusted,
mutate_fun = periodReturn,
period = "monthly",
type = "log"
) %>%
slice(-1) %>%
ungroup() %>%
set_names(c("asset", "date", "returns"))
symbols_vec <- asset_returns_tbl %>%
distinct(asset) %>%
pull()
w_tbl <- tibble(symbols_vec, weights) %>%
set_names(c("asset", "weights"))
portfolio_returns_tbl <- asset_returns_tbl %>%
tq_portfolio(
assets_col = asset,
returns_col = returns,
weights = w_tbl,
col_rename = "returns",
rebalance_on = "months"
)
## Warning in check_weights(weights, assets_col, map, x): Sum of weights does not
## equal 1.
market_returns_tbl <- tq_get(
x = market_symbol,
get = "stock.prices",
from = start_date,
to = end_date
) %>%
tq_transmute(
select = adjusted,
mutate_fun = periodReturn,
period = "monthly",
type = "log"
) %>%
slice(-1) %>%
rename(returns = monthly.returns)
portfolio_market_returns_tbl <- left_join(
market_returns_tbl,
portfolio_returns_tbl,
by = "date"
) %>%
set_names(c("date", "market_returns", "portfolio_returns"))
portfolio_market_returns_tbl
## # A tibble: 60 × 3
## date market_returns portfolio_returns
## <date> <dbl> <dbl>
## 1 2013-01-31 0.0499 0.0204
## 2 2013-02-28 0.0127 -0.00239
## 3 2013-03-28 0.0373 0.0121
## 4 2013-04-30 0.0190 0.0174
## 5 2013-05-31 0.0233 -0.0128
## 6 2013-06-28 -0.0134 -0.0247
## 7 2013-07-31 0.0504 0.0321
## 8 2013-08-30 -0.0305 -0.0224
## 9 2013-09-30 0.0312 0.0511
## 10 2013-10-31 0.0453 0.0301
## # ℹ 50 more rows
beta_tbl <- portfolio_market_returns_tbl %>%
tq_performance(
Ra = portfolio_returns,
Rb = market_returns,
performance_fun = CAPM.beta
)
beta_tbl
## # A tibble: 1 × 1
## CAPM.beta.1
## <dbl>
## 1 0.738
beta_value <- beta_tbl %>% pull(1)
# How tightly the points sit around the regression line (0 to 1)
r_squared <- portfolio_market_returns_tbl %>%
lm(portfolio_returns ~ market_returns, data = .) %>%
glance() %>%
pull(r.squared)
The slope of the regression line is the beta coefficient. The dashed gray line is a 45-degree reference line (beta = 1, where the portfolio moves one-for-one with the market).
portfolio_market_returns_tbl %>%
ggplot(aes(x = market_returns, y = portfolio_returns)) +
geom_abline(slope = 1, intercept = 0, color = "gray50", linetype = 2) +
geom_point(color = "cornflowerblue") +
geom_smooth(
method = "lm",
se = FALSE,
linewidth = 1.5,
color = tidyquant::palette_light()[[3]]
) +
labs(
x = paste0("market returns (", market_symbol, ")"),
y = "portfolio returns",
title = "Portfolio Returns vs. Market Returns"
) +
theme(plot.title = element_text(hjust = 0.5))
## `geom_smooth()` using formula = 'y ~ x'
My portfolio’s beta against SPY is 0.74, and the regression’s R-squared is 0.73.
Sign.
Size.
Does the plot match the beta from Step 5?
Baseline market choice.