Stock used for analysis:SPY,EFA, IJS, EEM,AGG

The assumption for this case study:

1. Stocks weight in the portfolio is the same for every enterprise.

2. Analysis based on the historical adjusted closing price data from 1st January 2013 to 31st December 2017.

3. Portfolio rebalancing was monthly

#Import Library

library(PerformanceAnalytics)
## Loading required package: xts
## Loading required package: zoo
## 
## Attaching package: 'zoo'
## The following objects are masked from 'package:base':
## 
##     as.Date, as.Date.numeric
## 
## Attaching package: 'PerformanceAnalytics'
## The following object is masked from 'package:graphics':
## 
##     legend
library(quantmod)
## Loading required package: TTR
## Registered S3 method overwritten by 'quantmod':
##   method            from
##   as.zoo.data.frame zoo
library(ggplot2)
library(xts)
library(zoo)
library(dplyr)
## 
## 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(PortfolioAnalytics)
## Loading required package: foreach
library(tidyquant)
## Loading required package: lubridate
## 
## Attaching package: 'lubridate'
## The following objects are masked from 'package:base':
## 
##     date, intersect, setdiff, union
## ══ Need to Learn tidyquant? ════════════════════════════════════════════════════
## Business Science offers a 1-hour course - Learning Lab #9: Performance Analysis & Portfolio Optimization with tidyquant!
## </> Learn more at: https://university.business-science.io/p/learning-labs-pro </>
library(tidyverse)
## ── Attaching packages ─────────────────────────────────────── tidyverse 1.3.1 ──
## ✓ tibble  3.1.6     ✓ purrr   0.3.4
## ✓ tidyr   1.1.4     ✓ stringr 1.4.0
## ✓ readr   2.1.1     ✓ forcats 0.5.1
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## x purrr::accumulate()      masks foreach::accumulate()
## x lubridate::as.difftime() masks base::as.difftime()
## x lubridate::date()        masks base::date()
## x dplyr::filter()          masks stats::filter()
## x dplyr::first()           masks xts::first()
## x lubridate::intersect()   masks base::intersect()
## x dplyr::lag()             masks stats::lag()
## x dplyr::last()            masks xts::last()
## x lubridate::setdiff()     masks base::setdiff()
## x lubridate::union()       masks base::union()
## x purrr::when()            masks foreach::when()
library(timetk)
library(tibbletime)
## 
## Attaching package: 'tibbletime'
## The following object is masked from 'package:stats':
## 
##     filter
library(broom)
library(dygraphs)

symbols <- c("SPY","EFA", "IJS", "EEM","AGG")

Obtain stock price data from Yahoo! Finance

prices <- 
  getSymbols(symbols, src = 'yahoo', 
             from = "2013-01-01",
             to = "2017-12-31",
             auto.assign = TRUE, warnings = FALSE) %>% 
  map(~Ad(get(.))) %>%
  reduce(merge) %>% 
  `colnames<-`(symbols)
## 'getSymbols' currently uses auto.assign=TRUE by default, but will
## use auto.assign=FALSE in 0.5-0. You will still be able to use
## 'loadSymbols' to automatically load data. getOption("getSymbols.env")
## and getOption("getSymbols.auto.assign") will still be checked for
## alternate defaults.
## 
## This message is shown once per session and may be disabled by setting 
## options("getSymbols.warning4.0"=FALSE). See ?getSymbols for details.
prices_monthly <- to.monthly(prices, indexAt = "last", OHLC = FALSE)

Create the variable returns

asset_returns_xts <- na.omit(Return.calculate(prices_monthly, method = "log"))

Assign weights

w <-  rep(1/length(symbols), length(symbols))

Create a portfolio and rebalance monthly

portfolio_returns_xts_rebalanced_monthly <- 
  Return.portfolio(asset_returns_xts, weights = w, rebalance_on = "months") %>%
  `colnames<-`("returns") 

asset_returns_long <-  
  prices %>% 
  to.monthly(indexAt = "last", OHLC = FALSE) %>% 
  tk_tbl(preserve_index = TRUE, rename_index = "date") %>%
  gather(asset, returns, -date) %>% 
  group_by(asset) %>%  
  mutate(returns = (log(returns) - log(lag(returns)))) %>% 
  na.omit()

portfolio_returns_tq_rebalanced_monthly <- 
  asset_returns_long %>%
  tq_portfolio(assets_col  = asset, 
               returns_col = returns,
               weights     = w,
               col_rename  = "returns",
               rebalance_on = "months")

We choose to use SPY ETF as a proxy for the market return, effectively treating the S&P 500 as the market.

Let’s calculate our market return for SPY and save it as market_return_xts.

spy_monthly_xts <- 
  getSymbols("SPY", 
             src = 'yahoo', 
             from = "2013-01-01", 
             to = "2017-12-31",
             auto.assign = TRUE, 
             warnings = FALSE) %>% 
  map(~Ad(get(.))) %>% 
  reduce(merge) %>%
  `colnames<-`("SPY") %>% 
  to.monthly(indexAt = "last", OHLC = FALSE)

market_returns_xts <-
  Return.calculate(spy_monthly_xts, method = "log") %>% 
  na.omit()

We will also want a data.frame object of market returns

market_returns_tidy <-
  market_returns_xts %>% 
  tk_tbl(preserve_index = TRUE, rename_index = "date") %>% 
  na.omit() %>%
  select(date, returns = SPY)

head(market_returns_tidy)

We make sure it’s periodicity aligns perfectly with our portfolio returns periodicity

portfolio_returns_tq_rebalanced_monthly %>% 
  mutate(market_returns = market_returns_tidy$returns) %>%
  head()

Calculating CAPM Beta

cov(portfolio_returns_xts_rebalanced_monthly,market_returns_tidy$returns)/var(market_returns_tidy$returns)
##              [,1]
## returns 0.8027526

The CAPM beta is obatined as 0.8031055

Merge all the data and rename columns

returns <- merge.xts(asset_returns_xts$EFA, asset_returns_xts$IJS, asset_returns_xts$EEM, asset_returns_xts$AGG, asset_returns_xts$SPY)
colnames(returns) <- c("EFA", "IJS", "EEM", "AGG", "SP500")

Produce interactive chart of stock returns

dygraph(returns, main = "Developed-market securities in Europe vs. Small-Cap 600 vs. Emerging Markets vs. Aggregate Bond vs.  S&P 500") %>%
  dyAxis("y", label = "Return", valueRange = c(-1,0.5)) %>%
  dyRangeSelector(dateWindow = c("2013-01-01", "2017-12-31")) %>%
  dyOptions(colors = RColorBrewer::brewer.pal(5, "Set2")) 

From our returns data set, we can get a sense of how well each stock has performed relative to the S&P 500 over the last several years.

In 2017, the EFA only recorded a loss of about 0.04% in August, while IJS recorded a loss of 2.76% in August and 0.37% in December.

EMM recorded a loss of 0.04 in September while AGG had losses of 0.57% and 0.15% in September and November respectively.

We take a look at the correlation between the stocks

corrplot::corrplot(cor(returns), method = 'number')

From the result,it is adviseable to select stocks with low correlations to each other. You wouldn’t want all the stocks in your portfolio to always rise and fall together — that could expose you to excess volatility that you may want to avoid.

From our correlation matrix, we observe that all of our stocks are positively correlated, albeit in varying degrees except IJS and AGG with negative correlation (-0.22). EFA is only weakly correlated with AGG (0.17), but EFA and the S&P 500 have a high correlation (0.78) with each other.

Importantly, our stocks have a fairly high positive correlation with the market, meaning they tend to move with the market most months.

Building our portfolio and assesing performance

Assign weights

wts <- c(1/3, 1/3, 1/3)

Construct a portfolio using our returns object and weights

Only select first three columns to isolate our individual stock data

portfolio_returns <- Return.portfolio(R = returns[,1:3], weights = wts, wealth.index = TRUE)

Then isolate our S&P 500 data

benchmark_returns <- Return.portfolio(R = returns[,4], wealth.index = TRUE)

Merge the two

comp <- merge.xts(portfolio_returns, benchmark_returns)
colnames(comp) <- c("Portfolio", "Benchmark")

Build an interactive graph to compare performance

dygraph(comp, main = "Portfolio Performance vs. Benchmark") %>%
  dyAxis("y", label = "Amount ($)")

This shows that if we had invested $1 into our Developed-market securities in Europe Cap-600-Emerging Markets-Aggregate Bond portfolio at the beginning of 2012 and didn’t touch it (i.e., no re-balancing), our portfolio’s value would have increased by 45.5%. This beat the performance of the S&P 500, which still yielded a return of about 9.9% over the same time horizon.