Choose Portfolio and Baseline Market

# ---- 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)

Steps 1-4: Build the Portfolio

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.

Step 5: Calculate CAPM Beta

5.1 Get Market Returns

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)

5.2 Join 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

5.3 Calculate the Beta Coefficient

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)

Step 6: Scatterplot of Returns with Regression Line

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'

Written Answer: How Sensitive Is My Portfolio to the Market?

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.