output: word_document: toc: yes html_document: toc: yes —

The code below is written to identify the portfolio (of two stocks: Apple and General Electric) weight that yields the hightest Sharp Ratio.

Suppose, instead, that you have four stocks you would like to invest: Apple, Tesla, Facebook and Netflix. To identify the optimal portfolio weight that would yield the highest Sharp Ratio, you analyze the data that covers the period of 2013-01-01 to 2017-12-31.

Q1 Revise the code below for the four stocks you would like to invest in, instead of Apple and General Electric.

Hint: Nothing else is required for this question, other than revising the code below.

Q2 Revise the code below to calculate monthly returns, instead of quarterly returns.

Hint: Nothing else is required for this question, other than revising the code below.

library(tidyquant)
library(ggplot2)

# Import stock prices
stock_prices <- c("AAPL", "TSLA", "FB", "NFLX") %>%
    tq_get(get  = "stock.prices",
           from = "2013-01-01",
           to   = "2017-12-31") %>%
    group_by(symbol)
stock_prices
## # A tibble: 5,036 x 8
## # Groups:   symbol [4]
##    symbol date        open  high   low close    volume adjusted
##    <chr>  <date>     <dbl> <dbl> <dbl> <dbl>     <dbl>    <dbl>
##  1 AAPL   2013-01-02  79.1  79.3  77.4  78.4 140129500     56.3
##  2 AAPL   2013-01-03  78.3  78.5  77.3  77.4  88241300     55.6
##  3 AAPL   2013-01-04  76.7  76.9  75.1  75.3 148583400     54.1
##  4 AAPL   2013-01-07  74.6  75.6  73.6  74.8 121039100     53.7
##  5 AAPL   2013-01-08  75.6  76.0  74.5  75.0 114676800     53.9
##  6 AAPL   2013-01-09  74.6  75.0  73.7  73.9 101901100     53.0
##  7 AAPL   2013-01-10  75.5  75.5  73.6  74.8 150286500     53.7
##  8 AAPL   2013-01-11  74.4  75.0  74.1  74.3  87626700     53.4
##  9 AAPL   2013-01-14  71.8  72.5  71.2  71.7 183551900     51.5
## 10 AAPL   2013-01-15  71.2  71.3  69.1  69.4 219193100     49.8
## # ... with 5,026 more rows

# Calculate monthly returns
returns <- stock_prices %>%
    tq_transmute(select     = adjusted,
                 mutate_fun = periodReturn,
                 period     = "monthly",
                 col_rename = "Ra") %>%
  spread(symbol, Ra)

returns <- returns[,-1] #returns[,-1] b/c 1st column is date, which we don't need
returns
## # A tibble: 60 x 4
##         AAPL        FB     NFLX    TSLA
##        <dbl>     <dbl>    <dbl>   <dbl>
##  1 -0.170     0.106     0.796    0.0608
##  2  0.0100   -0.120     0.138   -0.0714
##  3  0.00285  -0.0613    0.00638  0.0879
##  4  0.000271  0.0856    0.142    0.425 
##  5  0.0647   -0.123     0.0471   0.811 
##  6 -0.118     0.0218   -0.0670   0.0982
##  7  0.141     0.479     0.158    0.251 
##  8  0.128     0.122     0.161    0.259 
##  9 -0.0215    0.217     0.0891   0.144 
## 10  0.0964   -0.000398  0.0429  -0.173 
## # ... with 50 more rows

The efficient frontier first finds a grid of target returns and then a portfolio that has the lowest variance per each target return. But what is a reasonable grid of target returns?

Q3 Revise the code below to create 50 target return values in the grid, instead of 45 target return values.

Hint: Nothing else is required for this question, other than revising the code below.

# Compute the efficient frontier using a grid of target returns

# Calculate each stock's mean returns
stockmu <- colMeans(returns) 
stockmu
##       AAPL         FB       NFLX       TSLA 
## 0.02103526 0.03476665 0.05537655 0.04816872

# Create a grid of target values
grid <- seq(from = 0.03, to = max(stockmu), length.out = 50) #from=0.03: must be >= min(stockmu)
grid
##  [1] 0.03000000 0.03051789 0.03103578 0.03155367 0.03207156 0.03258944
##  [7] 0.03310733 0.03362522 0.03414311 0.03466100 0.03517889 0.03569678
## [13] 0.03621467 0.03673255 0.03725044 0.03776833 0.03828622 0.03880411
## [19] 0.03932200 0.03983989 0.04035778 0.04087566 0.04139355 0.04191144
## [25] 0.04242933 0.04294722 0.04346511 0.04398300 0.04450089 0.04501878
## [31] 0.04553666 0.04605455 0.04657244 0.04709033 0.04760822 0.04812611
## [37] 0.04864400 0.04916189 0.04967977 0.05019766 0.05071555 0.05123344
## [43] 0.05175133 0.05226922 0.05278711 0.05330500 0.05382289 0.05434077
## [49] 0.05485866 0.05537655

# Create empty vectors to store means and deviations
vpm <- vpsd <- rep(NA, times = 50) # 50 b/c 50 target returns were created in grid 
vpm
##  [1] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
## [24] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
## [47] NA NA NA NA
vpsd
##  [1] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
## [24] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
## [47] NA NA NA NA

# Create an empty matrix to store weights
mweights <- matrix(NA, 50, ncol(returns)) # 50 weights/target returns (rows) and ncol(returns) is # of stocks (columns)
mweights
##       [,1] [,2] [,3] [,4]
##  [1,]   NA   NA   NA   NA
##  [2,]   NA   NA   NA   NA
##  [3,]   NA   NA   NA   NA
##  [4,]   NA   NA   NA   NA
##  [5,]   NA   NA   NA   NA
##  [6,]   NA   NA   NA   NA
##  [7,]   NA   NA   NA   NA
##  [8,]   NA   NA   NA   NA
##  [9,]   NA   NA   NA   NA
## [10,]   NA   NA   NA   NA
## [11,]   NA   NA   NA   NA
## [12,]   NA   NA   NA   NA
## [13,]   NA   NA   NA   NA
## [14,]   NA   NA   NA   NA
## [15,]   NA   NA   NA   NA
## [16,]   NA   NA   NA   NA
## [17,]   NA   NA   NA   NA
## [18,]   NA   NA   NA   NA
## [19,]   NA   NA   NA   NA
## [20,]   NA   NA   NA   NA
## [21,]   NA   NA   NA   NA
## [22,]   NA   NA   NA   NA
## [23,]   NA   NA   NA   NA
## [24,]   NA   NA   NA   NA
## [25,]   NA   NA   NA   NA
## [26,]   NA   NA   NA   NA
## [27,]   NA   NA   NA   NA
## [28,]   NA   NA   NA   NA
## [29,]   NA   NA   NA   NA
## [30,]   NA   NA   NA   NA
## [31,]   NA   NA   NA   NA
## [32,]   NA   NA   NA   NA
## [33,]   NA   NA   NA   NA
## [34,]   NA   NA   NA   NA
## [35,]   NA   NA   NA   NA
## [36,]   NA   NA   NA   NA
## [37,]   NA   NA   NA   NA
## [38,]   NA   NA   NA   NA
## [39,]   NA   NA   NA   NA
## [40,]   NA   NA   NA   NA
## [41,]   NA   NA   NA   NA
## [42,]   NA   NA   NA   NA
## [43,]   NA   NA   NA   NA
## [44,]   NA   NA   NA   NA
## [45,]   NA   NA   NA   NA
## [46,]   NA   NA   NA   NA
## [47,]   NA   NA   NA   NA
## [48,]   NA   NA   NA   NA
## [49,]   NA   NA   NA   NA
## [50,]   NA   NA   NA   NA

# Load tseries package to use portfolio.optim() function to calculate the efficient frontier
library(tseries)

# Create your for loop
for(i in 1:length(grid)) {
  opt <- portfolio.optim(x = as.matrix(returns), pm = grid[i])
  vpm[i] <- opt$pm  
  vpsd[i] <- opt$ps
  mweights[i, ] <- opt$pw
}
vpm
##  [1] 0.03000000 0.03051789 0.03103578 0.03155367 0.03207156 0.03258944
##  [7] 0.03310733 0.03362522 0.03414311 0.03466100 0.03517889 0.03569678
## [13] 0.03621467 0.03673255 0.03725044 0.03776833 0.03828622 0.03880411
## [19] 0.03932200 0.03983989 0.04035778 0.04087566 0.04139355 0.04191144
## [25] 0.04242933 0.04294722 0.04346511 0.04398300 0.04450089 0.04501878
## [31] 0.04553666 0.04605455 0.04657244 0.04709033 0.04760822 0.04812611
## [37] 0.04864400 0.04916189 0.04967977 0.05019766 0.05071555 0.05123344
## [43] 0.05175133 0.05226922 0.05278711 0.05330500 0.05382289 0.05434077
## [49] 0.05485866 0.05537655
vpsd
##  [1] 0.05807033 0.05821638 0.05845001 0.05877019 0.05917550 0.05966420
##  [7] 0.06023428 0.06088344 0.06160919 0.06240885 0.06327962 0.06421860
## [13] 0.06522286 0.06628943 0.06741534 0.06859768 0.06983358 0.07112024
## [19] 0.07245497 0.07383516 0.07525830 0.07672200 0.07822400 0.07976212
## [25] 0.08133432 0.08293865 0.08457329 0.08623652 0.08792672 0.08964235
## [31] 0.09138198 0.09314428 0.09496278 0.09688912 0.09891772 0.10104241
## [37] 0.10325727 0.10555661 0.10793505 0.11038746 0.11290902 0.11549522
## [43] 0.11814180 0.12084479 0.12360050 0.12676397 0.13139550 0.13742230
## [49] 0.14467009 0.15296541
mweights
##                [,1]        [,2]       [,3]         [,4]
##  [1,]  5.459343e-01 0.302647347 0.09718191 5.423644e-02
##  [2,]  5.283957e-01 0.304697983 0.10682260 6.008372e-02
##  [3,]  5.108571e-01 0.306748619 0.11646329 6.593100e-02
##  [4,]  4.933185e-01 0.308799255 0.12610398 7.177827e-02
##  [5,]  4.757799e-01 0.310849892 0.13574467 7.762555e-02
##  [6,]  4.582413e-01 0.312900528 0.14538537 8.347283e-02
##  [7,]  4.407027e-01 0.314951164 0.15502606 8.932010e-02
##  [8,]  4.231641e-01 0.317001800 0.16466675 9.516738e-02
##  [9,]  4.056255e-01 0.319052436 0.17430744 1.010147e-01
## [10,]  3.880869e-01 0.321103072 0.18394813 1.068619e-01
## [11,]  3.705483e-01 0.323153708 0.19358882 1.127092e-01
## [12,]  3.530097e-01 0.325204345 0.20322952 1.185565e-01
## [13,]  3.354711e-01 0.327254981 0.21287021 1.244038e-01
## [14,]  3.179325e-01 0.329305617 0.22251090 1.302510e-01
## [15,]  3.003938e-01 0.331356253 0.23215159 1.360983e-01
## [16,]  2.828552e-01 0.333406889 0.24179228 1.419456e-01
## [17,]  2.653166e-01 0.335457525 0.25143297 1.477929e-01
## [18,]  2.477780e-01 0.337508161 0.26107366 1.536401e-01
## [19,]  2.302394e-01 0.339558798 0.27071436 1.594874e-01
## [20,]  2.127008e-01 0.341609434 0.28035505 1.653347e-01
## [21,]  1.951622e-01 0.343660070 0.28999574 1.711820e-01
## [22,]  1.776236e-01 0.345710706 0.29963643 1.770292e-01
## [23,]  1.600850e-01 0.347761342 0.30927712 1.828765e-01
## [24,]  1.425464e-01 0.349811978 0.31891781 1.887238e-01
## [25,]  1.250078e-01 0.351862614 0.32855851 1.945711e-01
## [26,]  1.074692e-01 0.353913251 0.33819920 2.004183e-01
## [27,]  8.993060e-02 0.355963887 0.34783989 2.062656e-01
## [28,]  7.239200e-02 0.358014523 0.35748058 2.121129e-01
## [29,]  5.485339e-02 0.360065159 0.36712127 2.179602e-01
## [30,]  3.731479e-02 0.362115795 0.37676196 2.238075e-01
## [31,]  1.977619e-02 0.364166431 0.38640266 2.296547e-01
## [32,]  2.237582e-03 0.366217068 0.39604335 2.355020e-01
## [33,]  0.000000e+00 0.342250008 0.41490715 2.428428e-01
## [34,]  0.000000e+00 0.314478187 0.43511972 2.504021e-01
## [35,]  0.000000e+00 0.286706367 0.45533229 2.579613e-01
## [36,]  0.000000e+00 0.258934547 0.47554486 2.655206e-01
## [37,]  0.000000e+00 0.231162727 0.49575743 2.730798e-01
## [38,]  0.000000e+00 0.203390907 0.51597000 2.806391e-01
## [39,]  0.000000e+00 0.175619087 0.53618257 2.881983e-01
## [40,]  0.000000e+00 0.147847266 0.55639514 2.957576e-01
## [41,]  0.000000e+00 0.120075446 0.57660771 3.033168e-01
## [42,]  0.000000e+00 0.092303626 0.59682028 3.108761e-01
## [43,]  0.000000e+00 0.064531806 0.61703285 3.184353e-01
## [44,]  0.000000e+00 0.036759986 0.63724543 3.259946e-01
## [45,]  0.000000e+00 0.008988166 0.65745800 3.335538e-01
## [46,] -2.115360e-18 0.000000000 0.71259648 2.874035e-01
## [47,] -5.242940e-18 0.000000000 0.78444736 2.155526e-01
## [48,]  4.714063e-17 0.000000000 0.85629824 1.437018e-01
## [49,] -1.149810e-17 0.000000000 0.92814912 7.185088e-02
## [50,] -1.462568e-17 0.000000000 1.00000000 4.996004e-16

Q4 If you want to obtain the highest possible average monthly return, how would you divide your money among the given stocks in the portfolio?

Hint: Recall how the grid is created above. The highest target monthly return is also the largest value in the grid. This highest target return is only attainable when you go all in with the stock that yielded the highest average monthly return! Buy mostly lower prices stocks. If you pay $600 to buy 600 stocks and each stock goes up one cent you will make $6 as opposed to buying one stock for $600 and only making 1 cent.

Q5 You may create portfolios with a maximum weight constraint so that your money is less concentrated only in a few assets. What argument in the tseries::portfolio.optim() allows you to set weight constraints?

Hint: You learned this at DataCamp. See the section, “Imposing weight constraints”.

# Convert portfolio weights to data frame, and add column and row names
names(stockmu)
## [1] "AAPL" "FB"   "NFLX" "TSLA"
colnames(mweights) <- names(stockmu)
mweights <- data.frame(mweights)
mweights$index <- seq(1, nrow(mweights))
mweights
##             AAPL          FB       NFLX         TSLA index
## 1   5.459343e-01 0.302647347 0.09718191 5.423644e-02     1
## 2   5.283957e-01 0.304697983 0.10682260 6.008372e-02     2
## 3   5.108571e-01 0.306748619 0.11646329 6.593100e-02     3
## 4   4.933185e-01 0.308799255 0.12610398 7.177827e-02     4
## 5   4.757799e-01 0.310849892 0.13574467 7.762555e-02     5
## 6   4.582413e-01 0.312900528 0.14538537 8.347283e-02     6
## 7   4.407027e-01 0.314951164 0.15502606 8.932010e-02     7
## 8   4.231641e-01 0.317001800 0.16466675 9.516738e-02     8
## 9   4.056255e-01 0.319052436 0.17430744 1.010147e-01     9
## 10  3.880869e-01 0.321103072 0.18394813 1.068619e-01    10
## 11  3.705483e-01 0.323153708 0.19358882 1.127092e-01    11
## 12  3.530097e-01 0.325204345 0.20322952 1.185565e-01    12
## 13  3.354711e-01 0.327254981 0.21287021 1.244038e-01    13
## 14  3.179325e-01 0.329305617 0.22251090 1.302510e-01    14
## 15  3.003938e-01 0.331356253 0.23215159 1.360983e-01    15
## 16  2.828552e-01 0.333406889 0.24179228 1.419456e-01    16
## 17  2.653166e-01 0.335457525 0.25143297 1.477929e-01    17
## 18  2.477780e-01 0.337508161 0.26107366 1.536401e-01    18
## 19  2.302394e-01 0.339558798 0.27071436 1.594874e-01    19
## 20  2.127008e-01 0.341609434 0.28035505 1.653347e-01    20
## 21  1.951622e-01 0.343660070 0.28999574 1.711820e-01    21
## 22  1.776236e-01 0.345710706 0.29963643 1.770292e-01    22
## 23  1.600850e-01 0.347761342 0.30927712 1.828765e-01    23
## 24  1.425464e-01 0.349811978 0.31891781 1.887238e-01    24
## 25  1.250078e-01 0.351862614 0.32855851 1.945711e-01    25
## 26  1.074692e-01 0.353913251 0.33819920 2.004183e-01    26
## 27  8.993060e-02 0.355963887 0.34783989 2.062656e-01    27
## 28  7.239200e-02 0.358014523 0.35748058 2.121129e-01    28
## 29  5.485339e-02 0.360065159 0.36712127 2.179602e-01    29
## 30  3.731479e-02 0.362115795 0.37676196 2.238075e-01    30
## 31  1.977619e-02 0.364166431 0.38640266 2.296547e-01    31
## 32  2.237582e-03 0.366217068 0.39604335 2.355020e-01    32
## 33  0.000000e+00 0.342250008 0.41490715 2.428428e-01    33
## 34  0.000000e+00 0.314478187 0.43511972 2.504021e-01    34
## 35  0.000000e+00 0.286706367 0.45533229 2.579613e-01    35
## 36  0.000000e+00 0.258934547 0.47554486 2.655206e-01    36
## 37  0.000000e+00 0.231162727 0.49575743 2.730798e-01    37
## 38  0.000000e+00 0.203390907 0.51597000 2.806391e-01    38
## 39  0.000000e+00 0.175619087 0.53618257 2.881983e-01    39
## 40  0.000000e+00 0.147847266 0.55639514 2.957576e-01    40
## 41  0.000000e+00 0.120075446 0.57660771 3.033168e-01    41
## 42  0.000000e+00 0.092303626 0.59682028 3.108761e-01    42
## 43  0.000000e+00 0.064531806 0.61703285 3.184353e-01    43
## 44  0.000000e+00 0.036759986 0.63724543 3.259946e-01    44
## 45  0.000000e+00 0.008988166 0.65745800 3.335538e-01    45
## 46 -2.115360e-18 0.000000000 0.71259648 2.874035e-01    46
## 47 -5.242940e-18 0.000000000 0.78444736 2.155526e-01    47
## 48  4.714063e-17 0.000000000 0.85629824 1.437018e-01    48
## 49 -1.149810e-17 0.000000000 0.92814912 7.185088e-02    49
## 50 -1.462568e-17 0.000000000 1.00000000 4.996004e-16    50

# Visualize minimum variance portfolio weights per target returns 
mweights %>%
  gather(var, value, 1:ncol(returns)) %>%
  ggplot(aes(x = index, y = value, fill = var)) +
  geom_col()

Q6 How would you divide your money among the stocks in the portfolio, if you are risk-averse and looking for the least amount of volatility?

Q7 How would you divide your money among the stocks in the portfolio, in order to obtain the greatest return per risk?

Hint: The Sharp Ratio measures returns adjusted for risk.

# Identify the minimum variance portfolio
mweights_minvar <- mweights[vpsd == min(vpsd), ]
mweights_minvar
##        AAPL        FB       NFLX       TSLA index
## 1 0.5459343 0.3026473 0.09718191 0.05423644     1

# Identify the highest Sharp Ratio portfolio
vsr <- (vpm - 0.0075)/vpsd

mweights_maxsr <- mweights[vsr == max(vsr), ]
mweights_maxsr
##         AAPL        FB      NFLX      TSLA index
## 15 0.3003938 0.3313563 0.2321516 0.1360983    15

Imposing weight constraints

Create three portfolios with different maximum weight constraints.

An advantage of a maximum weight constraint is the subsequent portfolio will be less concentrated in certain assets.

A disadvantage of a maximum weight constraint is the same target return may no longer be possible or will be obtained at the expense of a higher volatility.

The function portfolio.optim() allows you to set weight constraints within the reshigh argument. reshigh requires a vector of maximum weights for each asset.

Assigning any random number for the reshigh argument may give an error. This is because some target returns are not attainable with weight constraints.