library(xts)
## Loading required package: zoo
## 
## Attaching package: 'zoo'
## The following objects are masked from 'package:base':
## 
##     as.Date, as.Date.numeric
library(tibble)

# Simulate test returns for 3 assets over 100 days
set.seed(123)
dates <- seq(as.Date("2024-01-01"), by = "days", length.out = 100)
wide_returns <- tibble(
  date = dates,
  SPY = rnorm(100, 0.0005, 0.01),
  QQQ = rnorm(100, 0.0007, 0.012),
  IWM = rnorm(100, 0.0004, 0.015)
)

# Convert to xts
rets <- wide_returns[-1]  # remove date
rets_xts <- xts(rets, order.by = wide_returns$date)
library(PortfolioAnalytics)
library(PerformanceAnalytics)
library(ROI)
library(ROI.plugin.quadprog)

# Define the portfolio spec
funds <- colnames(rets_xts)
pspec <- portfolio.spec(assets = funds)
pspec <- add.constraint(pspec, type = "full_investment")
pspec <- add.constraint(pspec, type = "long_only")
pspec <- add.objective(pspec, type = "risk", name = "StdDev")
pspec <- add.objective(pspec, type = "return", name = "mean")

# Compute efficient frontier
eff <- create.EfficientFrontier(R = rets_xts, portfolio = pspec, type = "mean-StdDev")
## Warning: executing %dopar% sequentially: no parallel backend registered
# Output image to knit-safe plot
jpeg("ef_plot.jpg")
chart.EfficientFrontier(eff, match.col = "StdDev", n.portfolios = 25,
                        main = "Efficient Frontier: SPY, QQQ, IWM")
dev.off()
## png 
##   2
# Display in report
knitr::include_graphics("ef_plot.jpg")

#Chapter 7: Efficient Diversification

## Q1: Test Semistrong EMH by comparing returns before/after event (e.g., dividend)
library(quantmod)
## Loading required package: TTR
## Registered S3 method overwritten by 'quantmod':
##   method            from
##   as.zoo.data.frame zoo
getSymbols("AAPL", from = "2023-01-01", to = "2024-01-01")
## [1] "AAPL"
# Suppose dividend announcement on 2023-08-01
pre_event <- window(Cl(AAPL), end = as.Date("2023-07-31"))
post_event <- window(Cl(AAPL), start = as.Date("2023-08-02"))

# Compare returns
pre_return <- dailyReturn(pre_event)
post_return <- dailyReturn(post_event)

summary(pre_return)
##      Index            daily.returns      
##  Min.   :2023-01-03   Min.   :-0.026680  
##  1st Qu.:2023-02-23   1st Qu.:-0.005863  
##  Median :2023-04-17   Median : 0.002019  
##  Mean   :2023-04-16   Mean   : 0.003217  
##  3rd Qu.:2023-06-07   3rd Qu.: 0.010341  
##  Max.   :2023-07-31   Max.   : 0.046927
summary(post_return)
##      Index            daily.returns       
##  Min.   :2023-08-02   Min.   :-0.0480201  
##  1st Qu.:2023-09-08   1st Qu.:-0.0073216  
##  Median :2023-10-16   Median : 0.0012667  
##  Mean   :2023-10-15   Mean   : 0.0000746  
##  3rd Qu.:2023-11-21   3rd Qu.: 0.0079049  
##  Max.   :2023-12-29   Max.   : 0.0219489
## Q2: Calculate correlation matrix of 3 assets to analyze diversification
library(tidyquant)
## ── Conflicts ────────────────────────────────────────── tidyquant_conflicts() ──
## ✖ zoo::as.Date()                 masks base::as.Date()
## ✖ zoo::as.Date.numeric()         masks base::as.Date.numeric()
## ✖ PerformanceAnalytics::legend() masks graphics::legend()
## ✖ quantmod::summary()            masks base::summary()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(dplyr)
## 
## ######################### Warning from 'xts' package ##########################
## #                                                                             #
## # The dplyr lag() function breaks how base R's lag() function is supposed to  #
## # work, which breaks lag(my_xts). Calls to lag(my_xts) that you type or       #
## # source() into this session won't work correctly.                            #
## #                                                                             #
## # Use stats::lag() to make sure you're not using dplyr::lag(), or you can add #
## # conflictRules('dplyr', exclude = 'lag') to your .Rprofile to stop           #
## # dplyr from breaking base R's lag() function.                                #
## #                                                                             #
## # Code in packages is not affected. It's protected by R's namespace mechanism #
## # Set `options(xts.warn_dplyr_breaks_lag = FALSE)` to suppress this warning.  #
## #                                                                             #
## ###############################################################################
## 
## Attaching package: 'dplyr'
## 
## The following objects are masked from 'package:xts':
## 
##     first, last
## 
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## 
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
library(tidyr)
symbols <- c("SPY", "QQQ", "IWM")
prices <- tq_get(symbols, from = "2023-01-01", to = "2024-01-01")

returns <- prices %>%
  group_by(symbol) %>%
  tq_transmute(select = adjusted, mutate_fun = periodReturn, period = "monthly")

wide_returns <- returns %>%
  pivot_wider(names_from = symbol, values_from = monthly.returns)

cor(wide_returns[-1], use = "complete.obs")
##           SPY       QQQ       IWM
## SPY 1.0000000 0.8772807 0.8268349
## QQQ 0.8772807 1.0000000 0.6647969
## IWM 0.8268349 0.6647969 1.0000000
##Chapter7 CFA4
# Define data
portfolios <- data.frame(
  Portfolio = c("W", "X", "Z", "Y"),
  Return = c(15, 12, 5, 9),
  StdDev = c(36, 15, 7, 21)
)

# Plot efficient frontier scatter
library(ggplot2)

ggplot(portfolios, aes(x = StdDev, y = Return, label = Portfolio)) +
  geom_point(color = "blue", size = 3) +
  geom_text(vjust = -0.8) +
  labs(title = "CFA 7.4 – Portfolio Returns vs Risk",
       x = "Standard Deviation (%)",
       y = "Expected Return (%)") +
  theme_minimal()

#CFA chapter7 CFA10
# Define portfolio data
beta_A <- 1.0
beta_B <- 1.0
rf <- 0.05               # 5% risk-free rate
market_premium <- 0.08   # 8% market risk premium

# Compute expected returns under CAPM
exp_return_A <- rf + beta_A * market_premium
exp_return_B <- rf + beta_B * market_premium

# Display result
paste0("Expected Return of A = ", round(exp_return_A * 100, 2), "%\n",
       "Expected Return of B = ", round(exp_return_B * 100, 2), "%")
## [1] "Expected Return of A = 13%\nExpected Return of B = 13%"
## Q3: Plot efficient frontier of 3-asset portfolio
# Given values
risk_free_rate <- 0.05        # 5%
market_risk_premium <- 0.08   # 8%
beta <- 1.5

# CAPM Formula
expected_return <- risk_free_rate + beta * market_risk_premium

# Display the result as a percentage
paste0("Expected return = ", round(expected_return * 100, 2), "%")
## [1] "Expected return = 17%"

``` #Chapter 8: Index Models

## Q1: Estimate alpha and beta using linear regression (CAPM style)
library(quantmod)
library(PerformanceAnalytics)

# Download stock and market index data
getSymbols(c("AAPL", "^GSPC"), from = "2023-01-01", to = "2024-01-01")
## [1] "AAPL" "GSPC"
# Compute daily excess returns (assume risk-free rate = 0)
stock_ret <- dailyReturn(Cl(AAPL))
market_ret <- dailyReturn(Cl(GSPC))

# Run CAPM-style regression
model <- lm(stock_ret ~ market_ret)
summary(model)
## 
## Call:
## lm(formula = stock_ret ~ market_ret)
## 
## Residuals:
##       Min        1Q    Median        3Q       Max 
## -0.042963 -0.004653  0.000441  0.004985  0.035036 
## 
## Coefficients:
##              Estimate Std. Error t value Pr(>|t|)    
## (Intercept) 0.0007919  0.0005505   1.439    0.152    
## market_ret  1.1036341  0.0665136  16.593   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.008651 on 248 degrees of freedom
## Multiple R-squared:  0.5261, Adjusted R-squared:  0.5242 
## F-statistic: 275.3 on 1 and 248 DF,  p-value: < 2.2e-16
## Q3: Extract R² and standard error from regression output
r_squared <- summary(model)$r.squared
std_error <- summary(model)$sigma

r_squared
## [1] 0.5260981
std_error
## [1] 0.008650579
## Q4: Compare two beta estimates with confidence intervals
# Assume Broker A = 0.62, Broker B = 0.71; Model beta = our regression

model_beta <- coef(model)[2]
broker_a <- 0.62
broker_b <- 0.71

c(model_beta = model_beta, broker_a = broker_a, broker_b = broker_b)
## model_beta.market_ret              broker_a              broker_b 
##              1.103634              0.620000              0.710000
## Q5: Compute beta-based risk and total risk (standard deviation)
beta_value <- coef(model)[2]
total_risk <- sd(stock_ret)
market_risk <- sd(market_ret)

risk_table <- data.frame(
  Beta = beta_value,
  Std_Dev = total_risk,
  Market_StdDev = market_risk
)

risk_table
##                Beta    Std_Dev Market_StdDev
## market_ret 1.103634 0.01254085   0.008242053

#Chapter 9: Capital Asset Pricing Model

## Q8: Calculate Sharpe and Treynor Ratios for Portfolio R and S&P 500
rf <- 0.03  # Assume risk-free rate = 3%

# Portfolio R
r_return <- 0.11
r_sd <- 0.10
r_beta <- 0.5

# S&P 500
m_return <- 0.14
m_sd <- 0.12
m_beta <- 1.0

sharpe_r <- (r_return - rf) / r_sd
sharpe_m <- (m_return - rf) / m_sd

treynor_r <- (r_return - rf) / r_beta
treynor_m <- (m_return - rf) / m_beta

data.frame(
  Portfolio = c("R", "S&P 500"),
  Sharpe = c(sharpe_r, sharpe_m),
  Treynor = c(treynor_r, treynor_m)
)
##   Portfolio    Sharpe Treynor
## 1         R 0.8000000    0.16
## 2   S&P 500 0.9166667    0.11
## Q9: Plot Risk vs Return for visualization

portfolio_data <- data.frame(
  Portfolio = c("R", "S&P 500"),
  Return = c(0.11, 0.14),
  StdDev = c(0.10, 0.12),
  Beta = c(0.5, 1.0)
)

library(ggplot2)
ggplot(portfolio_data, aes(x = StdDev, y = Return, label = Portfolio)) +
  geom_point(size = 3, color = "blue") +
  geom_text(vjust = -1) +
  labs(title = "Risk vs Return Plot",
       x = "Standard Deviation (Risk)",
       y = "Expected Return") +
  theme_minimal()

## Q10: Draw the SML line based on CAPM
beta_vals <- seq(0, 1.5, 0.1)
sml_returns <- rf + beta_vals * (m_return - rf)

sml_df <- data.frame(Beta = beta_vals, ExpectedReturn = sml_returns)

ggplot(sml_df, aes(x = Beta, y = ExpectedReturn)) +
  geom_line(color = "darkgreen", size = 1.2) +
  geom_point(data = portfolio_data, aes(x = Beta, y = Return), color = "blue", size = 3) +
  geom_text(data = portfolio_data, aes(x = Beta, y = Return, label = Portfolio), vjust = -1) +
  labs(title = "Security Market Line (SML)",
       x = "Beta",
       y = "Expected Return") +
  theme_minimal()
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.

#Chapter 10: Arbitrage Pricing Theory

## Problem 1: Revised expected return using 2-factor APT
expected_return <- 0.12
beta_ip <- 1
beta_ir <- 0.5

expected_ip <- 0.03
actual_ip <- 0.05

expected_ir <- 0.05
actual_ir <- 0.08

revised_return <- expected_return +
  beta_ip * (actual_ip - expected_ip) +
  beta_ir * (actual_ir - expected_ir)

revised_return
## [1] 0.155
## Problem 4: Solve factor risk premiums λ₁ and λ₂

library(MASS)
## 
## Attaching package: 'MASS'
## The following object is masked from 'package:dplyr':
## 
##     select
betas <- matrix(c(1.5, 2.0,
                  2.2, -0.2), nrow = 2, byrow = TRUE)
returns <- c(0.31 - 0.06, 0.27 - 0.06)  # Subtract rf

lambdas <- ginv(betas) %*% returns
lambdas
##      [,1]
## [1,] 0.10
## [2,] 0.05
## Recalculate expected return using solved lambda values
rf <- 0.06

# Portfolio A
beta1_A <- 1.5
beta2_A <- 2.0

expected_A <- rf + beta1_A * lambdas[1] + beta2_A * lambdas[2]

# Portfolio B
beta1_B <- 2.2
beta2_B <- -0.2

expected_B <- rf + beta1_B * lambdas[1] + beta2_B * lambdas[2]

data.frame(Portfolio = c("A", "B"),
           GivenReturn = c(0.31, 0.27),
           APT_Return = c(expected_A, expected_B))
##   Portfolio GivenReturn APT_Return
## 1         A        0.31       0.31
## 2         B        0.27       0.27

#Chapter 11: The Efficient Market Hypothesis

## CFA 1: Three Forms of Efficient Market Hypothesis
emh_types <- data.frame(
  Type = c("Weak-form", "Semi-strong form", "Strong-form"),
  Information_Used = c(
    "Past price and volume data",
    "All publicly available information (incl. financials, news)",
    "All information, both public and private (insider info)"
  ),
  Implication = c(
    "Technical analysis is useless",
    "Neither technical nor fundamental analysis consistently helps",
    "Even insiders can't consistently outperform"
  )
)

emh_types
##               Type                                            Information_Used
## 1        Weak-form                                  Past price and volume data
## 2 Semi-strong form All publicly available information (incl. financials, news)
## 3      Strong-form     All information, both public and private (insider info)
##                                                     Implication
## 1                                 Technical analysis is useless
## 2 Neither technical nor fundamental analysis consistently helps
## 3                   Even insiders can't consistently outperform
## CFA 2: Simulate random walk of stock prices
set.seed(42)

n_days <- 100
returns <- rnorm(n_days, mean = 0.0005, sd = 0.01)  # Simulate daily returns
price <- cumprod(1 + returns) * 100  # Starting from 100

plot(price, type = "l", col = "blue", lwd = 2,
     main = "Simulated Stock Price (Random Walk)",
     xlab = "Day", ylab = "Price")

## CFA 3: Simulate stock reaction to positive earnings surprise
set.seed(123)

pre_event <- rnorm(10, 0.0005, 0.01)
event_impact <- c(0.05)  # Positive shock
post_event <- rnorm(10, 0.0005, 0.01)

price <- cumsum(c(pre_event, event_impact, post_event)) + 100

plot(price, type = "l", col = "darkgreen", lwd = 2,
     main = "Stock Price Response to Earnings Surprise",
     xlab = "Time (Day)", ylab = "Price")
abline(v = 11, col = "red", lty = 2)
text(11, price[11] + 0.5, "Event Day", pos = 4)

## CFA 4: Compare active fund vs passive index (simulated returns)
set.seed(111)

days <- 250
active <- cumprod(1 + rnorm(days, 0.0004, 0.012)) * 100
passive <- cumprod(1 + rnorm(days, 0.0005, 0.01)) * 100

plot(passive, type = "l", col = "black", lwd = 2,
     ylim = range(c(active, passive)),
     ylab = "Value", xlab = "Days", main = "Active vs. Passive Fund")
lines(active, col = "blue", lwd = 2)
legend("topleft", legend = c("Passive Index", "Active Fund"),
       col = c("black", "blue"), lwd = 2)

## CFA 5: Implications of EMH for investment strategies
emh_summary <- data.frame(
  Strategy = c("Technical Analysis", "Fundamental Analysis", "Index Investing", "Insider Trading"),
  WeakForm = c("No advantage", "May help", "Good", "Possible advantage"),
  SemiStrongForm = c("No advantage", "No advantage", "Good", "Possible advantage"),
  StrongForm = c("No advantage", "No advantage", "Good", "No advantage")
)

emh_summary
##               Strategy           WeakForm     SemiStrongForm   StrongForm
## 1   Technical Analysis       No advantage       No advantage No advantage
## 2 Fundamental Analysis           May help       No advantage No advantage
## 3      Index Investing               Good               Good         Good
## 4      Insider Trading Possible advantage Possible advantage No advantage

#Chapter 12: Behavioral Finance and Technical Analysis

## CFA 1: EMH vs. Behavioral Finance
emh_vs_behavioral <- data.frame(
  Assumption = c("Investor Rationality", "No Arbitrage"),
  EMH_View = c("Investors are rational and utility-maximizing",
               "Mispricings are quickly exploited by arbitrageurs"),
  Behavioral_View = c("Investors often act irrationally due to biases and heuristics",
                      "Limits to arbitrage exist (cost, risk, sentiment-driven bubbles)")
)

emh_vs_behavioral
##             Assumption                                          EMH_View
## 1 Investor Rationality     Investors are rational and utility-maximizing
## 2         No Arbitrage Mispricings are quickly exploited by arbitrageurs
##                                                    Behavioral_View
## 1    Investors often act irrationally due to biases and heuristics
## 2 Limits to arbitrage exist (cost, risk, sentiment-driven bubbles)
## CFA 2: Simulate Loss Aversion (Losses hurt more than gains feel good)

library(ggplot2)

gain_loss <- seq(-50, 50, by = 1)
utility <- ifelse(gain_loss >= 0, gain_loss^0.88, -2.25 * (-gain_loss)^0.88)

loss_aversion <- data.frame(gain_loss, utility)

ggplot(loss_aversion, aes(x = gain_loss, y = utility)) +
  geom_line(color = "red", size = 1.2) +
  labs(title = "Loss Aversion Curve",
       x = "Gain or Loss",
       y = "Perceived Value / Utility") +
  theme_minimal()

#Chapter 13: Regression Testing of the CAPM

## Problem 1: First-Pass Regression for Each Stock
library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ forcats   1.0.0     ✔ readr     2.1.5
## ✔ lubridate 1.9.4     ✔ stringr   1.5.1
## ✔ purrr     1.0.4     
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ purrr::accumulate() masks foreach::accumulate()
## ✖ dplyr::filter()     masks stats::filter()
## ✖ dplyr::first()      masks xts::first()
## ✖ dplyr::lag()        masks stats::lag()
## ✖ dplyr::last()       masks xts::last()
## ✖ MASS::select()      masks dplyr::select()
## ✖ purrr::when()       masks foreach::when()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
# Load or simulate data (replace with your actual returns)
# Let's say we have stock returns and market returns
stock_data <- data.frame(
  market_excess = c(0.01, 0.015, -0.005, 0.02, 0.01, 0.025, -0.01, 0.005, 0.015, -0.005),
  stock_A_excess = c(0.005, 0.01, -0.01, 0.015, 0.005, 0.03, -0.015, 0.002, 0.012, -0.008)
)

# First-pass regression for Stock A
model_A <- lm(stock_A_excess ~ market_excess, data = stock_data)
summary(model_A)
## 
## Call:
## lm(formula = stock_A_excess ~ market_excess, data = stock_data)
## 
## Residuals:
##        Min         1Q     Median         3Q        Max 
## -0.0031074 -0.0018512 -0.0002231  0.0007479  0.0062645 
## 
## Coefficients:
##                Estimate Std. Error t value Pr(>|t|)    
## (Intercept)   -0.004405   0.001134  -3.884  0.00465 ** 
## market_excess  1.125620   0.083385  13.499  8.7e-07 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.002901 on 8 degrees of freedom
## Multiple R-squared:  0.9579, Adjusted R-squared:  0.9527 
## F-statistic: 182.2 on 1 and 8 DF,  p-value: 8.701e-07
## Problem 2: Hypotheses for SML Regression
hypotheses <- data.frame(
  Null = "γ1 > 0: Higher beta → Higher expected return (CAPM holds)",
  Alt = "γ1 = 0: No relation between beta and return (CAPM fails)",
  Intercept = "γ0 ≈ 0 if excess return is used"
)

hypotheses
##                                                        Null
## 1 γ1 > 0: Higher beta → Higher expected return (CAPM holds)
##                                                        Alt
## 1 γ1 = 0: No relation between beta and return (CAPM fails)
##                         Intercept
## 1 γ0 ≈ 0 if excess return is used
## Problem 3: Regress average excess return on beta
# Suppose we already calculated average excess returns and betas:
beta_values <- c(-0.2, 0.1, 0.5, 0.7, 0.9, 1.0, 1.1, 1.3, 1.5)
avg_excess_returns <- c(0.002, 0.004, 0.006, 0.007, 0.008, 0.009, 0.010, 0.011, 0.013)

sml_data <- data.frame(beta = beta_values, return = avg_excess_returns)
sml_model <- lm(return ~ beta, data = sml_data)
summary(sml_model)
## 
## Call:
## lm(formula = return ~ beta, data = sml_data)
## 
## Residuals:
##        Min         1Q     Median         3Q        Max 
## -0.0006034 -0.0002227 -0.0000804  0.0002082  0.0006811 
## 
## Coefficients:
##              Estimate Std. Error t value Pr(>|t|)    
## (Intercept) 0.0030303  0.0002475   12.25 5.55e-06 ***
## beta        0.0061924  0.0002667   23.22 6.97e-08 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.0004182 on 7 degrees of freedom
## Multiple R-squared:  0.9872, Adjusted R-squared:  0.9854 
## F-statistic: 539.3 on 1 and 7 DF,  p-value: 6.967e-08
# Plot SML
ggplot(sml_data, aes(x = beta, y = return)) +
  geom_point() +
  geom_smooth(method = "lm", se = FALSE, col = "blue") +
  labs(title = "Security Market Line (SML)", x = "Beta", y = "Average Excess Return") +
  theme_minimal()
## `geom_smooth()` using formula = 'y ~ x'

cat("Higher beta stocks tend to offer higher returns.\n")
## Higher beta stocks tend to offer higher returns.
cat("R² of second-pass regression indicates strength of CAPM fit.\n")
## R² of second-pass regression indicates strength of CAPM fit.
cat("Stock H and I with high betas performed best, A underperformed.\n")
## Stock H and I with high betas performed best, A underperformed.
## Problem 5: Portfolio Creation and Second-Pass on Portfolios
# Group beta into 3 buckets
sml_data$portfolio <- cut(sml_data$beta, breaks = 3, labels = c("Low", "Medium", "High"))

# Calculate portfolio-level averages
portfolio_summary <- sml_data %>%
  group_by(portfolio) %>%
  summarize(beta = mean(beta), return = mean(return))

# Second-pass regression on portfolios
sml_portfolio_model <- lm(return ~ beta, data = portfolio_summary)
summary(sml_portfolio_model)
## 
## Call:
## lm(formula = return ~ beta, data = portfolio_summary)
## 
## Residuals:
##          1          2          3 
##  0.0001518 -0.0003687  0.0002169 
## 
## Coefficients:
##              Estimate Std. Error t value Pr(>|t|)  
## (Intercept) 0.0031495  0.0004083   7.714   0.0821 .
## beta        0.0060274  0.0005009  12.033   0.0528 .
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.0004539 on 1 degrees of freedom
## Multiple R-squared:  0.9931, Adjusted R-squared:  0.9863 
## F-statistic: 144.8 on 1 and 1 DF,  p-value: 0.05278
# Plot portfolio SML
ggplot(portfolio_summary, aes(x = beta, y = return)) +
  geom_point(size = 3) +
  geom_smooth(method = "lm", se = FALSE, color = "darkgreen") +
  labs(title = "SML with Portfolios", x = "Portfolio Beta", y = "Portfolio Excess Return") +
  theme_minimal()
## `geom_smooth()` using formula = 'y ~ x'

## Problem 6: Roll's Critique Text Output
cat("Roll's critique argues CAPM cannot be tested because the true market portfolio is unobservable.\n")
## Roll's critique argues CAPM cannot be tested because the true market portfolio is unobservable.
cat("Using a proxy like S&P 500 introduces benchmark error.\n")
## Using a proxy like S&P 500 introduces benchmark error.
cat("Thus, CAPM test results may reflect errors in the benchmark, not model failure.\n")
## Thus, CAPM test results may reflect errors in the benchmark, not model failure.
## Problem 7: Plot CML with 9 stocks and 3 portfolios
# Simulate std. deviation values
portfolio_summary$sd <- c(0.07, 0.09, 0.12)  # Just an example
market_return <- 0.009
market_sd <- 0.10
rf_rate <- 0.002

# Define CML
cml_slope <- (market_return - rf_rate) / market_sd
cml_x <- seq(0, 0.15, 0.01)
cml_y <- rf_rate + cml_slope * cml_x

# Plot
plot(cml_x, cml_y, type = "l", col = "red", lwd = 2,
     main = "Capital Market Line", xlab = "Standard Deviation", ylab = "Expected Return")
points(portfolio_summary$sd, portfolio_summary$return, pch = 19, col = "blue")
text(portfolio_summary$sd, portfolio_summary$return + 0.001,
     labels = portfolio_summary$portfolio, pos = 3)
points(market_sd, market_return, col = "green", pch = 17)
legend("topleft", legend = c("CML", "Portfolios", "Market"), col = c("red", "blue", "green"),
       lty = c(1, NA, NA), pch = c(NA, 19, 17))

##CFA 2a: Portfolio Evaluation and Benchmarks
cat("Portfolio performance is typically evaluated by comparing actual returns with expected returns predicted by a benchmark (e.g., CAPM using S&P 500).\n")
## Portfolio performance is typically evaluated by comparing actual returns with expected returns predicted by a benchmark (e.g., CAPM using S&P 500).
cat("The benchmark portfolio is assumed to represent the true 'market portfolio', used to derive expected returns based on beta.\n")
## The benchmark portfolio is assumed to represent the true 'market portfolio', used to derive expected returns based on beta.
##CFA 2b: Roll’s Benchmark Error Argument
cat("Richard Roll argued that the CAPM is untestable because the true market portfolio (which includes all assets: real estate, human capital, etc.) is unobservable.\n")
## Richard Roll argued that the CAPM is untestable because the true market portfolio (which includes all assets: real estate, human capital, etc.) is unobservable.
cat("Therefore, using a proxy like the S&P 500 introduces 'benchmark error' — any test of the CAPM may be invalid if the benchmark isn't the true market.\n")
## Therefore, using a proxy like the S&P 500 introduces 'benchmark error' — any test of the CAPM may be invalid if the benchmark isn't the true market.
##CFA 2c: Graphical Example – Perceived vs. True SML
library(ggplot2)

df <- data.frame(
  Beta = c(0.5, 1.0, 1.5),
  Measured_Return = c(0.05, 0.08, 0.11),
  True_Return = c(0.03, 0.08, 0.13)
)

ggplot(df, aes(x = Beta)) +
  geom_line(aes(y = Measured_Return), color = "blue", size = 1.2) +
  geom_line(aes(y = True_Return), color = "red", size = 1.2, linetype = "dashed") +
  geom_point(aes(y = Measured_Return), color = "blue") +
  geom_point(aes(y = True_Return), color = "red") +
  labs(title = "Measured vs. True Security Market Line",
       y = "Expected Return", x = "Beta") +
  theme_minimal() +
  annotate("text", x = 1.2, y = 0.115, label = "Measured SML", color = "blue") +
  annotate("text", x = 1.2, y = 0.135, label = "True SML", color = "red")

##CFA 2d: Comfort from Multiple Benchmarks?
cat("If a manager is judged superior using multiple benchmarks (Dow Jones, S&P 500, NYSE), it increases confidence.\n")
## If a manager is judged superior using multiple benchmarks (Dow Jones, S&P 500, NYSE), it increases confidence.
cat("However, all of these may still be poor proxies for the true market portfolio.\n")
## However, all of these may still be poor proxies for the true market portfolio.
cat("So while consensus helps, Roll’s critique still applies — true skill is hard to prove without the true market.\n")
## So while consensus helps, Roll’s critique still applies — true skill is hard to prove without the true market.
##CFA 2e: Defending a Position on Benchmark Errors
cat("Position: CAPM is still useful despite benchmark errors.\n")
## Position: CAPM is still useful despite benchmark errors.
cat("Roll’s critique shows implementation challenges, not theoretical flaws.\n")
## Roll’s critique shows implementation challenges, not theoretical flaws.
cat("Using diversified indexes as proxies provides practical value for investment decisions and comparisons.\n")
## Using diversified indexes as proxies provides practical value for investment decisions and comparisons.
cat("Thus, CAPM should be refined, not scrapped.\n")
## Thus, CAPM should be refined, not scrapped.