Install necessary packages:
# install.packages("quantmod")
# install.packages("PerformanceAnalytics")
# install.packages("tseries")
# install.packages("R.matlab")
# install.packages("tidyquant")
# install.packages("tidyr")
# install.packages("dplyr")
Load Libraries:
library(quantmod)
## Loading required package: xts
## Loading required package: zoo
##
## Attaching package: 'zoo'
## The following objects are masked from 'package:base':
##
## as.Date, as.Date.numeric
## Loading required package: TTR
## Registered S3 method overwritten by 'quantmod':
## method from
## as.zoo.data.frame zoo
library(PerformanceAnalytics)
##
## Attaching package: 'PerformanceAnalytics'
## The following object is masked from 'package:graphics':
##
## legend
library(xts)
library(tseries)
library(quadprog)
library(ggplot2)
library(tidyquant)
## Loading required package: lubridate
##
## Attaching package: 'lubridate'
## The following objects are masked from 'package:base':
##
## date, intersect, setdiff, union
library(tidyr)
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
The first step is to pull in the historical stock data that we will use to create our portfolio and pass through the models:
stocks <- c("AAPL","MSFT", "GE", "LMT", "PFE", "NVO", "AMZN", "TSLA", "WFC", "BLK")
startDate <- as.Date("2018-01-01")
endDate <- as.Date("2022-10-31")
stockData <- list()
for (stock in stocks) {
stockData[[stock]] <- getSymbols(stock, src = "yahoo",
from = startDate,
to = endDate,
auto.assign = FALSE)
}
stockPrices <- do.call(merge, lapply(stockData@.Data, function(x) Ad(x)))
Next, we will build the (equally weighted) portfolio:
prices <- do.call(merge, lapply(stockData, Ad))
returns <- Return.calculate(prices)
returns[is.na(returns)] <- 0
numStocks <- length(stockData)
weights <- rep(1/numStocks, numStocks)
portfolioReturns <- Return.portfolio(R = returns, weights = weights)
head(portfolioReturns)
## portfolio.returns
## 2018-01-02 0.000000000
## 2018-01-03 0.006011433
## 2018-01-04 0.007244349
## 2018-01-05 0.007751192
## 2018-01-08 0.003792290
## 2018-01-09 0.003213404
With this portfolio, we can now start testing our portfolio optimization methods.
The first method is Value-at-Risk and Conditional Value-at-Risk
confidenceLevel <- 0.95
VaR_Historical <- VaR(portfolioReturns, p = confidenceLevel, method = "historical")
CVaR_Historical <- ES(portfolioReturns, p = confidenceLevel, method = "historical")
cat("Historical VaR at", confidenceLevel*100, "% confidence level is:", VaR_Historical, "\n")
## Historical VaR at 95 % confidence level is: -0.03115543
cat("Historical CVaR at", confidenceLevel*100, "% confidence level is:", CVaR_Historical, "\n")
## Historical CVaR at 95 % confidence level is: -0.04622044
Next we will run a Monte Carlo simulation:
monteCarloOptimization <- function(returns, numPortfolios = 10000) {
numAssets <- ncol(returns)
results <- matrix(0, nrow = numPortfolios, ncol = 3 + numAssets) # Extend matrix to store weights
colnames(results) <- c("Return", "Risk", "Sharpe", paste("Weight", 1:numAssets, sep = "_"))
for (i in 1:numPortfolios) {
weights <- runif(numAssets)
weights <- weights / sum(weights)
portfolioReturn <- sum(apply(returns, 1, function(x) sum(x * weights)))
portfolioRisk <- sqrt(t(weights) %*% cov(returns) %*% weights)
sharpeRatio <- portfolioReturn / portfolioRisk
results[i,] <- c(portfolioReturn, portfolioRisk, sharpeRatio, weights)
}
return(results)
}
mcResults <- monteCarloOptimization(returns)
optimalPortfolio <- mcResults[which.max(mcResults[, "Sharpe"]),]
cat("Optimal Portfolio:\n")
## Optimal Portfolio:
cat(sprintf("Return: %f\n", optimalPortfolio["Return"]))
## Return: 1.600277
cat(sprintf("Risk: %f\n", optimalPortfolio["Risk"]))
## Risk: 0.016983
cat(sprintf("Sharpe Ratio: %f\n", optimalPortfolio["Sharpe"]))
## Sharpe Ratio: 94.227205
numAssets <- ncol(returns)
for (j in 1:numAssets) {
cat(sprintf("Weight of Asset %d: %f\n", j, optimalPortfolio[paste("Weight", j, sep = "_")]))
}
## Weight of Asset 1: 0.089983
## Weight of Asset 2: 0.030653
## Weight of Asset 3: 0.003950
## Weight of Asset 4: 0.031873
## Weight of Asset 5: 0.206962
## Weight of Asset 6: 0.250239
## Weight of Asset 7: 0.085758
## Weight of Asset 8: 0.267519
## Weight of Asset 9: 0.020181
## Weight of Asset 10: 0.012883
Next, we will do CAPM optimization. First, calculate the stock returns for further analysis:
stocks <- c("AAPL","MSFT", "GE", "LMT", "PFE", "NVO", "AMZN", "TSLA", "WFC", "BLK")
startDate <- as.Date("2018-01-01")
endDate <- as.Date("2022-10-31")
stockReturns <- list()
for (stock in stocks) {
tempReturns <- Delt(Ad(stockData[[stock]]))
tempReturns[1, ] <- 0 # Set the first day's return to 0
stockReturns[[stock]] <- tempReturns
}
mergedReturns <- do.call(merge, stockReturns)
head(mergedReturns)
## Delt.1.arithmetic Delt.1.arithmetic.1 Delt.1.arithmetic.2
## 2018-01-02 0.000000000 0.0000000000 0.0000000000
## 2018-01-03 -0.000174141 0.0046538648 0.0094550831
## 2018-01-04 0.004645007 0.0088014679 0.0209365040
## 2018-01-05 0.011385554 0.0123979846 0.0005396223
## 2018-01-08 -0.003714225 0.0010206830 -0.0140236180
## 2018-01-09 -0.000114764 -0.0006794837 0.0153172270
## Delt.1.arithmetic.3 Delt.1.arithmetic.4 Delt.1.arithmetic.5
## 2018-01-02 0.000000000 0.000000000 0.0000000000
## 2018-01-03 0.008382108 0.007409472 0.0096013389
## 2018-01-04 0.017153680 0.002179333 -0.0034746850
## 2018-01-05 0.009059648 0.001902770 0.0045877822
## 2018-01-08 -0.004610340 -0.011123219 -0.0009132804
## 2018-01-09 0.007161003 -0.001097439 0.0031083693
## Delt.1.arithmetic.6 Delt.1.arithmetic.7 Delt.1.arithmetic.8
## 2018-01-02 0.000000000 0.000000000 0.000000000
## 2018-01-03 0.012775310 -0.010233113 0.007693562
## 2018-01-04 0.004476013 -0.008289978 0.012508093
## 2018-01-05 0.016162517 0.006229741 0.006738352
## 2018-01-08 0.014424679 0.062638220 -0.011314616
## 2018-01-09 0.004675710 -0.008085381 0.003545651
## Delt.1.arithmetic.9
## 2018-01-02 0.000000000
## 2018-01-03 0.010550846
## 2018-01-04 0.013201333
## 2018-01-05 0.008519984
## 2018-01-08 0.007458555
## 2018-01-09 0.008404273
Next, we will need to pull in Market return data:
sp500Data <- getSymbols("^GSPC", src = "yahoo", from = startDate, to = endDate, auto.assign = FALSE)
if (is.null(sp500Data)) {
stop("Failed to retrieve S&P 500 data")
} else {
sp500Close <- Ad(sp500Data)
marketReturn <- Return.calculate(sp500Close)
marketReturn[1,] <- 0
head(marketReturn)
}
## GSPC.Adjusted
## 2018-01-02 0.000000000
## 2018-01-03 0.006398819
## 2018-01-04 0.004028636
## 2018-01-05 0.007033767
## 2018-01-08 0.001662344
## 2018-01-09 0.001302932
Next, we calculate the Beta using the CAPM method:
calculateBeta <- function(mergedReturns, marketReturn) {
combinedData <- cbind(coredata(mergedReturns), coredata(marketReturn))
covMat <- cov(combinedData)
beta <- covMat[1, 2] / var(coredata(marketReturn))
return(beta)
}
capmOptimization <- function(mergedReturns, marketReturn, riskFreeRate) {
numAssets <- ncol(mergedReturns)
betas <- numeric(numAssets)
for (i in 1:numAssets) {
betas[i] <- calculateBeta(mergedReturns[, i], marketReturn)
}
expectedReturns <- riskFreeRate + betas * (mean(marketReturn, na.rm = TRUE) - riskFreeRate)
covReturns <- cov(mergedReturns, use = "pairwise.complete.obs")
Dmat <- 2 * covReturns
dvec <- rep(0, numAssets)
Amat <- rbind(1, diag(numAssets))
bvec <- c(1, rep(0, numAssets))
portfolioOptim <- solve.QP(Dmat, dvec, t(Amat), bvec, meq = 1)
portfolioWeights <- portfolioOptim$solution
return(list(betas = betas, expectedReturns = expectedReturns, weights = portfolioWeights))
}
riskFreeRate <- 0.015
capmResults <- capmOptimization(mergedReturns, marketReturn, riskFreeRate)
format(capmResults, scientific = FALSE)
## betas
## "1.2153067, 1.2030067, 1.1398328, 0.6943240, 0.6195502, 0.5809871, 1.0689141, 1.4330871, 1.1887334, 1.2212237"
## expectedReturns
## "-0.0027448360, -0.0025652424, -0.0016428319, 0.0048620942, 0.0059538748, 0.0065169399, -0.0006073399, -0.0059246723, -0.0023568364, -0.0028312301"
## weights
## "0.0000000000000000000000000, -0.0000000000000000001319786, 0.0174174226883696790790790, 0.2708489615816870865749877, 0.2682033891351394694346766, 0.2935596103868725603724954, 0.1279709420708866063343123, 0.0021416117236306591621597, 0.0198580624134140244774205, 0.0000000000000000026930492"
print(capmResults)
## $betas
## [1] 1.2153067 1.2030067 1.1398328 0.6943240 0.6195502 0.5809871 1.0689141
## [8] 1.4330871 1.1887334 1.2212237
##
## $expectedReturns
## [1] -0.0027448360 -0.0025652424 -0.0016428319 0.0048620942 0.0059538748
## [6] 0.0065169399 -0.0006073399 -0.0059246723 -0.0023568364 -0.0028312301
##
## $weights
## [1] 0.000000e+00 -1.319786e-19 1.741742e-02 2.708490e-01 2.682034e-01
## [6] 2.935596e-01 1.279709e-01 2.141612e-03 1.985806e-02 2.693049e-18
Markowitz Optimization
markowitzOptimization <- function(mergedReturns, riskFreeRate) {
numAssets <- ncol(mergedReturns)
meanReturns <- colMeans(mergedReturns)
covReturns <- cov(mergedReturns)
Dmat <- 2 * covReturns
dvec <- rep(0, numAssets)
if (!is.matrix(Dmat) || nrow(Dmat) != ncol(Dmat) || nrow(Dmat) != length(dvec)) {
stop("Dmat or dvec dimension error")
}
Amat <- rbind(1, diag(numAssets))
bvec <- c(1, rep(0, numAssets))
if (ncol(Amat) != length(dvec) || nrow(Amat) != length(bvec)) {
stop("Amat or bvec dimension error")
}
sol <- solve.QP(Dmat, dvec, t(Amat), bvec, meq = 1)
weights <- sol$solution
return(weights)
}
riskFreeRate <- 0.015
optimalWeights <- markowitzOptimization(mergedReturns, riskFreeRate)
format(optimalWeights, scientific = FALSE)
## [1] " 0.0000000000000000000000000" "-0.0000000000000000001319786"
## [3] " 0.0174174226883696790790790" " 0.2708489615816870865749877"
## [5] " 0.2682033891351394694346766" " 0.2935596103868725603724954"
## [7] " 0.1279709420708866063343123" " 0.0021416117236306591621597"
## [9] " 0.0198580624134140244774205" " 0.0000000000000000026930492"
print(optimalWeights)
## [1] 0.000000e+00 -1.319786e-19 1.741742e-02 2.708490e-01 2.682034e-01
## [6] 2.935596e-01 1.279709e-01 2.141612e-03 1.985806e-02 2.693049e-18
(I also have a black litterman function i want to add here when I figure it out)
Mean Variance Frontier
meanVarianceFrontier <- function(mergedReturns, riskFreeRate, targetReturns) {
numAssets <- ncol(mergedReturns)
meanReturns <- colMeans(mergedReturns)
covReturns <- cov(mergedReturns)
Dmat <- 2 * covReturns
dvec <- rep(0, numAssets)
risks <- numeric(length(targetReturns))
weightsList <- vector("list", length(targetReturns))
for (i in seq_along(targetReturns)) {
Amat <- rbind(1, diag(numAssets), meanReturns)
bvec <- c(1, rep(0, numAssets), targetReturns[i])
sol <- solve.QP(Dmat, dvec, t(Amat), bvec, meq = 2)
risks[i] <- sqrt(t(sol$solution) %*% covReturns %*% sol$solution)
weightsList[[i]] <- sol$solution
}
efficientFrontier <- data.frame(Return = targetReturns, Risk = risks, Weights = weightsList)
return(efficientFrontier)
}
targetReturns <- seq(from = min(colMeans(mergedReturns)), to = max(colMeans(mergedReturns)), length.out = 10)
riskFreeRate <- 0.015
frontier <- meanVarianceFrontier(mergedReturns, riskFreeRate, targetReturns)
print(frontier)
## Return Risk
## 1 -4.634187e-05 0.01225770
## 2 2.689091e-04 0.01225770
## 3 5.841600e-04 0.01225770
## 4 8.994110e-04 0.01280337
## 5 1.214662e-03 0.01523553
## 6 1.529913e-03 0.01916682
## 7 1.845164e-03 0.02391364
## 8 2.160415e-03 0.02922132
## 9 2.475666e-03 0.03499802
## 10 2.790917e-03 0.04107459
## Weights.c.0...1.31978639907573e.19..0.0174174226883697..0.270848961581687..
## 1 0.000000e+00
## 2 -1.319786e-19
## 3 1.741742e-02
## 4 2.708490e-01
## 5 2.682034e-01
## 6 2.935596e-01
## 7 1.279709e-01
## 8 2.141612e-03
## 9 1.985806e-02
## 10 2.693049e-18
## Weights.c.0...1.31978639907573e.19..0.0174174226883697..0.270848961581687...1
## 1 0.000000e+00
## 2 -1.319786e-19
## 3 1.741742e-02
## 4 2.708490e-01
## 5 2.682034e-01
## 6 2.935596e-01
## 7 1.279709e-01
## 8 2.141612e-03
## 9 1.985806e-02
## 10 2.693049e-18
## Weights.c.0...1.31978639907573e.19..0.0174174226883697..0.270848961581687...2
## 1 0.000000e+00
## 2 -1.319786e-19
## 3 1.741742e-02
## 4 2.708490e-01
## 5 2.682034e-01
## 6 2.935596e-01
## 7 1.279709e-01
## 8 2.141612e-03
## 9 1.985806e-02
## 10 2.693049e-18
## Weights.c..3.24544041989652e.18..0.0297718959886366...7.29493561413981e.18..
## 1 -3.245440e-18
## 2 2.977190e-02
## 3 -7.294936e-18
## 4 2.607006e-01
## 5 2.324713e-01
## 6 3.310897e-01
## 7 5.174021e-02
## 8 9.422625e-02
## 9 0.000000e+00
## 10 4.443706e-18
## Weights.c.4.69272179376424e.17..0.0785421256584486..4.80890527126556e.18..
## 1 4.692722e-17
## 2 7.854213e-02
## 3 4.808905e-18
## 4 1.950442e-01
## 5 1.371037e-01
## 6 3.662023e-01
## 7 -4.045212e-17
## 8 2.231077e-01
## 9 2.163452e-17
## 10 -2.486965e-17
## Weights.c.5.52105629765884e.17..0.0867261209731244..1.50096593021237e.18..
## 1 5.521056e-17
## 2 8.672612e-02
## 3 1.500966e-18
## 4 1.227461e-01
## 5 3.153111e-02
## 6 4.010511e-01
## 7 -3.047554e-17
## 8 3.579456e-01
## 9 -4.836212e-17
## 10 -1.631335e-17
## Weights.c.7.2138917269427e.17..0.0739225158645998...1.24835744175808e.18..
## 1 7.213892e-17
## 2 7.392252e-02
## 3 -1.248357e-18
## 4 1.489222e-02
## 5 0.000000e+00
## 6 4.086092e-01
## 7 -2.533174e-17
## 8 5.025761e-01
## 9 2.373321e-17
## 10 -1.197335e-17
## Weights.c.1.02077430195765e.16..0..7.26860060107578e.18..0...1.32502910183705e.17..
## 1 1.020774e-16
## 2 0.000000e+00
## 3 7.268601e-18
## 4 0.000000e+00
## 5 -1.325029e-17
## 6 3.279480e-01
## 7 -2.014579e-17
## 8 6.720520e-01
## 9 4.169394e-17
## 10 6.029592e-17
## Weights.c.1.25126038420057e.16..0..1.02951345012955e.17..0...6.07249133874413e.17..
## 1 1.251260e-16
## 2 0.000000e+00
## 3 1.029513e-17
## 4 0.000000e+00
## 5 -6.072491e-17
## 6 1.639740e-01
## 7 -2.696022e-17
## 8 8.360260e-01
## 9 5.400600e-17
## 10 1.451594e-16
## Weights.c.1.48174646644348e.16..0..1.33216684015151e.17..0...1.08199535756512e.16..
## 1 1.481746e-16
## 2 0.000000e+00
## 3 1.332167e-17
## 4 0.000000e+00
## 5 -1.081995e-16
## 6 4.163336e-16
## 7 -3.377464e-17
## 8 1.000000e+00
## 9 6.631805e-17
## 10 1.745117e-16
Visualize the Mean Variance Frontier
ggplot(frontier, aes(x = Risk, y = Return)) +
geom_line(color = "blue") +
labs(title = "Mean-Variance Efficient Frontier",
x = "Portfolio Risk (Standard Deviation)",
y = "Expected Portfolio Return") +
theme_minimal()
Next, we pull in data from after our historical period to test each portfolio against: (Note, important to run this chunk again to overwrite the original (historical) data from the previous chunks)
stocks <- c("AAPL","MSFT", "GE", "LMT", "PFE", "NVO", "AMZN", "TSLA", "WFC", "BLK")
startDate <- as.Date("2023-01-01")
endDate <- as.Date("2023-10-31")
stockData <- list()
for (stock in stocks) {
stockData[[stock]] <- getSymbols(stock, src = "yahoo",
from = startDate,
to = endDate,
auto.assign = FALSE)
}
stockReturn <- list()
for (stock in stocks) {
tempReturns <- Delt(Ad(stockData[[stock]]))
tempReturns[1, ] <- 0
stockReturn[[stock]] <- tempReturns
}
stockReturns <- do.call(merge, stockReturn)
head(stockReturns)
## Delt.1.arithmetic Delt.1.arithmetic.1 Delt.1.arithmetic.2
## 2023-01-03 0.000000000 0.000000000 0.000000000
## 2023-01-04 0.010314283 -0.043743227 0.058204185
## 2023-01-05 -0.010604730 -0.029637684 0.015527052
## 2023-01-06 0.036794126 0.011785399 0.009117706
## 2023-01-09 0.004088992 0.009736197 0.010147280
## 2023-01-10 0.004456326 0.007617200 0.035778142
## Delt.1.arithmetic.3 Delt.1.arithmetic.4 Delt.1.arithmetic.5
## 2023-01-03 0.000000000 0.000000000 0.000000000
## 2023-01-04 -0.002156975 -0.022044424 -0.001387312
## 2023-01-05 0.001196200 -0.009375697 -0.003802123
## 2023-01-06 -0.008028137 0.025372509 0.017541330
## 2023-01-09 -0.030111597 -0.049685684 -0.010170301
## 2023-01-10 0.007189739 -0.015912385 -0.026014578
## Delt.1.arithmetic.6 Delt.1.arithmetic.7 Delt.1.arithmetic.8
## 2023-01-03 0.000000000 0.000000000 0.0000000000
## 2023-01-04 -0.007923565 0.051248853 0.0205790589
## 2023-01-05 -0.023725589 -0.029039098 -0.0053927868
## 2023-01-06 0.035611152 0.024651090 0.0089579737
## 2023-01-09 0.014869874 0.059349011 -0.0095793148
## 2023-01-10 0.028731709 -0.007681374 -0.0007076527
## Delt.1.arithmetic.9
## 2023-01-03 0.000000000
## 2023-01-04 0.013847464
## 2023-01-05 -0.028618845
## 2023-01-06 0.052421341
## 2023-01-09 0.020311699
## 2023-01-10 0.005697328
Equally Weighted Portfolio Returns (re-running this chunk using the new data)
prices <- do.call(merge, lapply(stockData, Ad))
returns <- Return.calculate(prices)
returns[is.na(returns)] <- 0
numStocks <- length(stockData)
weights <- rep(1/numStocks, numStocks)
portfolioReturns <- Return.portfolio(R = returns, weights = weights)
tail(portfolioReturns)
## portfolio.returns
## 2023-10-23 0.0031114703
## 2023-10-24 0.0122825494
## 2023-10-25 -0.0098386475
## 2023-10-26 -0.0174554965
## 2023-10-27 0.0003512552
## 2023-10-30 0.0122524775
Monte Carlo Optimization Returns
(get weights from function return)
mc_weights <- c(0.21859, 0.08281, 0.188986, 0.01652, 0.000796, 0.19554, 0.098302, 0.108662, 0.036544, 0.053249)
mc_portfolioReturns <- Return.portfolio(R = returns, weights = mc_weights)
## Warning in Return.portfolio.geometric(R = R, weights = weights, wealth.index =
## wealth.index, : The weights for one or more periods do not sum up to 1:
## assuming a return of 0 for the residual weights
tail(mc_portfolioReturns)
## portfolio.returns
## 2023-10-23 0.0043549297
## 2023-10-24 0.0193693869
## 2023-10-25 -0.0136348172
## 2023-10-26 -0.0228415861
## 2023-10-27 0.0002376274
## 2023-10-30 0.0150845036
CAPM Optimization Returns
capm_weights <- c(0.0000000000000000000000000, -0.0000000000000000001094735, 0.0174175751737773581995228, 0.2708490891210338369354815, 0.2682027762553574334880579, 0.2935597422412994927931607, 0.1279711440899859720143894, 0.0021415150238913334412860, 0.0198581580946546529253816, -0.0000000000000000160630695)
capm_portfolioReturns <- Return.portfolio(R = returns, weights = capm_weights)
tail(capm_portfolioReturns)
## portfolio.returns
## 2023-10-23 0.007082206
## 2023-10-24 0.001191459
## 2023-10-25 -0.005836649
## 2023-10-26 -0.010727712
## 2023-10-27 -0.003255237
## 2023-10-30 0.023326329
Markowitz Optimization
marko_weights <- c(0.0000000000000000000000000, -0.0000000000000000001094735, 0.0174175751737773581995228, 0.2708490891210338369354815, 0.2682027762553574334880579, 0.2935597422412994927931607,
0.1279711440899859720143894, 0.0021415150238913334412860, 0.0198581580946546529253816, -0.0000000000000000160630695)
marko_portfolioReturns <- Return.portfolio(R = returns, weights = marko_weights)
tail(marko_portfolioReturns)
## portfolio.returns
## 2023-10-23 0.007082206
## 2023-10-24 0.001191459
## 2023-10-25 -0.005836649
## 2023-10-26 -0.010727712
## 2023-10-27 -0.003255237
## 2023-10-30 0.023326329
Plotting the performance of each portfolio:
portfolioReturns_df <- as.data.frame(portfolioReturns)
portfolioReturns_tibble <- as_tibble(portfolioReturns_df, rownames = "Date")
mc_portfolioReturns_df <- as.data.frame(mc_portfolioReturns)
mc_portfolioReturns_tibble <- as_tibble(mc_portfolioReturns_df, rownames = "Date")
capm_portfolioReturns_df <- as.data.frame(capm_portfolioReturns)
capm_portfolioReturns_tibble <- as_tibble(capm_portfolioReturns_df, rownames = "Date")
marko_portfolioReturns_df <- as.data.frame(marko_portfolioReturns)
marko_portfolioReturns_tibble <- as_tibble(marko_portfolioReturns_df, rownames = "Date")
portfolioReturns_tibble <- portfolioReturns_tibble %>% mutate(Portfolio = "Portfolio 1") %>% rename(Returns = portfolio.returns)
mc_portfolioReturns_tibble <- mc_portfolioReturns_tibble %>% mutate(Portfolio = "MC Portfolio") %>% rename(Returns = portfolio.returns)
capm_portfolioReturns_tibble <- capm_portfolioReturns_tibble %>% mutate(Portfolio = "CAPM Portfolio") %>% rename(Returns = portfolio.returns)
marko_portfolioReturns_tibble <- marko_portfolioReturns_tibble %>% mutate(Portfolio = "Markowitz Portfolio") %>% rename(Returns = portfolio.returns)
combinedDf <- bind_rows(
portfolioReturns_tibble,
mc_portfolioReturns_tibble,
capm_portfolioReturns_tibble,
marko_portfolioReturns_tibble
)
# Define a color palette
color_palette <- c("Portfolio 1" = "blue",
"MC Portfolio" = "green",
"CAPM Portfolio" = "red",
"Markowitz Portfolio" = "purple")
ggplot(combinedDf, aes(x = as.Date(Date), y = Returns, color = Portfolio)) +
geom_line() +
labs(title = "Comparison of Portfolio Performance", x = "Date", y = "Returns") +
theme_minimal()
Calculate and graph the variance of each portfolio:
variance1 <- var(portfolioReturns, na.rm = TRUE)
variance2 <- var(mc_portfolioReturns, na.rm = TRUE)
variance3 <- var(capm_portfolioReturns, na.rm = TRUE)
variance4 <- var(marko_portfolioReturns, na.rm = TRUE)
variance_data <- data.frame(
Portfolio = c("Equally Weighted", "Monte Carlo", "CAPM", "Markowitz"),
Variance = c(variance1, variance2, variance3, variance4)
)
print(paste("Variance for Portfolio 1:", variance1))
## [1] "Variance for Portfolio 1: 0.000128153727390074"
print(paste("Variance for Portfolio 2:", variance2))
## [1] "Variance for Portfolio 2: 0.000144182771129367"
print(paste("Variance for Portfolio 3:", variance3))
## [1] "Variance for Portfolio 3: 9.25578927399404e-05"
print(paste("Variance for Portfolio 4:", variance4))
## [1] "Variance for Portfolio 4: 9.25578927399404e-05"
variance_data$Variance <- variance_data$Variance * 100
ggplot(variance_data, aes(x = Portfolio, y = Variance, fill = Portfolio)) +
geom_bar(stat = "identity") +
scale_y_continuous(labels = scales::percent_format(scale = 1)) +
labs(title = "Variance of Portfolios as Percentage",
x = "Portfolio",
y = "Variance (%)") +
theme_minimal() +
theme(legend.position = "none")
Calculate Beta of each portfolio for comparison (regression approach)
First, pull in the new market data:
sp500Data <- getSymbols("^GSPC", src = "yahoo", from = startDate, to = endDate, auto.assign = FALSE)
if (is.null(sp500Data)) {
stop("Failed to retrieve S&P 500 data")
} else {
sp500Close <- Ad(sp500Data)
marketReturn <- Return.calculate(sp500Close)
marketReturn[1,] <- 0
head(marketReturn)
}
## GSPC.Adjusted
## 2023-01-03 0.0000000000
## 2023-01-04 0.0075389706
## 2023-01-05 -0.0116455289
## 2023-01-06 0.0228407810
## 2023-01-09 -0.0007676325
## 2023-01-10 0.0069782332
calculate_beta1 <- function(portfolioReturns, marketReturn) {
model <- lm(portfolioReturns ~ marketReturn)
return(coef(model)[2])
}
calculate_beta2 <- function(mc_portfolioReturns, marketReturn) {
model <- lm(mc_portfolioReturns ~ marketReturn)
return(coef(model)[2])
}
calculate_beta3 <- function(capm_portfolioReturns, marketReturn) {
model <- lm(capm_portfolioReturns ~ marketReturn)
return(coef(model)[2])
}
calculate_beta4 <- function(marko_portfolioReturns, marketReturn) {
model <- lm(marko_portfolioReturns ~ marketReturn)
return(coef(model)[2])
}
beta1 <- calculate_beta1(portfolioReturns, marketReturn)
beta2 <- calculate_beta2(mc_portfolioReturns, marketReturn)
beta3 <- calculate_beta3(capm_portfolioReturns, marketReturn)
beta4 <- calculate_beta4(marko_portfolioReturns, marketReturn)
print(paste("Beta for Portfolio 1:", beta1))
## [1] "Beta for Portfolio 1: 1.17609654011679"
print(paste("Beta for Portfolio 2:", beta2))
## [1] "Beta for Portfolio 2: 1.20002537294762"
print(paste("Beta for Portfolio 3:", beta3))
## [1] "Beta for Portfolio 3: 0.659289276710059"
print(paste("Beta for Portfolio 4:", beta4))
## [1] "Beta for Portfolio 4: 0.659289276710059"
Calculate each portfolio’s Alpha value:
(commented out until I figure this piece out) calculate_alpha1 <- function(portfolioReturns, beta1, marketReturn, riskFreeRate) { averagePortfolioReturn <- mean(portfolioReturns) averageMarketReturn <- mean(marketReturn)
expectedReturn <- riskFreeRate + beta1 * (averageMarketReturn - riskFreeRate)
alpha <- averagePortfolioReturn - expectedReturn
return(alpha)
}
calculate_alpha2 <- function(mc_portfolioReturns, beta2, marketReturn, riskFreeRate) { expectedReturn <- riskFreeRate + beta2 * (marketReturn - riskFreeRate) alpha <- mean(mc_portfolioReturns) - expectedReturn return(alpha) }
calculate_alpha3 <- function(capm_portfolioReturns, beta3, marketReturn, riskFreeRate) { expectedReturn <- riskFreeRate + beta3 * (marketReturn - riskFreeRate) alpha <- mean(capm_portfolioReturns) - expectedReturn return(alpha) }
calculate_alpha4 <- function(marko_portfolioReturns, beta4, marketReturn, riskFreeRate) { expectedReturn <- riskFreeRate + beta4 * (marketReturn - riskFreeRate) alpha <- mean(marko_portfolioReturns) - expectedReturn return(alpha) }
alpha1 <- calculate_alpha1(portfolioReturns, beta1, marketReturn, riskFreeRate) alpha2 <- calculate_alpha2(mc_portfolioReturns, beta2, marketReturn, riskFreeRate) alpha3 <- calculate_alpha3(capm_portfolioReturns, beta3, marketReturn, riskFreeRate) alpha4 <- calculate_alpha4(marko_portfolioReturns, beta4, marketReturn, riskFreeRate)
print(paste(“Alpha for Portfolio 1:”, alpha1)) print(paste(“Alpha for MC Portfolio:”, alpha2)) print(paste(“Alpha for CAPM Portfolio:”, alpha3)) print(paste(“Alpha for Markowitz Portfolio:”, alpha4))
alpha_data <- data.frame( Portfolio = c(“Equally Weighted”, “Monte Carlo”, “CAPM”, “Markowitz”), Alpha = c(alpha1, alpha2, alpha3, alpha4) )
print(names(alpha_data)) print(alpha1)
print(alpha_data)
ggplot(alpha_data, aes(x = Portfolio, y = Alpha)) + geom_bar(stat = “identity”)
Calculating the total return for each portfolio:
```r
investment <- 100000
totalReturn1 <- cumprod(1 + portfolioReturns) - 1
totalReturn2 <- cumprod(1 + mc_portfolioReturns) - 1
totalReturn3 <- cumprod(1 + capm_portfolioReturns) - 1
totalReturn4 <- cumprod(1 + marko_portfolioReturns) - 1
finalReturn1 <- investment * (1 + as.numeric(last(totalReturn1)))
finalReturn2 <- investment * (1 + as.numeric(last(totalReturn2)))
finalReturn3 <- investment * (1 + as.numeric(last(totalReturn3)))
finalReturn4 <- investment * (1 + as.numeric(last(totalReturn4)))
returns_data <- data.frame(
Portfolio = c("Equally Weighted", "Monte Carlo", "CAPM", "Markowitz"),
FinalValue = c(finalReturn1, finalReturn2, finalReturn3, finalReturn4)
)
print(finalReturn1)
## [1] 126632.8
print(finalReturn2)
## [1] 146053.1
print(finalReturn3)
## [1] 109660.1
print(finalReturn4)
## [1] 109660.1
ggplot(returns_data, aes(x = Portfolio, y = FinalValue, fill = Portfolio)) +
geom_bar(stat = "identity") +
labs(title = "Final Monetary Value of Portfolios",
x = "Portfolio",
y = "Final Value (USD)") +
theme_minimal() +
theme(legend.position = "none") +
scale_y_continuous(labels = scales::dollar_format())