Stock Data


Download

> library(tidyquant) # To download the data
> library(plotly) # To create interactive charts
> library(timetk) # To manipulate the data series
> library(tidyverse) # To organize the data

I am going to use the same stocks and time period that I used with Python previously:

https://rpubs.com/pjozefek/705567

> tickers <- c('AMZN', 'AAPL', 'CSCO', 'IBM')
> 
> stock_data <- tq_get(tickers,
+                      from = '2012-01-03',
+                      to = '2016-12-31',
+                      get = 'stock.prices')

Log Returns

Log returns, being the continuously compounded return, can be added across time. This simplifies the analysis.

> log_ret <- stock_data %>%
+   group_by(symbol) %>%
+   tq_transmute(select = adjusted,
+                mutate_fun = periodReturn,
+                period = 'daily',
+                col_rename = 'ret',
+                type = 'log')
> head(log_ret)
# A tibble: 6 x 3
# Groups:   symbol [1]
  symbol date             ret
  <chr>  <date>         <dbl>
1 AMZN   2012-01-03  0       
2 AMZN   2012-01-04 -0.00853 
3 AMZN   2012-01-05  0.000563
4 AMZN   2012-01-06  0.0278  
5 AMZN   2012-01-09 -0.0224  
6 AMZN   2012-01-10  0.00436 

The data is in Long format, but it is easier to use in Wide format.

> # covert to Wide format
> # change date to and xts object
> log_ret_xts <- log_ret %>%
+   pivot_wider(names_from = symbol, values_from = ret) %>%
+   tk_xts()
> 
> log_ret_xts <- log_ret_xts[-1,]
> head(log_ret_xts)
                    AMZN         AAPL         CSCO          IBM
2012-01-04 -0.0085264668  0.005359783  0.019139273 -0.004087766
2012-01-05  0.0005632237  0.011040895 -0.003692805 -0.004754084
2012-01-06  0.0277625957  0.010399339 -0.003706492 -0.011547064
2012-01-09 -0.0224280687 -0.001587166  0.006345963 -0.005218020
2012-01-10  0.0043587552  0.003573713 -0.007407408 -0.001543116
2012-01-11 -0.0024564662 -0.001631630  0.012664911  0.005555397

We can then find the mean daily return and multiply by 252 trading days to find an annual return.

> mean_ret <- colMeans(log_ret_xts)
> print(round(mean_ret, 7))
     AMZN      AAPL      CSCO       IBM 
0.0011395 0.0006143 0.0004972 0.0000111 
> annual_ret <- colMeans(log_ret_xts)*252
> print(round(annual_ret, 7))
     AMZN      AAPL      CSCO       IBM 
0.2871530 0.1548033 0.1252873 0.0028074 

Covariance

Since we want to use the annualized Sharpe Ratio we will need the annualized covariance.

> # Correlation
> cor(log_ret_xts)
          AMZN      AAPL      CSCO       IBM
AMZN 1.0000000 0.2365540 0.2868891 0.2591122
AAPL 0.2365540 1.0000000 0.3014089 0.2952234
CSCO 0.2868891 0.3014089 1.0000000 0.4237137
IBM  0.2591122 0.2952234 0.4237137 1.0000000
> # daily covariance x 252
> cov_mat <- cov(log_ret_xts) * 252 # multiply by days
> print(round(cov_mat,4))
       AMZN   AAPL   CSCO    IBM
AMZN 0.0945 0.0190 0.0200 0.0149
AAPL 0.0190 0.0684 0.0179 0.0145
CSCO 0.0200 0.0179 0.0514 0.0180
IBM  0.0149 0.0145 0.0180 0.0352

Sample Sharpe Ratio

First we generate random weights.

> # uniform distribution from 0 to 1
> set.seed(12345)
> (weights <- runif(n = length(tickers)))
[1] 0.7209039 0.8757732 0.7609823 0.8861246
> sum(weights)
[1] 3.243784
> # rebalance weights to 100%
> (weights <- weights/sum(weights))
[1] 0.2222416 0.2699851 0.2345971 0.2731762
> sum(weights)
[1] 1

Then we calculate the annual portfolio return using these weights.

> (port_return <- sum(weights * mean_ret)*252)
[1] 0.1357709

Next, we use linear algebra to calculate the portfolio variance and standard deviation.

> (var_port <- t(weights) %*% (cov_mat %*% weights))
           [,1]
[1,] 0.02799402
> (sd_port <- sqrt(t(weights) %*% (cov_mat %*% weights)))
          [,1]
[1,] 0.1673141

Finally, we can calculate the Sharpe Ratio. In this case I will assume that the risk free rate is zero, since it is currently minimal.

> # Since Risk free rate is 0% 
> 
> (sharpe_ratio <- port_return/sd_port)
          [,1]
[1,] 0.8114728

Monte Carlo Simulation


To find the optimal portfolio we can create random weights and loop through the calculations many times.

> num_port <- 5000 #simulations
> 
> # Create a matrix to store the weights
> all_wts <- matrix(nrow = num_port,
+                   ncol = length(tickers))
> 
> # Create an empty vector to store  returns
> port_returns <- vector('numeric', length = num_port)
> 
> # Create an empty vector to store SD
> port_risk <- vector('numeric', length = num_port)
> 
> # Create an empty vector to store Sharpe
> sharpe_ratio <- vector('numeric', length = num_port)

Loop Through

> for (i in seq_along(port_returns)) {
+   
+   wts <- runif(length(tickers))
+   wts <- wts/sum(wts)
+   
+   # Store weights in the matrix
+   all_wts[i,] <- wts
+   
+   # Portfolio returns
+   port_ret <- sum(wts * mean_ret)*252
+   
+   # Store Portfolio Returns 
+   port_returns[i] <- port_ret
+   
+   # Creating and storing portfolio risk
+   port_sd <- sqrt(t(wts) %*% (cov_mat  %*% wts))
+   port_risk[i] <- port_sd
+   
+   # Creating and storing Portfolio Sharpe Ratios
+   # Assuming 0% Risk free rate
+   sr <- port_ret/port_sd
+   sharpe_ratio[i] <- sr
+   
+ }

Optimize Format

> # Storing the values in the table
> portfolio_values <- tibble(Return = port_returns,
+                            Risk = port_risk,
+                            SharpeRatio = sharpe_ratio)
> 
> # Converting matrix to a tibble and changing column names
> all_wts <- tk_tbl(all_wts)
> 
> colnames(all_wts) <- colnames(log_ret_xts)
> 
> # Combing all the values together
> portfolio_values <- tk_tbl(cbind(all_wts, portfolio_values))
> 
> head(portfolio_values)
# A tibble: 6 x 7
   AMZN     AAPL   CSCO    IBM Return  Risk SharpeRatio
  <dbl>    <dbl>  <dbl>  <dbl>  <dbl> <dbl>       <dbl>
1 0.313 0.114    0.223  0.349   0.137 0.171       0.798
2 0.382 0.520    0.0181 0.0800  0.193 0.207       0.931
3 0.463 0.000715 0.246  0.291   0.165 0.194       0.850
4 0.202 0.209    0.0931 0.495   0.104 0.163       0.634
5 0.185 0.133    0.394  0.288   0.124 0.167       0.742
6 0.283 0.171    0.307  0.239   0.147 0.172       0.856

Optimal Portfolio

> (min_var <- portfolio_values[which.min(portfolio_values$Risk),])
# A tibble: 1 x 7
   AMZN  AAPL  CSCO   IBM Return  Risk SharpeRatio
  <dbl> <dbl> <dbl> <dbl>  <dbl> <dbl>       <dbl>
1 0.100 0.173 0.248 0.479 0.0879 0.160       0.550
> (max_sr <- portfolio_values[which.max(portfolio_values$SharpeRatio),])
# A tibble: 1 x 7
   AMZN  AAPL  CSCO     IBM Return  Risk SharpeRatio
  <dbl> <dbl> <dbl>   <dbl>  <dbl> <dbl>       <dbl>
1 0.506 0.281 0.211 0.00251  0.215 0.209        1.03

Plots


> p <- min_var %>%
+   pivot_longer(AMZN:IBM, names_to = "Asset",
+          values_to = "Weights")%>%
+   mutate(Asset = as.factor(Asset)) %>%
+   ggplot(aes(x = fct_reorder(Asset,Weights), y = Weights, fill = Asset)) +
+   geom_bar(stat = 'identity') +
+   theme_minimal() +
+   labs(x = 'Assets', y = 'Weights', title = "Minimum Variance Portfolio Weights") +
+   scale_y_continuous(labels = scales::percent) 
> 
> ggplotly(p)
> p <- max_sr %>%
+   pivot_longer(AMZN:IBM, names_to = "Asset",
+                values_to = "Weights") %>%
+   mutate(Asset = as.factor(Asset)) %>%
+   ggplot(aes(x = fct_reorder(Asset,Weights), y = Weights, fill = Asset)) +
+   geom_bar(stat = 'identity') +
+   theme_minimal() +
+   labs(x = 'Assets', y = 'Weights', title = "Tangency Portfolio Weights") +
+   scale_y_continuous(labels = scales::percent) 
> 
> ggplotly(p)
> p <- portfolio_values %>%
+   ggplot(aes(x = Risk, y = Return, color = SharpeRatio)) +
+   geom_point() +
+   scale_color_gradient(low="black", high="yellow") +
+   theme_classic() +
+   scale_y_continuous(labels = scales::percent) +
+   scale_x_continuous(labels = scales::percent) +
+   labs(x = 'Annualized Risk',
+        y = 'Annualized Returns',
+        title = "Portfolio Optimization & Efficient Frontier") +
+   geom_point(aes(x = Risk,
+                  y = Return), data = min_var, color = 'red') +
+   geom_point(aes(x = Risk,
+                  y = Return), data = max_sr, color = 'red') +
+   annotate('text', x = 0.20, y = 0.25, label = "Tangency Portfolio") +
+   annotate('text', x = 0.18, y = 0.01, label = "Minimum variance portfolio") +
+   annotate(geom = 'segment', x = 0.15, xend = 0.16,  y = 0.03, 
+            yend = 0.09, color = 'red', arrow = arrow(type = "open")) +
+   annotate(geom = 'segment', x = 0.19, xend = 0.21,  y = 0.23, 
+            yend = 0.215, color = 'red', arrow = arrow(type = "open"))
> 
> 
> ggplotly(p)