> library(tidyquant) # To download the data
> library(plotly) # To create interactive charts
> library(timetk) # To manipulate the data series
> library(tidyverse) # To organize the dataI am going to use the same stocks and time period that I used with Python previously:
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')# 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.
AMZN AAPL CSCO IBM
0.0011395 0.0006143 0.0004972 0.0000111
AMZN AAPL CSCO IBM
0.2871530 0.1548033 0.1252873 0.0028074
Since we want to use the annualized Sharpe Ratio we will need the annualized covariance.
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
First we generate random weights.
[1] 0.7209039 0.8757732 0.7609823 0.8861246
[1] 3.243784
[1] 0.2222416 0.2699851 0.2345971 0.2731762
[1] 1
Then we calculate the annual portfolio return using these weights.
[1] 0.1357709
Next, we use linear algebra to calculate the portfolio variance and standard deviation.
[,1]
[1,] 0.02799402
[,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.
[,1]
[1,] 0.8114728
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)> 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
+
+ }> # 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
# 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
# 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
> 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)