stocks <- c("META", "TSLA", "GOOGL", "PEP", "AMZN")
getSymbols(stocks, from = "2014-01-01", to = "2023-12-29", src = "yahoo")
## [1] "META" "TSLA" "GOOGL" "PEP" "AMZN"
prices <- do.call(cbind, lapply(stocks, function(stock) Ad(get(stock))))
colnames(prices) <- stocks
returns <- na.omit(Return.calculate(prices))
mu.vec <- colMeans(returns)
sigma.mat <- cov(returns)
#Equal Weighted Portfolio
ew <- rep(1/5,5)
equalWeight.portfolio <- getPortfolio(er = mu.vec, cov.mat = sigma.mat, weights = ew)
plot(equalWeight.portfolio, col="red")
average_return <- mean(mu.vec)
portfolio_return <- sum(ew * mu.vec)
# Efficient Frontier
ef <- efficient.frontier(mu.vec,sigma.mat, nport = 50)
plot(ef$sd, ef$er, type='l', col='violet', xlab="Standard Deviation (Risk)", ylab="Expected Return", main="Efficient Frontier")
points(sqrt(diag(sigma.mat)), mu.vec, pch=19, col='black')
#### 3. Making protfolio
r.f <- 0.0005
# Tangency Portfolio
ef <- efficient.frontier(mu.vec, sigma.mat, alpha.min=-0.5, alpha.max=2, nport=50)
tan.port <- tangency.portfolio(mu.vec, sigma.mat, r.f)
# Efficient Frontier
plot(ef$sd, ef$er, type='l', col='violet', xlab="Standard Deviation", ylab="Expected Return", main="Efficient Frontier")
points(sqrt(diag(sigma.mat)), mu.vec, pch=19, col='black', labels=names(mu.vec))
points(tan.port$sd, tan.port$er, col="black", pch=16, cex=2)
text(tan.port$sd, tan.port$er, labels="Tangency", pos=3)
sr.tan <- (tan.port$er - r.f) / tan.port$sd
abline(a=r.f, b=sr.tan, col="blue", lwd=2, lty=2)
legend("topright", legend=c("Efficient Frontier", "Individual Assets", "Tangency Portfolio", "CML"),
col=c("violet", "black", "black", "blue"), pch=c(NA, 19, 16, NA), lty=c(1, NA, NA, 2))
#### 4. Perdormance Analytics (annualized return & volatility)
# Using Performance Analytics
stock_data <- lapply(stocks, function(sym) {
getSymbols(sym, from = "2020-01-01", to = "2022-12-29", auto.assign = FALSE)
})
prices <- lapply(stock_data, Ad)
all_prices <- do.call(merge, prices)
names(all_prices) <- stocks
head(all_prices)
## META TSLA GOOGL PEP AMZN
## 2020-01-02 209.5576 28.68400 68.4340 120.4818 94.9005
## 2020-01-03 208.4488 29.53400 68.0760 120.3132 93.7485
## 2020-01-06 212.3747 30.10267 69.8905 120.7745 95.1440
## 2020-01-07 212.8342 31.27067 69.7555 118.8762 95.3430
## 2020-01-08 214.9919 32.80933 70.2520 119.4883 94.5985
## 2020-01-09 218.0686 32.08933 70.9895 119.5681 95.0525
# Calculate returns
returns <- na.omit(Return.calculate(all_prices))
equal_weights <- rep(1/5,5)
pf_bh <- Return.portfolio(R = returns, weights = equal_weights, verbose = TRUE)
pf_rebal <- Return.portfolio(R = returns, weights = equal_weights, rebalance_on = "months", verbose = TRUE)
par(mfrow = c(2, 1), mar = c(2, 4, 2, 2))
plot.zoo(pf_bh$returns, main = "Buy and Hold Portfolio Returns")
plot.zoo(pf_rebal$returns, main = "Monthly Rebalanced Portfolio Returns")
eop_weight_bh <- pf_bh$EOP.Weight
eop_weight_rebal <- pf_rebal$EOP.Weight
par(mfrow = c(2, 1), mar = c(2, 4, 2, 2))
plot.zoo(eop_weight_bh$AAPL, main = "End of Period Weights - Buy and Hold (META)")
## Error in plot.window(...): need finite 'xlim' values
plot.zoo(eop_weight_rebal$AAPL, main = "End of Period Weights - Monthly Rebalanced (META)")
## Error in plot.window(...): need finite 'xlim' values
# Fetch price
sp500 <- getSymbols("^GSPC", from = "2014-01-01", to = "2023-12-29", src = "yahoo", auto.assign = FALSE)
head(sp500)
## GSPC.Open GSPC.High GSPC.Low GSPC.Close GSPC.Volume GSPC.Adjusted
## 2014-01-02 1845.86 1845.86 1827.74 1831.98 3080600000 1831.98
## 2014-01-03 1833.21 1838.24 1829.13 1831.37 2774270000 1831.37
## 2014-01-06 1832.31 1837.16 1823.73 1826.77 3294850000 1826.77
## 2014-01-07 1828.71 1840.10 1828.71 1837.88 3511750000 1837.88
## 2014-01-08 1837.90 1840.02 1831.40 1837.49 3652140000 1837.49
## 2014-01-09 1839.00 1843.23 1830.38 1838.13 3581150000 1838.13
# the daily frequency cpnverted sp500 to monthly frequency
sp500_monthly <- to.monthly(sp500)
# Print the first six rows of sp500_monthly
head(sp500_monthly)
## sp500.Open sp500.High sp500.Low sp500.Close sp500.Volume
## Jan 2014 1845.86 1850.84 1770.45 1782.59 75871910000
## Feb 2014 1782.68 1867.92 1737.92 1859.45 69725590000
## Mar 2014 1857.68 1883.97 1834.44 1872.34 71885030000
## Apr 2014 1873.96 1897.28 1814.36 1883.95 71595810000
## May 2014 1884.39 1924.03 1859.79 1923.57 63623630000
## Jun 2014 1923.87 1968.17 1915.98 1960.23 63283380000
## sp500.Adjusted
## Jan 2014 1782.59
## Feb 2014 1859.45
## Mar 2014 1872.34
## Apr 2014 1883.95
## May 2014 1923.57
## Jun 2014 1960.23
# Create sp500_returns using Return also closing price
sp500_returns <- Return.calculate(Cl(sp500_monthly))
# Time series plot
plot.ts(sp500_returns)
# year x month
table.CalendarReturns(sp500_returns)
## Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec sp500.Close
## 2014 NA 4.3 0.7 0.6 2.1 1.9 -1.5 3.8 -1.6 2.3 2.5 -0.4 15.5
## 2015 -3.1 5.5 -1.7 0.9 1.0 -2.1 2.0 -6.3 -2.6 8.3 0.1 -1.8 -0.7
## 2016 -5.1 -0.4 6.6 0.3 1.5 0.1 3.6 -0.1 -0.1 -1.9 3.4 1.8 9.5
## 2017 1.8 3.7 0.0 0.9 1.2 0.5 1.9 0.1 1.9 2.2 2.8 1.0 19.4
## 2018 5.6 -3.9 -2.7 0.3 2.2 0.5 3.6 3.0 0.4 -6.9 1.8 -9.2 -6.2
## 2019 7.9 3.0 1.8 3.9 -6.6 6.9 1.3 -1.8 1.7 2.0 3.4 2.9 28.9
## 2020 -0.2 -8.4 -12.5 12.7 4.5 1.8 5.5 7.0 -3.9 -2.8 10.8 3.7 16.3
## 2021 -1.1 2.6 4.2 5.2 0.5 2.2 2.3 2.9 -4.8 6.9 -0.8 4.4 26.9
## 2022 -5.3 -3.1 3.6 -8.8 0.0 -8.4 9.1 -4.2 -9.3 8.0 5.4 -5.9 -19.4
## 2023 6.2 -2.6 3.5 1.5 0.2 6.5 3.1 -1.8 -4.9 -2.2 8.9 4.7 24.6
# Remove first value because it is NA
sp500_returns <- sp500_returns[(-1),]
# the mean monthly returns
mean(sp500_returns)
## [1] 0.009284533
# geometric mean of monthly returns
mean.geometric(sp500_returns)
## sp500.Close
## Geometric Mean 0.008329235
# standard deviation
sd(sp500_returns)
## [1] 0.04380469
# Annualized mean and volatility
table.AnnualizedReturns(sp500_returns)
## sp500.Close
## Annualized Return 0.1047
## Annualized Std Dev 0.1517
## Annualized Sharpe (Rf=0%) 0.6897
# Plotting the 12-month rolling annualized mean
chart.RollingPerformance(R = sp500_returns, width = 12, FUN = "Return.annualized")
# Plotting the 12-month rolling annualized standard deviation
chart.RollingPerformance(R = sp500_returns, width = 12, FUN = "StdDev.annualized")
# Plotting the 12-month rolling annualized Sharpe ratio, assuming rf = 0
chart.RollingPerformance(R = sp500_returns, width = 12, FUN = "SharpeRatio.annualized")
# Total:
charts.RollingPerformance(R = sp500_returns, width = 12)
# To analyze specific date range
# Use daily return
sp500_ret_daily <- Return.calculate(Cl(sp500))
# Create specific years
sp500_2014 <- window(sp500_ret_daily, start = "2014-01-01", end = "2014-12-29")
sp500_2023 <- window(sp500_ret_daily, start = "2023-01-01", end = "2023-12-29")
# Plotting settings
par(mfrow = c(1, 2), mar = c(3, 2, 2, 2))
names(sp500_2014) <- "sp500_2014"
names(sp500_2023) <- "sp500_2023"
## Compare Density Chart with Normal Chart
# Plotting histogram of 2014
chart.Histogram(sp500_2014, methods = c("add.density", "add.normal"))
# Plotting histogram of 2023
chart.Histogram(sp500_2023, methods = c("add.density", "add.normal"))
## Both skewness and kurtosis
skewness(sp500_ret_daily)
## [1] -0.5228842
kurtosis(sp500_ret_daily)
## [1] 14.86335
# Semi-deviation, Value-at-Risk and 5% Expected Shortfall
SemiDeviation(sp500_returns)
## sp500.Close
## Semi-Deviation 0.03255194
VaR(sp500_returns, p = 0.05)
## sp500.Close
## VaR -0.06637531
ES(sp500_returns, p = 0.05)
## sp500.Close
## ES -0.0928014
# Analyzing the drawdowns
table.Drawdowns(sp500_ret_daily)
## From Trough To Depth Length To Trough Recovery
## 1 2020-02-20 2020-03-23 2020-08-18 -0.3392 126 23 103
## 2 2022-01-04 2022-10-12 <NA> -0.2543 500 195 NA
## 3 2018-09-21 2018-12-24 2019-04-23 -0.1978 146 65 81
## 4 2015-05-22 2016-02-11 2016-07-11 -0.1416 286 183 103
## 5 2018-01-29 2018-02-08 2018-08-24 -0.1016 146 9 137
par(mfrow=c(1,1))
chart.Drawdown(sp500_ret_daily)
meta <- getSymbols("META", from = "2014-01-01", to = "2023-12-29", auto.assign = FALSE)
tsla <- getSymbols("TSLA", from = "2014-01-01", to = "2023-12-29", auto.assign = FALSE)
# Adjusting closing prices
meta_ad <- Ad(meta)
tsla_ad <- Ad(tsla)
# Calculate returns
meta_ret <- Return.calculate(meta_ad)
tsla_ret <- Return.calculate(tsla_ad)
# Remove the first row
meta_ret <- meta_ret[-1,]
tsla_ret <- tsla_ret[-1,]
# scatter plot of returns
chart.Scatter(meta_ret, tsla_ret, xlab = "META returns", ylab = "TSLA returns", main = "META-TSLA returns")
# correlation
meta_tsla_cor <- cor(meta_ret, tsla_ret)
print(meta_tsla_cor)
## TSLA.Adjusted
## META.Adjusted 0.3497284
# Find and visualize the correlation
chart.Correlation(cbind(meta_ret, tsla_ret))
# Visualize the rolling estimates
par(mfrow = c(2, 1))
chart.RollingCorrelation(meta_ret, meta_ret, width = 20)
chart.RollingCorrelation(tsla_ret, tsla_ret, width = 251)
# Plot parameters
par(mfrow = c(1, 1))
# Load data for specified stocks
meta <- getSymbols("META", from = "2004-01-01", to = "2014-10-29", auto.assign = FALSE)
tsla <- getSymbols("TSLA", from = "2004-01-01", to = "2014-10-29", auto.assign = FALSE)
googl <- getSymbols("GOOGL", from = "2004-01-01", to = "2014-10-29", auto.assign = FALSE)
pep <- getSymbols("PEP", from = "2004-01-01", to = "2014-10-29", auto.assign = FALSE)
amzn <- getSymbols("AMZN", from = "2004-01-01", to = "2014-10-29", auto.assign = FALSE)
# Calculate returns
ret_meta <- Return.calculate(Ad(meta))
ret_tsla <- Return.calculate(Ad(tsla))
ret_googl <- Return.calculate(Ad(googl))
ret_pep <- Return.calculate(Ad(pep))
ret_amzn <- Return.calculate(Ad(amzn))
# Reduce NA in the first row
ret_meta <- ret_meta[-1,]
ret_tsla <- ret_tsla[-1,]
ret_googl <- ret_googl[-1,]
ret_pep <- ret_pep[-1,]
ret_amzn <- ret_amzn[-1,]
# Combination returns into a matrix
return_multi <- cbind(ret_meta, ret_tsla, ret_googl, ret_pep, ret_amzn)
# Means and standard deviations of returns
means <- colMeans(return_multi)
sds <- apply(return_multi, 2, sd)
# Ensure the plot is executed in one block without interruptions
plot(sds, means, pch = 19, col = "violet", xlab = "Standard Deviation", ylab = "Mean Return")
text(sds, means, labels = colnames(return_multi), pos = 3, cex = 0.8)
abline(h = 0.0006, col = "black", lty = 2) # Horizontal reference line
# Create a matrix with variances on the diagonal
diag_cov <- diag(sds^2)
# Compute the covariance and correlation matrices
cov_matrix <- cov(return_multi)
cor_matrix <- cor(return_multi)
# Portfolio analysis
weights <- c(0.2, 0.2, 0.2, 0.2, 0.2) # Equal weighting for simplicity
# Volatility budget
if(any(is.na(return_multi))) {
return_multi <- na.omit(return_multi)
}
vol_budget <- StdDev(return_multi, portfolio_method = "component", weights = weights)
weights_percrisk <- cbind(weights, vol_budget$pct_contrib_StdDev)
colnames(weights_percrisk) <- c("weights", "perc vol contrib")
weights_percrisk
## weights perc vol contrib
## META.Adjusted 0.2 0.29091575
## TSLA.Adjusted 0.2 0.38594202
## GOOGL.Adjusted 0.2 0.11769157
## PEP.Adjusted 0.2 0.03530626
## AMZN.Adjusted 0.2 0.17014439
# Portfolio specification
port_spec <- portfolio.spec(assets = colnames(return_multi))
# Investment constraint such that the weights sum to 1
port_spec <- add.constraint(portfolio = port_spec, type = "full_investment")
# Long only constraint such that each weight is between 0 and 1
port_spec <- add.constraint(portfolio = port_spec, type = "long_only")
# An objective to minimize portfolio standard deviation
port_spec <- add.objective(portfolio = port_spec, type = "risk", name = "StdDev")
# Optimize the portfolio
opt <- optimize.portfolio(return_multi, portfolio = port_spec, optimize_method = "ROI")
## Error in gmv_opt(R = R, constraints = constraints, moments = moments, : "package:ROI" %in% search() || requireNamespace("ROI", quietly = TRUE) is not TRUE
# Print the results
opt
## Error in eval(expr, envir, enclos): object 'opt' not found
# The optimal weights
optimal_weights <- extractWeights(opt)
## Error in eval(expr, envir, enclos): object 'opt' not found
chart.Weights(opt)
## Error in eval(expr, envir, enclos): object 'opt' not found
# More specific optimization with return and risk
port_spec <- add.objective(portfolio = port_spec, type = "return", name = "mean")
port_spec <- add.objective(portfolio = port_spec, type = "risk", name = "var", risk_aversion = 10)
opt1 <- optimize.portfolio(return_multi, portfolio = port_spec, optimize_method = "ROI")
## Error in gmv_opt(R = R, constraints = constraints, moments = moments, : "package:ROI" %in% search() || requireNamespace("ROI", quietly = TRUE) is not TRUE
extractWeights(opt1)
## Error in eval(expr, envir, enclos): object 'opt1' not found
chart.Weights(opt1)
## Error in eval(expr, envir, enclos): object 'opt1' not found
# Risk-budgeted portfolio
port_spec <- add.objective(portfolio = port_spec, type = "risk_budget", name = "StdDev", min_prisk = 0.06, max_prisk = 0.1)
# Optimize the portfolio
opt2 <- optimize.portfolio.rebalancing(return_multi, portfolio = port_spec, optimize_method = "random", trace = F, search_size = 1500, rebalance_on = "quarters")
# Print the results and chart the optimal weights
extractWeights(opt2)
## META.Adjusted TSLA.Adjusted GOOGL.Adjusted PEP.Adjusted
## 2012-09-28 0.156 0.088 0.39 0.264
## 2012-12-31 0.156 0.088 0.39 0.264
## 2013-03-28 0.156 0.088 0.39 0.264
## 2013-06-28 0.156 0.088 0.39 0.264
## 2013-09-30 0.156 0.088 0.39 0.264
## 2013-12-31 0.156 0.088 0.39 0.264
## 2014-03-31 0.156 0.088 0.39 0.264
## 2014-06-30 0.156 0.088 0.39 0.264
## 2014-09-30 0.156 0.088 0.39 0.264
## 2014-10-28 0.156 0.088 0.39 0.264
## AMZN.Adjusted
## 2012-09-28 0.102
## 2012-12-31 0.102
## 2013-03-28 0.102
## 2013-06-28 0.102
## 2013-09-30 0.102
## 2013-12-31 0.102
## 2014-03-31 0.102
## 2014-06-30 0.102
## 2014-09-30 0.102
## 2014-10-28 0.102
chart.Weights(opt2)
# Objective Measures
objective_measures <- extractObjectiveMeasures(opt2)
objective_measures
## StdDev StdDev.contribution.META.Adjusted
## 2012-09-28 0.010375512 0.003770275
## 2012-12-31 0.010248360 0.003825761
## 2013-03-28 0.009992099 0.003462362
## 2013-06-28 0.010140212 0.002997028
## 2013-09-30 0.010073058 0.003312682
## 2013-12-31 0.010451981 0.003163557
## 2014-03-31 0.010879444 0.003242234
## 2014-06-30 0.011092788 0.003239132
## 2014-09-30 0.010942496 0.003127407
## 2014-10-28 0.010988883 0.003086022
## StdDev.contribution.TSLA.Adjusted StdDev.contribution.GOOGL.Adjusted
## 2012-09-28 0.001494549 0.003522491
## 2012-12-31 0.001189778 0.003810466
## 2013-03-28 0.001214610 0.003743446
## 2013-06-28 0.001638624 0.003763076
## 2013-09-30 0.001632816 0.003374152
## 2013-12-31 0.001628535 0.003886124
## 2014-03-31 0.001740340 0.003991670
## 2014-06-30 0.001744950 0.004174452
## 2014-09-30 0.001692996 0.004138115
## 2014-10-28 0.001713053 0.004191487
## StdDev.contribution.PEP.Adjusted StdDev.contribution.AMZN.Adjusted
## 2012-09-28 0.0006410906 0.0009471066
## 2012-12-31 0.0004926435 0.0009297124
## 2013-03-28 0.0006199558 0.0009517244
## 2013-06-28 0.0007865994 0.0009548841
## 2013-09-30 0.0008375544 0.0009158545
## 2013-12-31 0.0007938417 0.0009799232
## 2014-03-31 0.0008779235 0.0010272773
## 2014-06-30 0.0008048586 0.0011293954
## 2014-09-30 0.0008331649 0.0011508138
## 2014-10-28 0.0008352123 0.0011631088
## StdDev.pct_contrib_StdDev.META.Adjusted
## 2012-09-28 0.3633821
## 2012-12-31 0.3733046
## 2013-03-28 0.3465100
## 2013-06-28 0.2955587
## 2013-09-30 0.3288656
## 2013-12-31 0.3026754
## 2014-03-31 0.2980146
## 2014-06-30 0.2920034
## 2014-09-30 0.2858038
## 2014-10-28 0.2808313
## StdDev.pct_contrib_StdDev.TSLA.Adjusted
## 2012-09-28 0.1440458
## 2012-12-31 0.1160944
## 2013-03-28 0.1215571
## 2013-06-28 0.1615966
## 2013-09-30 0.1620973
## 2013-12-31 0.1558112
## 2014-03-31 0.1599659
## 2014-06-30 0.1573049
## 2014-09-30 0.1547175
## 2014-10-28 0.1558896
## StdDev.pct_contrib_StdDev.GOOGL.Adjusted
## 2012-09-28 0.3395004
## 2012-12-31 0.3718123
## 2013-03-28 0.3746406
## 2013-06-28 0.3711043
## 2013-09-30 0.3349679
## 2013-12-31 0.3718074
## 2014-03-31 0.3669001
## 2014-06-30 0.3763213
## 2014-09-30 0.3781692
## 2014-10-28 0.3814298
## StdDev.pct_contrib_StdDev.PEP.Adjusted
## 2012-09-28 0.06178881
## 2012-12-31 0.04807047
## 2013-03-28 0.06204460
## 2013-06-28 0.07757229
## 2013-09-30 0.08314798
## 2013-12-31 0.07595132
## 2014-03-31 0.08069562
## 2014-06-30 0.07255693
## 2014-09-30 0.07614030
## 2014-10-28 0.07600521
## StdDev.pct_contrib_StdDev.AMZN.Adjusted mean
## 2012-09-28 0.09128288 0.0006507189
## 2012-12-31 0.09071816 0.0005431544
## 2013-03-28 0.09524769 0.0008699676
## 2013-06-28 0.09416806 0.0012342458
## 2013-09-30 0.09092119 0.0015300810
## 2013-12-31 0.09375478 0.0016332639
## 2014-03-31 0.09442369 0.0015057469
## 2014-06-30 0.10181349 0.0014681109
## 2014-09-30 0.10516922 0.0013884396
## 2014-10-28 0.10584413 0.0013172142