In this workshop we will learn a) how to handle a flexible list of tickers from an Excel file, b) how to simulate portfolios with more than 2 assets, c) how to calculate the efficient frontier, the Global Minimum Variance portfolio and the optimal portfolio.
We will download price data using a flexible list of tickers from Excel.
Download the InputW6.xlsx file from Canvas / Modules / Week 7. Save this file in the directory where you save this .Rmd file.
You have to install the package IntroCompFinR from a specific web repository. Go to the console and type:
install.packages(“IntroCompFinR”, repos = “http://R-Forge.R-project.org”)
Now load the required libraries for this workshop:
library(quantmod)
library(dplyr)
library(PerformanceAnalytics)
library(IntroCompFinR)
library(readxl)
Now we set the working directory where you downloaded the Excel file. You can set the working directory using the RStudio Menu: Session/Set working directory/Choose Directory, and then you can copy and paste the R code from the console to your R chunk.
library(readxl)
InputW6 <- read_excel("InputW6.xlsx")
View(InputW6)
We read a list of tickers from an Excel file. The Excel file has one Sheet with a list of tickers along with 4 different portfolios with their respective weights. You can download the Excel file “InputsW6.xlsx” from the course site. Save this file in your working directory.
input.df<-read_excel("InputW6.xlsx",sheet = "ticklist")
tickers.list<-input.df$ticker
Now we have the input of the program in the data frame input.df. The first and only one column is the list of tickers. We can get monthly data from Yahoo from 2016 to Sep 21, 2021:
# Using getsymbols we can download monthly data from yahoo, paring tickers to stock prices
getSymbols(tickers.list,
from = "2016-01-01", to = "2021-09-21",
periodicity = "monthly",
src = "yahoo")
[1] "AMZN" "JPM" "WMT" "TSLA"
Since the getSymbols function creates an xts - zoo object for each asset, it is convenient to merge those objects into one single object. We will call this object prices.zoo using the merge function.
However, since we are reading the tickers from an Excel file, the name of the objects are the ticker names, which are stored in tickers.list. Although we know this, we cannot get access to these xts-xoo objects directly. Let’s explore why this is the case. If try to run the following line:
#prices.zoo <- merge(tickers.list)
This will not work since the merge function expect a list of objects separated by commas as parameters, and we are sending the string vector contained in tickers.list. We can also try:
#prices.zoo <- merge(tickers.list[1], tickers.list[2], tickers.list[3], tickers.list[4])
This will not work either since merge expect a list of objects, and we are sending as parameters the tickers as words or strings, not as object names.
Then, we have to find a way to create a list of xts-zoo objects and named them as the ticker names stored in tickers.list. We can use the function get to create these objects from the string names:
# zoo is the creator for an S3 class of indexed totally ordered observations which includes irregular time series
p1<-get(tickers.list[1])
p2<-get(tickers.list[2])
p3<-get(tickers.list[3])
p4<-get(tickers.list[4])
prices.zoo <- merge(p1, p2, p3, p4)
Now p1, p2, p3, and p4 will be xts-zoo objects, and the prices.zoo will have all these objects merged. Note that the merge fuction works for xts and zoo objects only. We can now remove the objects we created:
rm(p1, p2, p3, p4)
We solved the problem in the case of exactly 4 tickers. However, what about solving this problem for any number of tickers? In this case, we can think in storing a list of objects. The function lapply applies a function to a list. This list can be any type of variable. Then, we can create a list of objects xts-zoo objects using the function get and the tickers.list as parameters of the lapply function:
objList <- lapply(tickers.list, get)
class(objList)
[1] "list"
Now the list objList is a list of xts-zoo objects. Check how the lapply function applied the get function to the string vector tickers.list. Then the get function will be applied to each element of the tickers.list vector and the result will be assigned to the objList. objList will be a list of xts-zoo objects that contains the data frames with the price data for each stock.
We do not have to specify how many objects we send to this list. Then, if we specified 10 tickers in the Excel file, then this list will be a list of 10 xts-zoo objects, which will have the price information of 10 tickers.
To access each member of an object in R, we have to use double squared bracket and the element number. For example, if I want to display the first observations of the first xls-zoo object in the list, we can do the following:
head(objList[[1]])
AMZN.Open AMZN.High AMZN.Low AMZN.Close AMZN.Volume
2016-01-01 33 33 27 29 2604018000
2016-02-01 29 29 24 28 2482896000
2016-03-01 28 30 27 30 1880190000
2016-04-01 30 33 29 33 1569284000
2016-05-01 33 36 33 36 1812290000
2016-06-01 36 37 34 36 1490818000
AMZN.Adjusted
2016-01-01 29
2016-02-01 28
2016-03-01 30
2016-04-01 33
2016-05-01 36
2016-06-01 36
Now we have to do a merge of the objects contained in this list. Since we know that we have only 4 tickers, then we can do the following:
prices.zoo <- merge(objList[[1]], objList[[2]], objList[[3]], objList[[4]])
This will work ok, but what if I want to generalize the code for any number of tickers specified in the Excel file? One effective way to do this is using the function do.call. This function allows you to call any R function, but instead of writing out the arguments one by one, I can use a list to hold the arguments of the function:
prices.zoo <- do.call(merge, objList)
We generalized our code to ANY number of tickers! The do.call function is similar to the lapply function, but the do.call function runs a function and send a list as parameters of the function.
Then the prices.zoo object will be a xts-zoo objects with many columns for all prices for all tickers.
I select only adjusted prices and drop NA values:
prices.df <- as.data.frame(na.omit(Ad(prices.zoo)))
We used the function na.omit(). This function will drop any observation that has at least one missing value for any of the stock prices. In this case, if we have a Mexican stock and US stocks, then the holidays of any country will be dropped for all stocks, including the observations of the Mexican stocks.
Now we calculate the monthly compounded returns. We do this as the difference in the log prices for asset i.
rit=Δ(log(priceit))
returns.df <- diff(log(as.zoo(prices.df)))
We take a look to the return data.
head(returns.df)
AMZN.Adjusted JPM.Adjusted WMT.Adjusted
2016-02-01 -0.061 -0.049 -0.00030
2016-03-01 0.072 0.051 0.03189
2016-04-01 0.105 0.065 -0.01656
2016-05-01 0.092 0.040 0.05683
2016-06-01 -0.010 -0.049 0.03845
2016-07-01 0.059 0.029 -0.00068
TSLA.Adjusted
2016-02-01 0.0038
2016-03-01 0.1799
2016-04-01 0.0467
2016-05-01 -0.0756
2016-06-01 -0.0503
2016-07-01 0.1008
tail(returns.df)
AMZN.Adjusted JPM.Adjusted WMT.Adjusted
2021-04-01 0.114 0.010 0.0338
2021-05-01 -0.073 0.071 0.0150
2021-06-01 0.065 -0.054 -0.0032
2021-07-01 -0.033 -0.024 0.0108
2021-08-01 0.042 0.058 0.0382
2021-09-01 -0.055 0.023 -0.0570
TSLA.Adjusted
2021-04-01 0.060
2021-05-01 -0.126
2021-06-01 0.084
2021-07-01 0.011
2021-08-01 0.068
2021-09-01 0.053
To avoid scientific notations for outputs, I define the following:
options(scipen=100)
options(digits=2)
I display descriptive statistics of returns using the table.Stats function from the PerformanceAnalytics package:
returns.zoo<-as.zoo(returns.df)
table.Stats(returns.df)
We can do a Boxplot of the returns to better appreciate the median, the quartiles Q1 and Q3, volatility and extreme values of these returns:
chart.Boxplot(returns.zoo)
Box plots help us to understand risk of financial instruments.
Learn what is a box plot and provide your own INTERPRETATION of this plot (use your own words); respond in CAPITAL LETTERS.
WE CAN SEE THAT THE MEAN RETURS ARE ALL POSITIVE FOR THESE STOCKS. ALSO, WE CAN SEE IN THE BOX PLOT THE THE RETURNS OF TESLA HAVE A VERY LARGE SCALE, MEANING THAT IT IS MMORE RISKY BUT CAN GIVE VERY HIGH RETURNS AND HAS A MEAN THAT IS THE HIGHEST OF ALL THE STOCKS. FOR AMAZON WE HAVE A SMALLER RANGE THAT HAS LESS OUTLIERS AND ALSO A HIGH MEAN RETURN. I CAN SEE THAT THE STOCKS THAT HAVE LESS VOLATILITY O RISK ALSO HAVE SMALLER RETURNS.
Using these 4 assets you have to simulate 1000 portfolios with random weights for each asset, but the sum of the 4 random weights must be equal to 1.
Design an algorithm to create a vector of 4 rows and 1,000 columns with random weights, where the sum of each column is equal to 1. We can use the function runif which generates a random number following the uniform probability distribution. Here is an example of this algorithm (remember that this is an example, but there are many different ways to write this algoritm)
# I create an empty vector
W<-c()
# I do a loop from 1 to 3 to create 1,000 columns of 4 random weights following
# the uniform probability distribution:
for(i in 1:4) {
W<-rbind(W,runif(1000))
}
# The problem I have now is that some of the 1,000 portfolios
# might end up having a sum of weights higher than one. # I can do a simple "trick" by
# dividing each of the 4 weights by the sum of these 4
# weights. And I can do this # for all 1000 portfolios:
# I first create a vector with the sum of weights for all portfolios:
sumw <- colSums(W)
# I do another loop to divide each weight by the sum of weights:
for(i in 1:4) {
W[i,]<-W[i,]/sumw
# In each iteration I divide one raw of W2 by the vector sumw,
# which is the sum of the weights of all 1000 portfolios
}
# I check that the sum of weights is 1 (I do this only for 10 portfolios)
colSums(W[,1:10])
[1] 1 1 1 1 1 1 1 1 1 1
# All sums are equal to 1, as expected
# Then each column of this matrix represents a random portfolio without allowing for short sales.
Then each of the 1,000 columns of W represent one random portfolio of 4 assets. I can calculate the expected return and expected risk of each of these portfolios according to Markowitz portfolio theory.
# I create a matrix for the historical returns of the assets:
ret.mat <- as.matrix(returns.df)
# I generate the expected returns of the assets as their geometric means:
exp.rets<-exp(colMeans(ret.mat)) - 1
# I generate the variance-covariance matrix:
COV<-var(ret.mat)
# I estimate the expected returns of all 1,000 portfolios using matrix algebra:
ERP<-t(W)%*%exp.rets
# I estimate the expected variance and risk of the portfolios using matrix algebra:
VARP<-t(W)%*%COV%*%W
# I get the diagonal of this matrix and take square root to get the expected risk
# of the 1,000 portfolios:
RISKP<-sqrt(diag(VARP))
# I remove the VARP object since it has 1,000 times 1,000 cels! 1 million cells!
rm(VARP)
Now I can plot all the expected risk vs return of the 1,000 portfolios:
plot(RISKP,ERP[,1], main="Random portfolios of 4 assets",xlab="Volatility",ylab="Exp Return")
Write code to identify which of the 1,000 portfolios is the portfolio with the lowest expected variance. You have to show the weights of each asset and its corresponding expected risk and expected variance. Hint: you can use sub-setting of data frames.
IN ORDER TO SOLVE THIS CHALLENGE WE OUGH TO CREATE A DATA FRAME THAT INCLUDES EXPECTED RISK AND PORTFOLIO RETURN, AND THEN ARRANGE THEM IN ORDER TO FIND OUT WHICH PORTFOLIO RETURN IS MORE FAVOURABLE.
# CREATING DATA FRAME, RENAMING THEM
portfolios.df = cbind(ERP,RISKP,t(W))
portfolios.df = as.data.frame(portfolios.df)
names(portfolios.df) = c("ERet","ERisk",tickers.list)
# SHOWING LOWEST EXPECTED RISK FIRST
sorted = portfolios.df[order(portfolios.df$ERisk),]
MinVar =sorted[1,]
MinVar
MINIMUM VARIANCE PORTFOLIO: .067 AMZN, 0.31 JPM, 0.62 WMT AND 0.0079 W TSLA.
USING GMVPORTWS FUNCTION WE CAN FIND AN ESTIMATE GMV PORTFOLIO
gmvportws = globalMin.portfolio(exp.rets, COV)
“Remember that the calculation of the Global Minimum Variance Portfolio is an optimization problem stated as the portfolio with the minimum risk of all possible combinations subject to the sum of return weights of the portfolio is equal to 1.
According to Portfolio Theory, the expected risk of a portfolio P is calculated as the squared root of the Portfolio Variance.
SD(P)=(Var(P))‾‾‾‾‾‾‾‾√
The portfolio variance can be calculated with the following matrix multiplication:
Var(P)=W′∗∑cov∗W where:
W is the weight vector of the stocks, and W′ is the tranposed matrix of weights. Then, the optimization problem to calculate the Global Minimum Variance Portfolio is defined as:
Minimize:
W′∗∑cov∗W
subject to:
1′∗W=1
1′ is the transpose vector (horizontal vector of 1’s) that is multiplied times the vertical vector W. The result of this matrix multiplication is the sum of all weights. The function globalMin.portfolio gets the variance-covariance matrix COVMAT, represented in the code as covmat, and the vector of expected returns to calculate the solution of this optimization problem.
You can check the solution of this problem in the note Portfolio Theory - Part 3 or any Finance book that covers Portfolio theory.
The globalMin.portfolio function returns a portfolio class object, which has different attributes:” (C.DORANTES)
# Print the whole object
gmvportws
Call:
globalMin.portfolio(er = exp.rets, cov.mat = COV)
Portfolio expected return: 0.016
Portfolio standard deviation: 0.042
Portfolio weights:
AMZN.Adjusted JPM.Adjusted WMT.Adjusted TSLA.Adjusted
0.132 0.273 0.602 -0.006
# Check the attributes
attributes(gmvportws)
$names
[1] "call" "er" "sd" "weights"
$class
[1] "portfolio"
# To see the weights of the portfolio
gmvportws$weights
AMZN.Adjusted JPM.Adjusted WMT.Adjusted TSLA.Adjusted
0.132 0.273 0.602 -0.006
# To see the class of the object
class(gmvportws)
[1] "portfolio"
“setting the parameter shorts = FALSE.”
RUNNING SAME FUNCTION WITHOUT ALLOWING SHORT SALES
gmvportwos <- globalMin.portfolio(exp.rets, COV, shorts = FALSE)
# Print the object
gmvportwos
Call:
globalMin.portfolio(er = exp.rets, cov.mat = COV, shorts = FALSE)
Portfolio expected return: 0.016
Portfolio standard deviation: 0.042
Portfolio weights:
AMZN.Adjusted JPM.Adjusted WMT.Adjusted TSLA.Adjusted
0.13 0.27 0.60 0.00
“The efficient frontier calculates the portfolio of minimum variance for different levels of expected returns. It uses a similar optimization solution than in the case of the GMV Portfolio, but it adds the restriction that the expected return must be a specific return. We use the efficient.frontier function:”
ef <- efficient.frontier(exp.rets, COV, nport = 100,
alpha.min = -0.5,
alpha.max = 1.5, shorts = TRUE)
“The efficient.frontier function returns a Markowitz class object, which has different attributes:”
attributes(ef)
$names
[1] "call" "er" "sd" "weights"
$class
[1] "Markowitz"
Finally, w CREATING VISUALIZATION OF EFFICIENT FRONTIER INCLUDING ASSETS OF PORTFOLIO IN THE PLOT.
plot(ef, plot.assets = TRUE, col = "blue", pch = 16)
“This frontier is not actually the “efficient” frontier since there are some portfolios with the same level of risk with lower expected return. We can generate only portfolios that are actually efficient by changing the alpha.min and alpha.max parameters.
The efficient.frontier function calculates the frontier using 2 portfolios: the GMV portfolio and the tangent/optimal portfolio. These 2 portfolios are efficient. Using these 2 portfolios the function calculates intermediate efficient portfolios by considering each portfolio as a financial instrument and generating linear combinations of these 2 portfolios with weights alpha for one portfolio and (1-alpha) for the second portfolio.
When we specify alpha.min and alpha.max, the function will do a loop to generate several linear combinations of efficient portfolios to generate portfolios between the GMV and the optimal portfolio. When alpha=1 it is assigning 100% to the GMV portfolio and 0% to the tangent portfolio; when alpha=-0.5 it shorts GMV in 50% and assign 150% to the tangent portfoLIO” (C. DORANTES).
ef <- efficient.frontier(exp.rets, COV, nport = 100,
alpha.min = -0.5,
alpha.max = 1, shorts = TRUE)
plot(ef, plot.assets = TRUE, col = "blue", pch = 16)
With your own words, INTERPRET this frontier. Write your interpretation in CAPITAL LETTERS.
FOLLOWING THE EFFICIENT FRONTIER CURVE, WE CAN OBSERVE THAT TRADITIONAL RETAIL-BASED BUSINESS PROVES TO BE MORE RELIABLE IN TERMS OF STANDARD DEVIATION BUT PRESENT A LOWER RETURN IN COMPARRISON TO INTERNET AND TECH BASED COMPANIES SUCH AS AMAZON AND TESLA.
Now that you are more familiar with R objects and Portfolio Theory, you have to look at the following code, run it, and write as comments what each line of code does:
r.free = 0.0
tan.port <- tangency.portfolio(exp.rets, COV, r.free)
tanportweights<-getPortfolio(er=exp.rets,cov.mat=COV,weights=tan.port$weights)
plot(tanportweights, col="blue")
gmin.port<-globalMin.portfolio(exp.rets, COV)
efrontier <- efficient.frontier(exp.rets, COV, alpha.min=-0.5,
alpha.max=1.5, nport=30)
attributes(efrontier)
$names
[1] "call" "er" "sd" "weights"
$class
[1] "Markowitz"
plot(efrontier)
plot(efrontier, plot.assets=TRUE, col="blue", pch=16)
points(gmin.port$sd, gmin.port$er, col="green", pch=16, cex=2)
points(tan.port$sd, tan.port$er, col="red", pch=16, cex=2)
text(gmin.port$sd, gmin.port$er, labels="GLOBAL MIN", pos=2)
text(tan.port$sd, tan.port$er, labels="TANGENCY", pos=2)
sr.tan = (tan.port$er - r.free)/tan.port$sd
abline(a=r.free, b=sr.tan, col="green", lwd=2)
“Make sure you explain what each function does and which are the parameters. For example, the plot function receives the object efrontier of the class Markowitz. A Markowitz object contains plot(x, plot.assets = FALSE, …). The gmin.port is the global minimum portfolio and has globalMin.portfolio(er, covmat). The tan.port is the tangency portfolio and has tangency.portfolio(er, covmat, r.free).” (C. DORANTES).
You have to simulate 5 frontiers of 2-asset portfolios where each frontier you assume a hypothetical correlation between the 2 assets, starting from -1 up to +1 jumping by 0.5. Do the following:
Download monthly price data for WMT and AMZN from Yahoo from Jan 2016 to date. Calculate cc returns
Calculate the expected returns and store them in a vector
Calculate the Variance-Covariance matrix of these 2 stock returns
Create a vector wa for simulating different weights for WMT. Create this vector with sequential values from 0 to 1. Create the vector wb equal to the complement of wa (1-wa) that represent the % invested in AMZN.
Then, with these weight vectors of 11 elements you have to estimate the expected return, variance and expected standard deviation of each of these 11 portfolios.
Do a scatter plot for the portfolio frontier, so that you can see the relationship between risk and return of these 11 portfolios.
You have to simulate 5 portfolio frontiers using the weight vectors with positive weights. Each of these portfolio frontiers will have a hypothetical correlation between WMT and AMZN. Create the first portfolio frontier with a hypothetical correlation of -1, then the 2nd portfolio frontier with a correlation of -0.5, then the 3rd with a correlation of 0, then the 4rd frontier with a correlation of 0.5, and finally, the 5th frontier with a correlation of 1.
Graph the 5 frontiers in one plot. What do you observe? EXPLAIN with your own words.
Encapsulate the code you wrote in a function that receives the two tickers, initial date and end date, and do the previous steps finishing in the simulation of 5 portfolio frontiers with different levels of correlation.
FIRST I DOWNLOAD THE MONTHLY PRICES OF WMT AND AMAZON
# WE CALL STOCK PRICES FOR BOTH AMAZON AND WALMART
getSymbols(Symbols=c("AMZN","WMT"),
from="01-01-2016",
src="yahoo",
periodicity="monthly")
[1] "AMZN" "WMT"
NOW I CALCULATE THE EXP RETURNS IN ONE SAME VECTOR
# MERGE BOTH DATASETS
port<-merge(WMT,AMZN)
rm(WMT,AMZN)
# CALCULATE RETURNS:
rets<-diff(log(port))
# SELECT ADJUSTED PRICES
rets.df <-as.data.frame(na.omit(Ad(rets)))
names(rets.df)<-c("RWMT","RAMZN")
NOW I CALCULATE THE VARIANCE-COVARIANCE MATRIX OF THESE TWO STOCK RETURNS
# CREATING MATRIX FOR HISTORICAL RETURNS OF THE ASSETS:
ret.mat <- as.matrix(rets.df)
# GENERATE EXPECTED RETURNS OF THE ASSETS EXPRESSED AS THEIR GEOMETRIC MEANS:
exp.rets<-exp(colMeans(ret.mat)) - 1
# WE GENERATE THE VARIANCE-COVARIANCE MATRIX:
COV<-var(ret.mat)
COV
RWMT RAMZN
RWMT 0.0037 0.0019
RAMZN 0.0019 0.0232
COV<-var(rets.df)
ER <-exp(apply(rets.df,2,mean))-1
ER
RWMT RAMZN
0.0086 0.0234
Create a vector w for simulating different weights for WMT. Create this vector with sequential values from 0 to 1. Create the vector wb equal to the complement of wa (1-wa) that represent the % invested in AMZN.
# I create the weights for WMT, from 0 to 1 jumping by 0.10:
wa<-seq(0,1,by=0.1)
# I create the weights for AMZN, which is the complement of the WMT weight:
wb<-1-wa
# I bind both vectors, so that each portfolio will be represented in each column
W<-rbind(wa,wb)
W
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11]
wa 0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1
wb 1 0.9 0.8 0.7 0.6 0.5 0.4 0.3 0.2 0.1 0
ERP <- t(W) %*% ER
ERP
[,1]
[1,] 0.0234
[2,] 0.0220
[3,] 0.0205
[4,] 0.0190
[5,] 0.0175
[6,] 0.0160
[7,] 0.0145
[8,] 0.0131
[9,] 0.0116
[10,] 0.0101
[11,] 0.0086
Then, with these weight vectors of 11 elements you have to estimate the expected return, variance and expected standard deviation of each of these 11 portfolios.
# I estimate the expected returns using matrix algebra:
ERP<-t(W)%*%exp.rets
# I estimate the expected variance and risk of the portfolios using matrix algebra:
VARP<-t(W)%*%COV%*%W
# I get the diagonal of this matrix and take square root to get the expected risk
RISKP<-sqrt(diag(VARP))
# I remove the VARP
rm(VARP)
ERP
[,1]
[1,] 0.0234
[2,] 0.0220
[3,] 0.0205
[4,] 0.0190
[5,] 0.0175
[6,] 0.0160
[7,] 0.0145
[8,] 0.0131
[9,] 0.0116
[10,] 0.0101
[11,] 0.0086
class(ERP)
[1] "matrix" "array"
class(RISKP)
[1] "numeric"
Do a scatter plot for the portfolio frontier, so that you can see the relationship between risk and return of these 11 portfolios.
plot(RISKP,ERP, main="Portolio Risk vs Return")
install.packages("IntroCompFinR", repos = "http://R-Forge.R-project.org")
Error in install.packages : Updating loaded packages
wa2<-seq(-0.4,1.4,0.1)
wb2<-1-wa2
W2<-rbind(wa2,wb2)
W2
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11]
wa2 -0.4 -0.3 -0.2 -0.1 0 0.1 0.2 0.3 0.4 0.5 0.6
wb2 1.4 1.3 1.2 1.1 1 0.9 0.8 0.7 0.6 0.5 0.4
[,12] [,13] [,14] [,15] [,16] [,17] [,18] [,19]
wa2 0.7 0.8 0.9 1 1.1 1.2 1.3 1.4
wb2 0.3 0.2 0.1 0 -0.1 -0.2 -0.3 -0.4
ERP2 <- t(W2) %*% ER
ERP2
[,1]
[1,] 0.0294
[2,] 0.0279
[3,] 0.0264
[4,] 0.0249
[5,] 0.0234
[6,] 0.0220
[7,] 0.0205
[8,] 0.0190
[9,] 0.0175
[10,] 0.0160
[11,] 0.0145
[12,] 0.0131
[13,] 0.0116
[14,] 0.0101
[15,] 0.0086
[16,] 0.0071
[17,] 0.0057
[18,] 0.0042
[19,] 0.0027
VARP2 <-t(W2)%*% COV %*% W2
VARP2<-diag(VARP2)
RISKP2 <-sqrt(VARP2)
RISKP2
[1] 0.210 0.195 0.181 0.166 0.152 0.139 0.125 0.112 0.099 0.088
[11] 0.077 0.069 0.063 0.060 0.061 0.065 0.073 0.083 0.094
ERP2
[,1]
[1,] 0.0294
[2,] 0.0279
[3,] 0.0264
[4,] 0.0249
[5,] 0.0234
[6,] 0.0220
[7,] 0.0205
[8,] 0.0190
[9,] 0.0175
[10,] 0.0160
[11,] 0.0145
[12,] 0.0131
[13,] 0.0116
[14,] 0.0101
[15,] 0.0086
[16,] 0.0071
[17,] 0.0057
[18,] 0.0042
[19,] 0.0027
names(wa)<-wa
wa
0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1
0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0
plot(RISKP2,ERP2, main="Portolio Risk vs Return")
lines(RISKP2,ERP2,col="blue")
lines(RISKP,ERP,col="green")
text(RISKP2,ERP2,labels=wa2, cex=0.7, pos=3)
“Now I will simulate different levels of correlations between both assets, starting from -1 to 1 jumping by 0.5.
I can start with the case of corr (r) = -1
To calculate the expected variance of each of the portfolios, I can modify the Variance-Covariance matrix according to this simulated correlation.
Remember that the correlation between 2 random variables is the standardized version of the covariance of the 2 variables; correlation is equal to the covariance between 2 variables divided by the product of the standard deviation of each covariance:
Corr(r1,r2)=Cov(r1,r2)SD(r1)∗SD(r2) We do simple algebra to express covariance in terms of correlation:
Cov(r1,r2)=Corr(r1,r2)∗SD(r1)∗SD(r2)
Then, for a simulated correlation= -1:
Cov(r1,r2)=−1∗SD(r1)∗SD(r2)
I can get the standard deviation of a column of a dataframe as:” (C. DORANTES)
sd(rets.df$rwmt)
[1] NA
apply(rets.df,2,sd)
RWMT RAMZN
0.061 0.152
covmat1 <-var(rets.df)
sdrwmt <-sqrt(covmat1[1,1])
sdrwmt
[1] 0.061
sdramzn <- sqrt(covmat1[2,2])
sdramzn
[1] 0.15
covmat1[1,2]=-1*sd(rets.df$rwmt)*sd(rets.df$ramzn)
covmat1[2,1]<-covmat1[1,2]
covmat1
RWMT RAMZN
RWMT 0.0037 NA
RAMZN NA 0.023
RISKMAT<-matrix(0,nrow=19,ncol=5)
COV<-var(rets.df)
i=0
# I create a vector with colors, so that each correlation level will have one color
cols<-c("red","blue","green","brown","yellow")
for (r in seq(-1,1,by=0.5)) {
i=i+1
covmat1<-COV
# I calculate the corresponding covariance between WMT and AMZN return for the corresponding
# level of correlation r
covmat1[1,2]=r*sd(rets.df$rwmt)*sd(rets.df$ramzn)
covmat1[2,1]<-covmat1[1,2]
RISKMAT[,i]<-sqrt(diag(t(W2)%*%covmat1%*%W2))
# Only in the first iteration I draw the plot, and then a line for the correlation
if (i==1) {
plot(RISKMAT[,i],ERP2, xlab="Portfolio volatility", ylab="Portfolio Expected Return",
main="Portfolios of 2 Assets: WMT & AMZN",
col=cols[i])
lines(RISKMAT[,i],ERP2,type="b",col=cols[i])
text(RISKMAT[,i],ERP2,labels=wa, cex=0.7, pos=3)
}
# For the second iteration and the last one I just add a line for the corresponding set of
# portfolios with the corresponding correlation level r:
else {
lines(RISKMAT[,i],ERP2,col=cols[i])
text(RISKMAT[,i],ERP2,labels=wa, cex=0.7, pos=3)
}
}
Warning: no non-missing arguments to min; returning InfWarning: no non-missing arguments to max; returning -InfError in plot.window(...) : need finite 'xlim' values
simulating_frontiers <- function(ticker1,ticker2,startdate,enddate,
min_w1, max_w1, weight_jump, corr_jump) {
library(quantmod)
getSymbols(Symbols=c(ticker1,ticker2),
from=startdate,
to=enddate,
src="yahoo",
periodicity="monthly")
# I merge both datasets
# Since I do not know which tickers will be used, then I need to use the get function
# in order to get the xts object using the name of the ticker:
dataset1<-get(ticker1)
dataset2<-get(ticker2)
port1<-merge(dataset1,dataset2)
rm(dataset1,dataset2)
# I calculate returns:
rets<-diff(log(port1))
# I select only adjusted prices
rets.df <-as.data.frame(na.omit(Ad(rets)))
names(rets.df)<-c(ticker1,ticker2)
# I calculate the variance-covariance matrix:
COV<-var(rets.df)
# I calculate the expected (geometric) returns of each stock:
ER <-exp(apply(rets.df,2,mean))-1
# I create the weights for WMT, from 0 to 1 jumping by 0.10:
wa<-seq(min_w1,max_w1,by=weight_jump)
# I create the weights for AMZN, which is the complement of the WMT weight:
wb<-1-wa
# I bind both vectors, so that each portfolio will be represented in each column
W<-rbind(wa,wb)
#Now I have a matrix in which I represent 11 portfolios. Each column is one portfolio
#Portfolio 1 is composed of 0% WMT, 100%AMZN, while Portfolio 11 is 100%WMT and 0%AMZN
#I can estimate the Expected return of the 11 portfolio using Matrix Algebra:
ERP <- t(W) %*% ER
#Now I can get the expected variance and risk of each portfolio:
VARP <-t(W)%*% COV %*% W
#The expected variance of the portfolio will be in the diagonal of this Matrix:
VARP<-diag(VARP)
#I take the square root to get the standard deviation of each portfolio:
RISKP <-sqrt(VARP)
plot(RISKP,ERP, main="Portolio Risk vs Return")
## Now I will do the same, but I will create more portfolios starting with shorting the first asset with min_w1=-0.4
wa2<-seq(min_w1,max_w1,weight_jump)
wb2<-1-wa2
W2<-rbind(wa2,wb2)
#Now I have a matrix in which I represent 19 portfolios. Each column is one portfolio
#Portfolio 1 is composed of -40% asset1, 140% asset2, while Portfolio 11 is 140% asset1 and -40% asset2
#I can estimate the Expected return of the 11 portfolio using Matrix Algebra:
ERP2 <- t(W2) %*% ER
#Now I can get the expected variance and risk of each portfolio:
VARP2 <-t(W2)%*% COV %*% W2
#The expected variance of the portfolio will be in the diagonal of this Matrix:
VARP2<-diag(VARP2)
#I take the square root to get the standard deviation of each portfolio:
RISKP2 <-sqrt(VARP2)
names(wa)<-wa
plot(RISKP2,ERP2, main="Portolio Risk vs Return")
lines(RISKP2,ERP2,col="blue")
lines(RISKP,ERP,col="green")
text(RISKP2,ERP2,labels=wa2, cex=0.7, pos=3)
##Now I will simulate different levels of correlations between both assets, starting
# from -1 to 1 jumping by corr_jump
#I can start with the case of corr (r) = -1
# To calculate the expected variance of each of the portfolios, I can modify the VAriance-Covariance
# matrix according to this simulated correlation
# Remember that the correlation is the standardized version of the covariance:
# Corr(r1,r2) = Cov(r1,r2) / (sd(r1)*sd(r2))
# Then:
# Cov(r1,r2)= Corr(r1,r2) * sd(r1)* sd(r2)
#Then, for a simulated correlation= -1:
# Cov(r1,r2) = -1*sd(r1)*sd(r2)
#I can get the standard deviation of a column of a dataframe as:
#sd(rets.df$rwmt)
#I can also use the apply function to the dataframe:
#apply(rets.df,2,sd)
#I can also get the standard deviation from the variance-covariance matrix:
covmat1 <-var(rets.df)
sd1 <-sqrt(covmat1[1,1])
sd2 <- sqrt(covmat1[2,2])
#Then, if I assume a correlation of - 1, I can construct the variance-covariance matrix as follows:
covmat1[1,2]=-1*sd1*sd2
covmat1[2,1]<-covmat1[1,2]
covmat1
# The expected return is the same as the expected returns I calculated before since
# I am only manipulating correlations, not mean returns
corrs=seq(-1,1,by=corr_jump)
#Then, I can use a loop instead of repeating the same code for 5 different correlations:
RISKMAT<-matrix(0,nrow=length(wa2),ncol=length(corrs))
COV<-var(rets.df)
i=0
# I create a vector with colors, so that each correlation level will have one color
cols<-c("red","blue","green","brown","yellow")
for (r in corrs) {
i=i+1
covmat1<-COV
# I calculate the corresponding covariance between WMT and AMZN return for the corresponding
# level of correlation r
covmat1[1,2]=r*sd1*sd2
covmat1[2,1]<-covmat1[1,2]
print(W2)
print(covmat1)
RISKMAT[,i]<-sqrt(diag(t(W2)%*%covmat1%*%W2))
# Only in the first iteration I draw the plot, and then a line for the correlation
if (i==1) {
plot(RISKMAT[,i],ERP2, xlab="Portfolio volatility",
ylab="Portfolio Expected Return",
main=paste0("Portfolios of 2 Assets ",ticker1," & ",ticker2),
col=cols[i])
lines(RISKMAT[,i],ERP2,type="b",col=cols[i])
text(RISKMAT[,i],ERP2,labels=wa, cex=0.7, pos=3)
}
# For the second iteration and the last one I just add a line for the corresponding set of
# portfolios with the corresponding correlation level r:
else {
lines(RISKMAT[,i],ERP2,col=cols[i])
text(RISKMAT[,i],ERP2,labels=wa, cex=0.7, pos=3)
}
}
# I just add a legend for the frontiers according to each hypothetical correlation:
legend("bottomright",legend=c("r=-1","r=-0.5","r=0","r=0.5","r=1"),
col=cols,lty=1:5,cex=0.7)
}
simulating_frontiers("PG","MSFT","2018-01-01","2022-03-31",
min_w1 = -0.4,max_w1 = 1.4, weight_jump = 0.1,
corr_jump = 0.50)
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11]
wa2 -0.4 -0.3 -0.2 -0.1 0 0.1 0.2 0.3 0.4 0.5 0.6
wb2 1.4 1.3 1.2 1.1 1 0.9 0.8 0.7 0.6 0.5 0.4
[,12] [,13] [,14] [,15] [,16] [,17] [,18] [,19]
wa2 0.7 0.8 0.9 1 1.1 1.2 1.3 1.4
wb2 0.3 0.2 0.1 0 -0.1 -0.2 -0.3 -0.4
PG MSFT
PG 0.0022 -0.0026
MSFT -0.0026 0.0030
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11]
wa2 -0.4 -0.3 -0.2 -0.1 0 0.1 0.2 0.3 0.4 0.5 0.6
wb2 1.4 1.3 1.2 1.1 1 0.9 0.8 0.7 0.6 0.5 0.4
[,12] [,13] [,14] [,15] [,16] [,17] [,18] [,19]
wa2 0.7 0.8 0.9 1 1.1 1.2 1.3 1.4
wb2 0.3 0.2 0.1 0 -0.1 -0.2 -0.3 -0.4
PG MSFT
PG 0.0022 -0.0013
MSFT -0.0013 0.0030
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11]
wa2 -0.4 -0.3 -0.2 -0.1 0 0.1 0.2 0.3 0.4 0.5 0.6
wb2 1.4 1.3 1.2 1.1 1 0.9 0.8 0.7 0.6 0.5 0.4
[,12] [,13] [,14] [,15] [,16] [,17] [,18] [,19]
wa2 0.7 0.8 0.9 1 1.1 1.2 1.3 1.4
wb2 0.3 0.2 0.1 0 -0.1 -0.2 -0.3 -0.4
PG MSFT
PG 0.0022 0.000
MSFT 0.0000 0.003
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11]
wa2 -0.4 -0.3 -0.2 -0.1 0 0.1 0.2 0.3 0.4 0.5 0.6
wb2 1.4 1.3 1.2 1.1 1 0.9 0.8 0.7 0.6 0.5 0.4
[,12] [,13] [,14] [,15] [,16] [,17] [,18] [,19]
wa2 0.7 0.8 0.9 1 1.1 1.2 1.3 1.4
wb2 0.3 0.2 0.1 0 -0.1 -0.2 -0.3 -0.4
PG MSFT
PG 0.0022 0.0013
MSFT 0.0013 0.0030
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11]
wa2 -0.4 -0.3 -0.2 -0.1 0 0.1 0.2 0.3 0.4 0.5 0.6
wb2 1.4 1.3 1.2 1.1 1 0.9 0.8 0.7 0.6 0.5 0.4
[,12] [,13] [,14] [,15] [,16] [,17] [,18] [,19]
wa2 0.7 0.8 0.9 1 1.1 1.2 1.3 1.4
wb2 0.3 0.2 0.1 0 -0.1 -0.2 -0.3 -0.4
PG MSFT
PG 0.0022 0.0026
MSFT 0.0026 0.0030
Graph the 5 frontiers in one plot. What do you observe? EXPLAIN with your own words. ON A STANDARD EXPECTED RETURN VERSUS STANDARD DEVIATION GRAPH, INVESTORS WILL PREFER PORTFOLIOS THAT LIE TO THE LEFT AND ABOVE OF THE CURRENT INVESTMENT OPPORTUNITY SET. WE CAN THUS SAY THAT THE FIRST PORTFOLIO IS THE BEST SINCE IT OFFERS LESS VOLATILITY AND A HIGH RETURN IN COMPARISON TO THE OTHER PORTFOLIOS.