library(PerformanceAnalytics)
library(yfR)
library(ggplot2)
library(dplyr)
library(tidyr)
library(ggthemes)
library(tidyquant)
library(roll)
<-'2010-01-01'
start <-'2022-01-01'
end <- yf_collection_get("IBOV",
data first_date = start,
last_date = end,
freq_data = "daily",)
<-data[complete.cases(data),]
data$month <- format(as.Date(data$ref_date, format="%y/%m/%d"),"%y/%m")
data<- data %>%
sd group_by(ticker,month) %>%
summarise_at(vars(ret_adjusted_prices),list(sd = sd)) %>%
as.data.frame()
<- data %>%
vol group_by(ticker,month) %>%
summarise_at(vars(volume),list(vol_sum = sum)) %>%
as.data.frame()
<- data %>%
vol2 group_by(month) %>%
summarise_at(vars(volume),
list(vol_tot = sum)) %>%
as.data.frame()
<- merge(sd ,vol, by=c("ticker","month"))
data2 <- merge(data2 ,vol2, by=c("month"))
data2 $w <- data2$vol_sum/data2$vol_tot
data2$sdw <- data2$sd * data2$w
data2<- data2 %>%
data3 group_by(month) %>%
summarise_at(vars(sdw),list(total = sum)) %>%
as.data.frame()
<-data3[complete.cases(data3),]
data3$id <-"Weighted"
data3# Index
<- yf_get(tickers = '^BVSP',
ibov first_date = start,
last_date = end,
freq_data = "daily",)
<-ibov[complete.cases(ibov),]
ibov$month <- format(as.Date(ibov$ref_date, format="%y/%m/%d"),"%y/%m")
ibov<- ibov %>%
sd2 group_by(month, ticker) %>%
summarise_at(vars(ret_closing_prices) ,
list(total = var ) ) %>%
as.data.frame()
$total <- sd2$total^0.5
sd2$id <-"ibov"
sd2$ticker <- NULL
sd2<- rbind(data3,sd2)
data4 ggplot(data=data4, aes(x=month, y=total, color = id, group = id)) +
geom_line() +
labs(x = "",
y='Standard deviation',
title ="Diversification benefits - Ibov vs. Weighted sd of individual stocks") + theme_solarized()
4 Portfolio Management
This is part of my Portfolio Management course.
4.1 Diversification
In this graph, we observe the benefits of diversification. The pink line computes the standard deviation of Ibov from 2010 to 2022, while the light green line computes the average of the standard deviation of the individual stocks that compose Ibov weighted by monthly volume. We can see that the portfolio has a lower standard deviation than the weighted average.
4.1.1 Two assets price behavior
Note below that often when the price of a asset drops, the price of the other rises.
Let’s see the same graph using prices in day 1 = $100.
4.1.2 Correlation
Finally, let’s compute the correlation between the returns of these assets.
## [1] 0.5443201
The correlation is 0.544
4.1.3 Two-asset portfolio return
Now, let’s compute the average return of a portfolio with 40% invested in asset 1 and the remaining 60% invested in asset 2.
# Defining weights and calculating portfolio return (daily)
<- c(0.40, 0.60)
w # Creating a df with stocks and weights
<- tibble(ticker = stocks,w = w)
w_tbl # Including the weights in the df prices (which contains the prices)
<- left_join(data ,w_tbl, by = 'ticker')
prices # calculating the product of return times the portfolio weights for all days (this is necessary to calculate average return)
$w_ret <- prices$ret_closing_prices * prices$w
prices# Creating a dataframe with portfolio returns
<- prices %>%
port_ret group_by(ref_date) %>%
summarise(port_ret = sum(w_ret))
# Creating prices from the vector of returns
$price_close2 <- cumprod(1+port_ret$port_ret) * 100
port_ret# Graph with all returns
$ticker <- 'Portfolio'
port_retggplot(stock1, aes(ref_date , price_close2, color = ticker))+
geom_line() +geom_line(data=stock2) +geom_line(data=port_ret) +
labs(x = "",
y='Closing prices',
title="Two assets and Portfolio returns, Initial price = 100",
subtitle = "Begin 01/01/2010") + theme_solarized()
Obs: The return of the portfolio is the weighted average of the assets’ returns.
## [1] 0.03216604
## [1] 0.03330732
The average return of asset 1 and asset 2 are, respectively, 0.032 and 0.033.
## [1] 0.03285081
The average return of the portfolio is 0.033. ___
We can compute the average return of the portfolio “by hand”:
[1] 0.03285081
The average return of the portfolio, computed by hand, is 0.033.
Notice below that the difference between the two methods of calculation is zero, as it should be!
[1] 0
4.2 Portfolio risk
4.2.1 Two-asset portfolio risk
However, the standard deviation of the portfolio returns is not the weighted average of the assets’ standard deviation. Remember the equation below:
\[var = (w_1^2 \times \sigma_1^2) + (w_2^2 \times \sigma_2^2) + 2(w_1 \times w_2 \times \sigma_1 \times \sigma_2 \times \rho_{12})\]
Where \(\sigma^2\) is the variance of a asset, \(w\) are the weights, and \(\rho\) is the correlation between the assets.
## [1] 2.116466
## [1] 2.931682
## [1] 2.330659
## [1] 2.605595
## [1] -0.274936
These are the estimates from the previous example:
The standard deviation of asset 1 is 2.116.
The standard deviation of asset 2 is 2.932.
The standard deviation of the portfolio is 2.331.
The weighted average of the assets’ standard deviation is 2.606.
The decrease in the standard deviation due to diversification is -0.275.
4.2.2 Multi-assets portfolio risk
While the expected return of a three-asset portfolio is the weighted average of expected returns, the variance of the portfolio is measured using the equation below:
\[var = (w_1^2 \times \sigma_1^2) + (w_2^2 \times \sigma_2^2) + 2(w_1 \times w_2 \times \sigma_1 \times \sigma_2 \times \rho_{12}) + \\ (w_3^2 \times \sigma_3^2) + 2(w_1 \times w_3 \times \sigma_1 \times \sigma_3 \times \rho_{13}) + 2(w_2 \times w_3 \times \sigma_2 \times \sigma_3 \times \rho_{23})\]
When you have enough stocks (with equal weights), you can compute the variance as follows:
\[Var=\frac{1}{N} \times average\;variance + (1-\frac{1}{N}) \times average \; covariance\]
So, now, let’s compute the variance of multi-assets portfolios, equally weighted using real data from the Brazilian market.
<-'2015-01-01'
start <-'2022-01-01'
end # Finding the tickers
<- yf_collection_get("IBOV",
data_temp first_date = start,
last_date = end,
freq_data = "monthly")
<-unique(data_temp$ticker)
tickers<- data_temp %>% select(ref_date) %>% distinct()
df # Now collecting data
for (i in 1:length(tickers)) {
<- yf_get(tickers[[i]],
data first_date = start,
last_date = end,
freq_data = "monthly")
<- data[complete.cases(data),]
data <- data %>% select(ref_date, ret_closing_prices)
data colnames(data) <- c("ref_date", tickers[[i]] )
<- merge(df,data,by="ref_date")
df
}# Setup to randomly combine "j" stocks and compute the standard deviation of this combination
<- data.frame(matrix(NA,nrow = 1000, ncol = length(tickers)))
final $ref_date <- NULL
dffor (j in 1:length(tickers) ) {
for (i in 1:2000 ) {
<- as.data.frame(df[, sample( ncol(df) , j )])
df_r <- cov(df_r)
cov # Var mean
<- as.data.frame(diag(cov))
var <- mean(var$`diag(cov)`)
var_mean #Covariance mean
if (j == 1) {
<- var_mean
covar_mean else {
} <- cov
covar diag(covar)=NA
upper.tri(covar)] <- NA
covar[<-c(covar)
covar<- mean(covar,na.rm=TRUE)
covar_mean
}# Port Sd.
<- ( (1/ j * var_mean ) + (1- (1/j ) ) * covar_mean ) ^ 0.5
var_p <- var_p
final[i,j]
}
}# Computing the average of the "i" portfolios with "j" stocks
<- as.data.frame(colMeans(final))
sd $N <- seq_along(sd[,1])
sdggplot(sd, aes(y=`colMeans(final)` , x = N)) + geom_line() +
labs(x = "Number of stocks (equal weight)",
y='Standard deviation (monthly)',
title="Standard deviation a equally weighted portfolios",
subtitle = "Begin 01/01/2015 (Brazilian stocks)") + theme_solarized()
You see, the previous graph contains the average of all possible combinations using N stocks. The inclusion of the next stock is defined by volume. If you don’t take the average of all possible combinations, you get the next graph. Clearly, the drop in risk is not so smooth.
4.3 Correlation extended
See what happens with the portfolio risk when we change the combination of weights of each asset.
The graph below contains the set of feasible portfolios (i.e., investment possibilities) containing a combination of two assets. For this example, I am fabricating the data as below. The graph below suggests there is one combination of weights that minimizes the standard deviation of the portfolio.
Notice that this graph expands the weights to below zero and above one. These weights’ values occur when you can sell one asset to buy the other. The portfolios in which you are selling either one of the assets are represented in both outer parts of the curve.
= -0.50
corr12
= 0.175
ret1 = 0.258
sd1 = sd1^2
var1 = 0.055
ret2 = 0.115
sd2 = sd2^2
var2 = corr12*sd1*sd2
covar12 = seq(from=-0.4, to=1.4, by=0.05)
w1 = 1 - w1
w2
= w1*ret1 + w2*ret2
retp = w1^2 * var1 + w2^2 * var2 + 2*w1*w2*covar12
varp = sqrt(varp)
sdp = paste("portfolio", 1:length(w1), sep=" ")
port.names <- data.frame(port.names, w1, w2, retp, sdp)
df
<- df %>% mutate(Condition = case_when(w1<0 ~ "W1<0" , w1>1 ~ "W1>1" , 0<w1 | w1<1 ~ "0<w1<1" ))
df
<- df[df$w1 == 1, ]
d1 <- df[df$w2 == 1, ]
d2
ggplot(df, aes(sdp, retp, color= Condition) ) +
geom_point(size=3) +
labs(x = "Standard deviation",y='Expected return') +
xlim(0, max(sdp)) + ylim(0, max(retp) ) +
geom_text(data = d1, aes( label = "Asset 1") , color = "black", hjust=1, vjust=-1, size=5) +
geom_text(data = d2, aes( label = "Asset 2") , color = "black", hjust=1, vjust= 2, size=5) +
ggtitle(paste("Correlation = " , corr12))
4.3.1 Combinations of correlation
Let’s now change the correlation coefficient between these assets to see what happens.
That is, the shape of the investment possibilities is very sensitive to the correlation coefficient, especially when the correlation is negative.
4.3.2 Correlation matrix Brazil
Here are some correlations between selected Brazilian stocks using montly data since 2010. As you can see, correlations are all positive and only a few are close to zero.
<-c("ITUB3.SA", "WEGE3.SA", "PETR3.SA", "VALE3.SA", "ITSA3.SA" , "BBAS3.SA", "ABEV3.SA", "GGBR4.SA", "LREN3.SA", "B3SA3.SA", "UGPA3.SA","CSMG3.SA", "HYPE3.SA")
stocks <-'2010-01-01'
start <-'2022-01-01'
end <- yf_get(tickers = stocks[[1]],
df first_date = start,
last_date = end,
freq_data = "monthly")
<- df %>% select(ref_date)
df
for (i in 1:length(stocks)) {
<- yf_get(tickers = stocks[[i]],
data first_date = start,
last_date = end,
freq_data = "monthly")
<-data[complete.cases(data),]
data<- data %>% select(ref_date, ret_adjusted_prices)
datacolnames(data) <- c("ref_date", stocks[[i]] )
<- merge(df,data,by="ref_date")
df
}$ref_date <-NULL
df<- cor(df, method = c("pearson"))
corlibrary(corrplot)
corrplot(cor,method = 'number', type = 'lower' , tl.col = "black", order = 'alphabet', tl.cex = 0.75, col = COL1('Reds') , number.cex = 0.55)
Let’s see the correlations using circles to improve the visuals.
corrplot(cor,method = 'circle', type = 'lower' , tl.col = "black", order = 'alphabet', tl.cex = 0.75, col = COL1('Reds') )
4.4 Efficient frontier
4.4.1 Minimum variance portfolio
Before we create a graph of the efficient frontier using real data, let’s go back to fabricated data to visualize the efficient frontier and the minimum variance portfolio.
Below, you can see clearly that the portfolio closest to the y-axis is the one with minimum variance.This portfolio is important because it is at the beginning of the efficient frontier. All portfolios below are not optimal (because there is one portfolio in the frontier with the same level of risk but higher return).
4.4.2 The historical returns and standard deviation of two assets
Let’s now move to an efficient frontier example using real data from Brazil. First, let’s download and compute the average return and the standard deviation of returns for two Brazilian stocks.
## Ticker ER SD
## 1 BBDC3.SA 0.9905195 8.886146
## 2 PETR3.SA 1.2659083 13.074245
4.4.3 Creating a plot
This is the representation in a graph, when return is on the y-asis and the standard deviation is on the x-axis.
4.4.4 The frontier using two assets
It is not as beautiful as the graph using fabricated data because the real correlation between these assets are not low. But notice that the shape is the same as before. Also notice that we can see the minimum variance portfolio as well.
library(data.table)
<- cov(asset1$ret_adjusted_prices, asset2$ret_adjusted_prices)
cov_12 <- cor(asset1$ret_adjusted_prices, asset2$ret_adjusted_prices)
corr_12 <- seq(from = 0, to = 1, length.out = 1000)
weights <- data.table(w1 = weights ,w2 = 1 - weights)
two_assets # calculate the expected returns and standard deviations for the 1000 possible portfolios
':=' (er_p = w1 * er_1 + w2 * er_2,
two_assets[, sd_p = sqrt(w1^2 * sd_1^2 +w2^2 * sd_2^2 +2 * w1 * w2 * cov_12))]
ggplot() +
geom_point(data = two_assets, aes(x = sd_p, y = er_p, color = w1)) +
geom_point(data = data.table(sd = c(sd_1, sd_2), mean = c(er_1, er_2)),
aes(x = sd, y = mean), color = "red", size = 3, shape = 18) +
theme_bw() + ggtitle("Possible Portfolios with Two Risky Assets") +
xlab("Volatility") + ylab("Expected Returns") +
scale_y_continuous( limits = c(min(two_assets$er_p)*0.99, max(two_assets$er_p)*1.01)) +
scale_x_continuous( limits = c(min(two_assets$sd_p)*0.9, max(two_assets$sd_p)*1.1)) +
scale_color_continuous(name = expression(omega[x]))+ theme_solarized()
The correlation between these assets is 0.595, which explains the shape of the curve. Keep in mind that it is hard to find stocks with low correlations. You might find low correlation in more international markets. Not so much in Brazil. (Code inspired in: https://github.com/DavZim/Efficient_Frontier).
4.4.5 The frontier using multiple assets
<-'2010-01-01'
start <-'2022-01-01'
end <- "yearly"
freq_data <- yf_collection_get("IBOV",
df first_date = start,
last_date = end,
freq_data = freq_data)
<- unique(df$ticker)
stocks <- df %>% select(ref_date)
df for (i in 1:length(stocks)) {
<- yf_get(tickers = stocks[[i]],
data first_date = start,
last_date = end,
freq_data = freq_data)
<-data[complete.cases(data),]
data<- data %>% select(ref_date, ret_closing_prices)
datacolnames(data) <- c("ref_date", stocks[[i]] )
<- merge(df,data,by="ref_date")
df
}$ref_date <-NULL
df<- cov(df)
cov<- as.vector(colMeans(df))
ret# Random numbers to create the frontier
set.seed(100)
<- 20000
int <- data.frame((replicate(length(stocks),sample(int,rep=TRUE)) / int ))
w$sum <- rowSums(w)
wcolnames(w) <- c(stocks, 'Sum')
for (i in 1:int) {
1:length(stocks)] <- w[i, 1:length(stocks)] / w[i, ncol(w)]
w[i,
}$Sum <- NULL
w$Sum <- rowSums(w)
w$Sum <- NULL
w# creating final dataframe
<- data.frame(matrix(NA,nrow = int,ncol = 2))
port colnames(port) <- c("Return", "Sd")
for (i in 1:int) {
1] <- sum( w[i, ] * ret )
port[i,2] <- sqrt( as.matrix(w[i, ]) %*% as.matrix(cov) %*% as.matrix(t(w[i, ]) ))
port[i,
}#ggplot
ggplot(port, aes(x=Sd, y=Return)) +
geom_point(alpha=0.2) +
theme_solarized() +
xlab("Standard deviation") + ylab("Expected Return") +
labs(title = paste(int , "random portfolios - All Ibov (2010-2022, Yearly returns)") )
4.5 The Risk-free asset
Let’s include a risk-free asset now to see if anything changes. If this economy is correctly priced, the risk free asset must have an expected return lower than both risky assets. Additionally, the risk-free asset is free of risk, so it will be placed at the y-axis. (Graphs inspired in compfinezbook ).
4.5.1 Combining Asset 1 and 2 vs. Asset 1 and risk-free rate
A strategy combining asset 1 and risk-free rate seems to lose comparing to a strategy combining asset 1 and 2.
4.5.2 The tangent portfolio
Nevertheless, see what happens if you combine asset 1 and asset 2 and find the tangent portfolio.
4.7 Capital Market Line (CML)
The slope of the CML is the Sharpe ratio of the efficient portfolio. That is, the highest Sharpe ratio of all portfolios.
This portfolio is called Market portfolio. According to the CAPM, all investors should choose a portfolio on the CML, by holding some combination of the risk-free security and the market portfolio. The CML is represented in red below.
To see the Market portfolio more clearly, I adjusted the axis in the graph below.
Notice that the Market portfolio seems to be an outlier (remember, we are using real data, so outliers are expected to occur).
If we ignore the top 500 Sharpes and assume they are outlier, this is what we get.
4.7.1 CML with different risk free rates
If the risk free rate changes, all investors are expected to change their allocations.
Notice that the Market portfolio seems to be an outlier (remember, we are using real data, so outliers are expected to occur).
If we zoom in, here is what we find (again, you have to understand the effects of the outliers here and ignore them when making decisions).
So, here is the interpretation: if the risk free rate drops (let’s say from the orange to the blue line), the market portfolio switches to a safer one (“New Market Port.”).
If investors want the same level of return as before, they will need to adjust their holdings. They will buy the new market portfolio (“New Market Port.”) and will borrow at the risk free rate to get into a levered position (“After adjustment”).
The consequence of all is that, when the risk free rate drops, investor can have the same expected return as before while incurring in much less risk.
4.8 Beta
See a more detailed post here.
The graph below is a simple plot of the IBOV’s (market) and ABEV’s (a selected stock, see below) returns. We can observe that when the IBOV’s returns are positive, ABEV’s returns are likely positive too. The blue line shows this positive linear relationship. The Beta is the linear coefficient of this line (if you remember the type of function y = a + bx, the linear coefficient is b; this is what we are going to calculate below).
There are two things to understand here: 1) Beta is used in valuation models (such as the CAPM); thus it is essential to calculate it correctly; 2) when Beta is higher than 1, it means the stock is aggressive (meaning that when the market’s return is 1% the stock’s return is higher than 1%). The opposite occurs when the Beta is below 1 (meaning that when the market’s return is 1% the stock’s return is lower than 1%), and the stock is defensive.
<- 'daily'
freq.data <-'2000-01-01'
start <-Sys.Date()
end <- yf_get(tickers = "^BVSP",
ibov first_date = start,
last_date = end,
thresh_bad_data = 0.5,
freq_data = freq.data)
<- yf_get(tickers = "PETR4.SA",
asset first_date = start,
last_date = end,
thresh_bad_data = 0.5,
freq_data = freq.data )
<- ibov %>% tq_transmute(select = price_adjusted,
ret_ibov mutate_fun = periodReturn,
period = 'daily',
col_rename = 'return',
type = 'log')
<- asset %>%tq_transmute(select = price_adjusted,
ret_asset mutate_fun = periodReturn,
period = 'daily',
col_rename = 'return',
type = 'log')
<- left_join(ret_ibov, ret_asset, by = c("ref_date" = "ref_date"))
ret <-252
window $var <- roll_cov(ret$return.x, ret$return.x, width = window)
ret$cov <- roll_cov(ret$return.x, ret$return.y, width = window)
ret$beta <- ret$cov / ret$var
ret<- subset(ret, ret$beta != "NA" )
ret ggplot(ret, aes(x= return.x, y=return.y)) +
geom_point()+
geom_smooth(method=lm, se=FALSE)+
labs( y = "Daily returns PETR4", x="Daily returns IBOV",title = "Beta PETR4")+
theme(plot.title = element_text(color="darkgreen", size=18, face="bold"),
panel.background = element_rect(fill = "grey95", colour = "grey95"),
axis.title=element_text(size=12,face="bold"),
title=element_text(size=10,face="bold", color="darkgreen"),
axis.text.y = element_text(face = "bold", color = "darkgreen", size = 12),
axis.text.x = element_text(face = "bold", color = "darkgreen", size = 12))+
xlim(-0.2, 0.2) + ylim(-0.2, 0.2) + theme_solarized()
4.8.1 R-Squared
The r-squared of the linear regression above represents the proportion of the stock’s total risk that can be explained by the market risk.
##
## Call:
## lm(formula = return.y ~ return.x, data = ret)
##
## Residuals:
## Min 1Q Median 3Q Max
## -0.208393 -0.009931 -0.000161 0.009957 0.151061
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 9.084e-05 2.584e-04 0.352 0.725
## return.x 1.110e+00 1.468e-02 75.611 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.01908 on 5455 degrees of freedom
## Multiple R-squared: 0.5117, Adjusted R-squared: 0.5116
## F-statistic: 5717 on 1 and 5455 DF, p-value: < 2.2e-16
The stock’s beta is 1.11.
The standard error of the beta estimate is: 0.0147.
That is, we can use this error to infer the confidence interval where the “true” beta is.
Additionally, we can say that 51.173 percent of the stock’s risk is explained by market risk.
4.8.2 Beta hererogeneity
Notice that different stocks have different betas.
##
## Call:
## lm(formula = return.y ~ return.x, data = ret)
##
## Residuals:
## Min 1Q Median 3Q Max
## -0.164865 -0.008987 -0.000832 0.007810 0.280403
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 0.0008855 0.0002655 3.336 0.000857 ***
## return.x 0.4465415 0.0150793 29.613 < 2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.01961 on 5455 degrees of freedom
## Multiple R-squared: 0.1385, Adjusted R-squared: 0.1383
## F-statistic: 876.9 on 1 and 5455 DF, p-value: < 2.2e-16
Beta this asset is 0.447.
And around 13.849 percent of this stock’s risk is explained by market risk.
4.8.3 Beta through time
Below we observe that a stock’s beta varies over time (i.e., previous 252 trading days).
4.8.4 Betas in Brazil
Here is a graph of Betas from Brazilian companies.
<-'2018-06-01'
start <-'2022-06-01'
end <- 'monthly'
freq_data <- yf_get(tickers = "^BVSP",
ibov first_date = start,
last_date = end,
thresh_bad_data = 0.5,
freq_data = freq_data )
<- yf_collection_get("IBOV",
asset first_date = start,
last_date = end,
thresh_bad_data = 0.5,
freq_data = freq_data )
<- ibov %>%tq_transmute(select = price_adjusted,
ret_ibov mutate_fun = periodReturn,
period = 'monthly',
col_rename = 'return')
<- asset %>%
ret_asset group_by(ticker) %>%
tq_transmute(select = price_adjusted,
mutate_fun = periodReturn,
period = 'monthly',
col_rename = 'return')
<- left_join(ret_ibov, ret_asset, by = c("ref_date" = "ref_date"))
ret <- ret %>%
var group_by(ticker) %>%
summarise(var = cov(return.x, return.x))
<- ret %>%
cov group_by(ticker) %>%
summarise(cov = cov(return.x, return.y))
<- merge(cov, var, by ="ticker" )
beta $beta <- beta$cov/beta$var
betaggplot(beta, aes(x = reorder(ticker, beta ), y = beta, fill=beta) ) +
geom_bar(aes(fill = -beta), position = "dodge", stat="identity")+
theme(legend.position="none",
axis.text.y = element_blank() )+
coord_flip()+
labs(y = "Beta", x = "Stocks", title = "Betas Brazilian companies, monthly data (2018-2022)") + theme_solarized()
Here is the list of top and bottom Betas (using monthly data from 2018 until 2022). That is, there is a lot of variation in the betas of Brazilian firms.
## ticker beta
## 77 VIIA3.SA 2.117961
## 26 CVCB3.SA 2.091105
## 42 GOLL4.SA 1.965591
## 20 COGN3.SA 1.847912
## 4 AZUL4.SA 1.752127
## 80 YDUQ3.SA 1.674490
## 27 CYRE3.SA 1.656599
## 61 PRIO3.SA 1.644783
## 49 JHSF3.SA 1.608768
## 59 PETR3.SA 1.502978
## ticker beta
## 78 VIVT3.SA 0.08580102
## 69 SUZB3.SA 0.26684614
## 48 JBSS3.SA 0.38555018
## 63 RADL3.SA 0.44335512
## 23 CRFB3.SA 0.44709683
## 50 KLBN11.SA 0.47248943
## 64 RAIL3.SA 0.56232149
## 34 ENBR3.SA 0.59252119
## 75 VALE3.SA 0.60719539
## 70 TAEE11.SA 0.60751436
4.9 Security Market Line (SML)
Under the CAPM assumptions, the security market line (SML) is the line along which all individual securities should lie when plotted according to their expected return and beta. To create the graph below, I need estimates to the Brazilian Risk free rate and the Equity Risk Premium. I am using 10% as risk free rate and 16% for the Equity Risk Premium. Equity Risk Premium was based on this website.
<-'2018-06-01'
start <-'2022-06-01'
end <- 'monthly'
freq_data <- yf_get(tickers = "^BVSP",
ibov first_date = start,
last_date = end,
thresh_bad_data = 0.5,
freq_data = freq_data )
<- yf_collection_get("IBOV",
asset first_date = start,
last_date = end,
thresh_bad_data = 0.5,
freq_data = freq_data )
<- ibov %>%tq_transmute(select = price_adjusted,
ret_ibov mutate_fun = periodReturn,
period = 'monthly',
col_rename = 'return')
<- asset %>%
ret_asset group_by(ticker) %>%
tq_transmute(select = price_adjusted,
mutate_fun = periodReturn,
period = 'monthly',
col_rename = 'return')
<- left_join(ret_ibov, ret_asset, by = c("ref_date" = "ref_date"))
ret #To compute beta
<- ret %>% group_by(ticker) %>% summarise(var = cov(return.x, return.x))
var <- ret %>% group_by(ticker) %>% summarise(cov = cov(return.x, return.y))
cov <- merge(cov, var, by ="ticker" )
beta $beta <- beta$cov/beta$var
beta# Risk free rate
$rf <- 0.1
beta# Equity risk premium from here: https://ceqef.fgv.br/node/594
$erp <- 0.16
beta# expected return
$ep <- beta$rf + beta$beta * beta$erp
beta#Security Market Line
ggplot(beta, aes(x = beta, y = ep ) ) + geom_point() +
::geom_text_repel(data = beta, aes(label = ticker) , size = 2.5 , max.overlaps = Inf)+
ggrepeltheme(legend.position="none", axis.text.y = element_blank() )+
labs(y = "Expected return",
x = "Beta",
title = "SML Brazil, monthly data (2018-2022)" ,
subtitle = "Rf = 10%, Risk premium = 16%" )+ theme_solarized()