Monthly Stock Returns Analysis
Monthly Stock Returns Analysis
2024-10-26
library(plotly)
##
## Attaching package: 'PerformanceAnalytics'
## The following object is masked from 'package:graphics':
##
## legend
1
library(tseries)
library(forecast)
library(smooth)
# Question 2
start_date = "2017-01-01"
end_date = "2020-12-31"
## [1] "AAPL"
monthly_aapl <- [Link](AAPL)
aapl_prices <- monthly_aapl$[Link]
aapl_returns <- diff(log(aapl_prices))
## [1] "PG"
monthly_pg <- [Link](PG)
pg_prices <- monthly_pg$[Link]
pg_returns <- diff(log(pg_prices))
## [1] "AMZN"
monthly_amzn <- [Link](AMZN)
amzn_prices <- monthly_amzn$[Link]
amzn_returns <- diff(log(amzn_prices))
## [1] "JPM"
monthly_jpm <- [Link](JPM)
jpm_prices <- monthly_jpm$[Link]
jpm_returns <- diff(log(jpm_prices))
2
# Walmart (WMT) Monthly Returns
getSymbols("WMT", from = start_date, to = end_date, src = "yahoo")
## [1] "WMT"
monthly_wmt <- [Link](WMT)
wmt_prices <- monthly_wmt$[Link]
wmt_returns <- diff(log(wmt_prices))
## Warning: ^TNX contains missing values. Some functions will not work if objects
## contain missing values in the middle of the series. Consider using [Link](),
## [Link](), [Link](), etc to remove or replace them.
## [1] "TNX"
monthly_tnx <- [Link](TNX)
# Excess Returns
excess_aapl <- aapl_returns - risk_free_returns
excess_pg <- pg_returns - risk_free_returns
excess_amzn <- amzn_returns - risk_free_returns
excess_jpm <- jpm_returns - risk_free_returns
excess_wmt <- wmt_returns - risk_free_returns
# Information Tables
price_data <- [Link](aapl_prices, pg_prices, amzn_prices, jpm_prices, wmt_prices)
returns_data <- [Link](aapl_returns, pg_returns, amzn_returns, jpm_returns, wmt_returns)
excess_returns_data <- [Link](excess_aapl, excess_pg, excess_amzn, excess_jpm, excess_wmt)
returns_data <- returns_data[-1,] # Remove NA row
excess_returns_data <- excess_returns_data[-1,]
# Variance-Covariance Matrix
stock_names <- c("AAPL", "PG", "AMZN", "JPM", "WMT") # Stock names
var_cov_matrix <- matrix(c(cov(returns_data)), nrow = 5, ncol = 5, byrow = TRUE)
dimnames(var_cov_matrix) <- list(stock_names, stock_names)
# Display Data
var_cov_matrix
3
# Calculate Returns
mean_returns <- matrix(colMeans(returns_data, [Link] = TRUE))
mean_excess_returns <- matrix(colMeans(excess_returns_data, [Link] = TRUE))
avg_risk_free <- mean((risk_free_returns)[-1,])
cat("\n")
return_matrix
# Display Data
optimal_weight
## Weights
## AAPL 0.2682597
## PG 0.2191622
## AMZN 0.2765342
## JPM -0.1756479
## WMT 0.4116918
# Calculate Stats
opt_portfolio_return <- t(optimal_weight) %*% mean_returns
opt_portfolio_variance <- t(optimal_weight) %*% var_cov_matrix %*% optimal_weight
opt_portfolio_sd <- sqrt(opt_portfolio_variance)
Sharpe_Ratio <- (opt_portfolio_return - avg_risk_free) / opt_portfolio_sd
cat("\n")
dimnames(opt_portfolio_stats) <- list(optstat_names, "Opt. Portfolio")
opt_portfolio_stats
## Opt. Portfolio
4
## Return 0.025173612
## Variance 0.002695675
## Std Dev 0.051919894
## Sharpe ratio 0.452432754
## Global Minimum Variance portfolio
gmv_weights <- solve(var_cov_matrix, matrix(rep(1, 5), byrow = TRUE))
gmv_weight <- gmv_weights / sum(gmv_weights)
dimnames(gmv_weight) <- list(stock_names, "Weights")
# Display Data
gmv_weight
## Weights
## AAPL -0.05468113
## PG 0.59377999
## AMZN 0.13869409
## JPM 0.09273972
## WMT 0.22946732
cat("\n")
gmv_portfolio_stats
## GMV Portfolio
## Return 0.014563922
## Variance 0.001478138
## Std Dev 0.038446556
# Efficient Frontier and CAL
j <- 0
return_portfolio <- rep(0, 50000)
sd_portfolio <- rep(0, 50000)
vect_0 <- rep(0, 50000)
fractions <- matrix(vect_0, 10000, 5)
5
return_portfolio[j] <- fractions[j,] %*% mean_returns
}
}
}
}
}
}
0.02
0.00
Short Sale
GMV
Tangency Portfolio
−0.02
CAL
# Set Dates
start_date = "2021-01-01"
end_date = "2024-06-30"
6
# Fetch Data for Selected Stocks
getSymbols("AAPL", from = start_date, to = end_date, periodicity = "monthly")
## [1] "AAPL"
getSymbols("PG", from = start_date, to = end_date, periodicity = "monthly")
## [1] "PG"
getSymbols("AMZN", from = start_date, to = end_date, periodicity = "monthly")
## [1] "AMZN"
getSymbols("JPM", from = start_date, to = end_date, periodicity = "monthly")
## [1] "JPM"
getSymbols("WMT", from = start_date, to = end_date, periodicity = "monthly")
## [1] "WMT"
# Fetch S&P 500 Data
getSymbols("ˆGSPC", from = start_date, to = end_date, src = "yahoo", periodicity = "monthly")
## [1] "GSPC"
rGSPC <- diff(log(Ad(GSPC))) # Use adjusted closing prices only
rGSPC <- rGSPC[-1,]
SP500 <- Ad(GSPC)
## Warning: ^TNX contains missing values. Some functions will not work if objects
## contain missing values in the middle of the series. Consider using [Link](),
## [Link](), [Link](), etc to remove or replace them.
## [1] "TNX"
priceTNX <- Ad(TNX) # Accessing the adjusted close price for TNX
rTNX <- diff(log(priceTNX)) / 100
## [,1]
## AAPL 0.2078924
7
## PG 0.1887335
## AMZN 0.1724997
## JPM -0.1522083
## WMT 0.9299538
# Fetch Closing Prices
close_price_AAPL <- Ad(AAPL)
close_price_PG <- Ad(PG)
close_price_AMZN <- Ad(AMZN)
close_price_JPM <- Ad(JPM)
close_price_WMT <- Ad(WMT)
# Align Dates
min_length <- min(length(index(AAPL)), length(portfolio_index), length(SP500_index))
dates <- index(AAPL)[1:min_length]
portfolio_index <- portfolio_index[1:min_length]
SP500_index <- SP500_index[1:min_length]
# Create DataFrames
monthly_index_df <- [Link](Date = dates, Index = portfolio_index)
SP500_index_df <- [Link](Date = dates, Index = SP500_index)
8
Tangency Portfolio Index (2021−2024)
120
100
Index
80
60
40
2021 2022 2023 2024
Date
# Question - 4
library(xts)
library(PerformanceAnalytics)
library(ggplot2)
9
# Estimate CAPM beta
beta <- [Link](portfolio_xts, market_xts, Rf = risk_free_rate)
0.01
Expected Return
0.00
0 1 2
Beta
# Calculate the excess returns for CAPM model
market_excess_returns <- market_returns - risk_free_rate
portfolio_excess_returns <- portfolio_returns - risk_free_rate
10
# Fit the CAPM model
capm_model <- lm(portfolio_excess_returns ~ market_excess_returns)
##
## Call:
## lm(formula = portfolio_excess_returns ~ market_excess_returns)
##
## Residuals:
## Min 1Q Median 3Q Max
## -0.7447 -0.1482 0.0161 0.3651 0.6229
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -0.002716 0.066103 -0.041 0.967
## market_excess_returns -0.618277 1.338420 -0.462 0.647
##
## Residual standard error: 0.4161 on 39 degrees of freedom
## (1 observation deleted due to missingness)
## Multiple R-squared: 0.005442, Adjusted R-squared: -0.02006
## F-statistic: 0.2134 on 1 and 39 DF, p-value: 0.6467
# Data frame for scatter plot of market vs portfolio returns
df <- [Link](market_excess_returns, portfolio_excess_returns)
11
0.4
Portfolio Return (WeightPortRet)
0.0
−0.4
−0.8
−0.10 −0.05 0.00 0.05
Market Return (RSP)
#QUESTION-5
# Create xts object for portfolio returns
portfolio_xts <- xts(portfolio_returns, [Link] = dates)
# Calculate the interaction term between market returns and shutdown dummy
market_interaction <- [Link](excess_market_returns) * shutdown_dummy
12
portfolio_returns = portfolio_returns_vector,
excess_market_returns = excess_market_returns_vector,
shutdown_dummy = shutdown_dummy_vector,
market_interaction = market_interaction_vector
)
##
## Call:
## lm(formula = portfolio_returns ~ excess_market_returns + shutdown_dummy +
## market_interaction, data = covid_data)
##
## Residuals:
## Min 1Q Median 3Q Max
## -0.7447 -0.1482 0.0161 0.3651 0.6229
##
## Coefficients: (2 not defined because of singularities)
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -0.002358 0.066103 -0.036 0.972
## excess_market_returns -0.618277 1.338420 -0.462 0.647
## shutdown_dummy NA NA NA NA
## market_interaction NA NA NA NA
##
## Residual standard error: 0.4161 on 39 degrees of freedom
## (1 observation deleted due to missingness)
## Multiple R-squared: 0.005442, Adjusted R-squared: -0.02006
## F-statistic: 0.2134 on 1 and 39 DF, p-value: 0.6467
# QUESTION-6
## [1] "VTI"
rVTI <- diff(log(VTI$[Link]))
rVTI <- [Link](rVTI)
13
}
cv <- calculateCV(portfolio_returns)
cvVTI <- calculateCV(rVTI)
risk_free_rate
## [1] 0.0003577014
# Sharpe Ratio
sharpe_ratio <- [Link](portfolio_xts, Rf = risk_free_rate)
vanguard_sharpe_ratio <- [Link](vanguard_xts, Rf = risk_free_rate)
# Sortino Ratio
sortino_ratio <- SortinoRatio(portfolio_xts, MAR = risk_free_rate)
vanguard_sortino_ratio <- SortinoRatio(vanguard_xts, MAR = risk_free_rate)
# Treynor Ratio
treynor_ratio <- TreynorRatio(portfolio_xts, market_xts, Rf = risk_free_rate)
vanguard_treynor_ratio <- TreynorRatio(vanguard_xts, market_xts, Rf = risk_free_rate)
# Print results
print(paste("Coefficient of Variation Portfolio:", round(cv, 4)))
14
mean_return_monthly <- mean(portfolio_returns)
mean_return_six_months <- mean(portfolio_returns) * 6
mean_return_annual <- mean(portfolio_returns) * 12
# Calculate VaR at 2%
VaR_2_percent <- quantile(portfolio_returns, 0.02, [Link] = TRUE)
VaR_2_percentVTI <- quantile(rVTI, 0.02, [Link] = TRUE)
15
Scatter Diagram of Portfolio Returns Over Time
0.4
Portfolio Returns
0.0
−0.4
−0.8
Dates
# Scatter diagram for Portfolio vs. S&P 500 Returns
plot(portfolio_df$portfolio_xts, market_df$market_xts,
col = 'black',
pch = 16,
main = 'Scatter Plot of Portfolio vs. S&P 500 Returns',
xlab = 'Portfolio Returns',
ylab = 'S&P 500 Returns')
abline(lm(market_df$market_xts ~ portfolio_df$portfolio_xts), col = 'red') # Add trend line
legend("topleft", legend = c("Scatter Points", "Trend Line"), col = c('black', 'red'),
pch = c(16, NA), lty = c(NA, 1))
16
Scatter Plot of Portfolio vs. S&P 500 Returns
Scatter Points
Trend Line
0.05
S&P 500 Returns
0.00
−0.05
−0.10
Portfolio Returns
print(portfolio_returns)
##
## Attaching package: 'Metrics'
## The following object is masked from 'package:smooth':
##
## accuracy
## The following object is masked from 'package:greybox':
##
## accuracy
## The following object is masked from 'package:forecast':
##
## accuracy
# Split data into training and testing sets
train_size <- length(portfolio_returns) - 2
train_set <- [Link](
portfolio_excess_returns = portfolio_excess_returns[1:train_size],
market_excess_returns = market_excess_returns[1:train_size]
)
17
test_set <- [Link](
portfolio_excess_returns = portfolio_excess_returns[(train_size+1):(train_size+2)],
market_excess_returns = market_excess_returns[(train_size+1):(train_size+2)]
)
18
Actual vs Forecasted Portfolio Returns
−0.4
−0.5
Returns
−0.6
−0.7
Actual
Forecasted
Test Periods
# Show CAPM model details
capm_model
##
## Call:
## lm(formula = portfolio_excess_returns ~ market_excess_returns,
## data = train_set)
##
## Coefficients:
## (Intercept) market_excess_returns
## 0.008922 -0.483695
# Predictions with standard error
pred <- predict(capm_model, newdata = test_set, [Link] = TRUE)
print(pred)
## $fit
## 1 2
## -0.00739087 NA
##
## $[Link]
## 1 2
## 0.07350413 NA
##
## $df
## [1] 38
##
## $[Link]
## [1] 0.4130659
19
#QUESTION-10
capm_model <- lm(portfolio_excess_returns ~ market_excess_returns, data = train_set)
## 1 2
## 0.02009444 0.03350310
# Plot forecasted values with appropriate labels
plot(1:2, fore, type = "o", col = "blue", pch = 16,
xaxt = "n", # Disable default x-axis
xlab = "Periods", ylab = "Forecasted Portfolio Returns",
main = "Ex-Ante Forecast of Portfolio Returns")
axis(1, at = 1:2, labels = c("2024M7", "2023M8")) # Add custom x-axis labels
0.028
0.024
0.020
2024M7 2023M8
Periods
#QUESTION-11
library(Metrics)
samplestart4 <- [Link]('2020-01-01')
sampleend4 <- [Link]('2024-08-01')
start_date1 = "2017-01-01"
end_date1 = "2024-08-31"
20
AAPL23 <- getSymbols("AAPL", from = start_date1, to = end_date1, periodicity = "monthly", [Link] =
MSFT23 <- getSymbols("MSFT", from = start_date1, to = end_date1, periodicity = "monthly", [Link] =
AMZN23 <- getSymbols("AMZN", from = start_date1, to = end_date1, periodicity = "monthly", [Link] =
JNJ23 <- getSymbols("JNJ", from = start_date1, to = end_date1, periodicity = "monthly", [Link] = FA
WMT23 <- getSymbols("WMT", from = start_date1, to = end_date1, periodicity = "monthly", [Link] = FA
# Naïve Forecast
Lagged <- rwf(Port4, h = 3)
plot(Lagged, ylab = 'Indexed Portfolio')
21
Forecasts from Random walk
200
Indexed Portfolio
150
100
140
100
Time
# Exponential Smoothing
ESfit1 <- ses(Port4, alpha = 0.2, initial = "simple", h = 3)
ESfit2 <- ses(Port4, alpha = 0.6, initial = "simple", h = 3)
ESfit3 <- ses(Port4, h = 3)
22
fcol = "white", type = "o")
lines(fitted(ESfit1), col = "red", type = "o")
lines(fitted(ESfit2), col = "black", type = "o")
lines(fitted(ESfit3), col = "blue", type = "o")
lines(ESfit1$mean, col = "red", type = "o")
lines(ESfit2$mean, col = "black", type = "o")
lines(ESfit3$mean, col = "blue", type = "o")
legend("topleft", lty = 1, col = c(1, "red", "black", "blue"),
c("data", expression(alpha == 0.2), expression(alpha == 0.6),
expression(alpha == 0.99)), pch = 1)
data
α = 0.2
α = 0.6
α = 0.99
180
140
100
accuracy_matrix <- matrix(c(MAE = mae, RMSE = rmse, MAPE = mape, MSE = mse),
nrow = 1,
dimnames = list(c(" "),
c("MAE", "RMSE", "MAPE", "SE")))
23
# Print accuracy comparison
print(accuracy_matrix)
24