Usaremos o conjunto de pacotes "tidyverse" para realizar algumas operações, sobretudo utilizando recursos gráficos.
##### ----- Importando Pacotes ------ #####
library("tidyverse")
## Warning: package 'ggplot2' was built under R version 4.0.5
## Warning: package 'readr' was built under R version 4.0.4
library("tidyquant")
library("Quandl")
library("timetk")
## Warning: package 'timetk' was built under R version 4.0.5
library("devtools")
library("readxl")
library("AER")
library("fBasics")
##### ------ Importando Dados ------ #####
# Cotações
ativos <- c("VALE3.SA", "^BVSP") # Localizar e Substituir o nome do ativo por outros
inicio <- as.Date("2010-01-04")
fim <- as.Date("2020-12-30")
cotacao <- ativos %>%
tq_get(get = "stock.prices", from = inicio, to = fim) %>%
mutate(adjusted = na.locf(adjusted))
# Retornos
df_retornos <- cotacao %>%
group_by(symbol) %>%
select(date, adjusted) %>%
tq_transmute(select = adjusted,
mutate_fun = periodReturn,
period = "daily",
type = "arithmetic",
col_rename = "retorno") %>%
spread(key = symbol, value = retorno)
##### ------ Importando a taxa SELIC da API do BACEN (de 2010 até hoje) ------ #####
Quandl.api_key('TC1ow5j6G7s4SFHTzgDz') # Acrescentando a chave da API - Comando necessário pra acessar o Quandl
selic_diaria <- as.data.frame(Quandl("BCB/11", type = "xts")/100) # Importando a serie do selic do Bacen
df_selic <- selic_diaria %>%
rownames_to_column() %>%
tibble() %>%
rename("date" = rowname, "selic" = V1) %>%
mutate(selic = na.locf(selic))
df_selic$date <- as.Date(df_selic$date)
# acréscimo na base de dados
df_retornos <- df_retornos %>% left_join(df_selic, by = "date")
##### ------ Montando regressão do CAPM ------ #####
df_capm <- df_retornos %>%
transmute("date" = date,
"Rp-Rf" = VALE3.SA - selic,
"Rm-Rf" = `^BVSP` - selic)
capm1 <- lm(df_capm$`Rp-Rf` ~ df_capm$`Rm-Rf`)
summary(capm1)
##
## Call:
## lm(formula = df_capm$`Rp-Rf` ~ df_capm$`Rm-Rf`)
##
## Residuals:
## Min 1Q Median 3Q Max
## -0.221786 -0.010506 -0.000477 0.010061 0.114318
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 0.0003861 0.0003983 0.969 0.332
## df_capm$`Rm-Rf` 1.0576230 0.0250455 42.228 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.02078 on 2719 degrees of freedom
## (6 observations deleted due to missingness)
## Multiple R-squared: 0.3961, Adjusted R-squared: 0.3959
## F-statistic: 1783 on 1 and 2719 DF, p-value: < 2.2e-16
##### ------ Montando regressão de Fama-French ------ #####
MKT_Factor <- read_excel("D:/Dropbox/UFMG/Programação/R/Métodos/Ativ Prát 3/Market_Factor.xls")
## New names:
## * `` -> ...6
HML_Factor <- read_excel("D:/Dropbox/UFMG/Programação/R/Métodos/Ativ Prát 3/HML_Factor.xls")
## New names:
## * `` -> ...6
SMB_Factor <- read_excel("D:/Dropbox/UFMG/Programação/R/Métodos/Ativ Prát 3/SMB_Factor.xls")
## New names:
## * `` -> ...6
MKT <- MKT_Factor %>% select(date, Rm_minus_Rf) %>% rename("MKT" = Rm_minus_Rf)
MKT$date <- as.Date(MKT$date)
HML <- HML_Factor %>% select(date, HML)
HML$date <- as.Date(HML$date)
SMB <- SMB_Factor %>% select(date, SMB)
SMB$date <- as.Date(SMB$date)
fatores <- df_capm %>%
left_join(HML, by = "date") %>%
left_join(SMB, by = "date") %>%
left_join(MKT, by = "date") %>%
select(-`Rm-Rf`)
ffrench1 <- lm(`Rp-Rf` ~ MKT + HML + SMB, data = fatores)
summary(ffrench1)
##
## Call:
## lm(formula = `Rp-Rf` ~ MKT + HML + SMB, data = fatores)
##
## Residuals:
## Min 1Q Median 3Q Max
## -0.230730 -0.010448 -0.000378 0.009950 0.133562
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 0.0002042 0.0003985 0.512 0.608
## MKT 0.9891863 0.0294807 33.554 < 2e-16 ***
## HML 0.7748240 0.0551971 14.037 < 2e-16 ***
## SMB -0.4052043 0.0507856 -7.979 2.16e-15 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.02077 on 2714 degrees of freedom
## (9 observations deleted due to missingness)
## Multiple R-squared: 0.3976, Adjusted R-squared: 0.3969
## F-statistic: 597.1 on 3 and 2714 DF, p-value: < 2.2e-16
# A) Teste de normalidade dos resíduos de Jarque-Bera
jarqueberaTest(residuals(capm1))
##
## Title:
## Jarque - Bera Normalality Test
##
## Test Results:
## STATISTIC:
## X-squared: 6999.3136
## P VALUE:
## Asymptotic p Value: < 2.2e-16
##
## Description:
## Mon Jul 12 22:59:18 2021 by user: Bruno Marcelino
jarqueberaTest(residuals(ffrench1))
##
## Title:
## Jarque - Bera Normalality Test
##
## Test Results:
## STATISTIC:
## X-squared: 9779.084
## P VALUE:
## Asymptotic p Value: < 2.2e-16
##
## Description:
## Mon Jul 12 22:59:18 2021 by user: Bruno Marcelino
# tanto para o modelo de Fama-French quanto para o CAPM, o teste rejeita a hipótese nula de que os resíduos seguem uma distribuição normal.
# B) Medidas de Influência das observações
outliers_capm <- influence.measures(capm1)
outliers_ffrench <- influence.measures(ffrench1)
# Gráficos de identificação dos outliers
par(mfrow=c(2,3))
plot(ffrench1, which=1:6)
# para o modelo de Fama-French, o principal gráfico (influência em relação a resíduos padronizados) indica que não há outliers tão influentes sobre o modelo, o que pode caracterizar sua robustez (pois envolve mais variáveis)
par(mfrow=c(2,3))
plot(capm1, which=1:6)
# para o modelo CAPM, houveram diversos resíduos que ultrapassaram o limite padrão estabelecido para a influência, interferindo no ajuste do modelo.
# C) Testes de heterocedasticidade de Breusch-Pagan
bptest(capm1)
##
## studentized Breusch-Pagan test
##
## data: capm1
## BP = 0.79666, df = 1, p-value = 0.3721
# o modelo CAPM aceita a hipótese nula do teste de Breusch-Pagan, o que o caracteriza como homocedástico
bptest(ffrench1)
##
## studentized Breusch-Pagan test
##
## data: ffrench1
## BP = 12.19, df = 3, p-value = 0.00676
# o modelo de Fama-French rejeita a hipótese nula, portanto há indícios de heterocedasticidade
# D) Testes de Correlação Serial
# Teste de Durbin-Watson
dwtest(capm1)
##
## Durbin-Watson test
##
## data: capm1
## DW = 1.9032, p-value = 0.005819
## alternative hypothesis: true autocorrelation is greater than 0
dwtest(ffrench1)
##
## Durbin-Watson test
##
## data: ffrench1
## DW = 1.9333, p-value = 0.04096
## alternative hypothesis: true autocorrelation is greater than 0
# O teste de Durbin-Watson em ambos os modelos indica que há correlação serial, por rejeitarem a hipótese nula.
# Teste de Breusch-Godfrey
bgtest(capm1)
##
## Breusch-Godfrey test for serial correlation of order up to 1
##
## data: capm1
## LM test = 6.3642, df = 1, p-value = 0.01164
# O teste de Breusch-Godfrey indica que há correlação serial no modelo CAPM, rejeitando a hipótese nula.
bgtest(ffrench1)
##
## Breusch-Godfrey test for serial correlation of order up to 1
##
## data: ffrench1
## LM test = 2.9722, df = 1, p-value = 0.08471
# Já o teste de Breusch-Godfrey indica, por pouco, que não haveria correlação serial no modelo Fama-French, aceitando a hipótese nula.
# Gerando CAPM e Fama-French sem outliers
outliers_capm <- which(apply(influence.measures(capm1)$is.inf, 1, any))
outliers_ffrench <- which(apply(influence.measures(ffrench1)$is.inf, 1, any))
capm2 <- lm(`Rp-Rf` ~ `Rm-Rf`, data = df_capm[-outliers_capm, ])
summary(capm2)
##
## Call:
## lm(formula = `Rp-Rf` ~ `Rm-Rf`, data = df_capm[-outliers_capm,
## ])
##
## Residuals:
## Min 1Q Median 3Q Max
## -0.222470 -0.009627 -0.000059 0.009771 0.076152
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -0.0001153 0.0003430 -0.336 0.737
## `Rm-Rf` 1.0063167 0.0261028 38.552 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.01714 on 2496 degrees of freedom
## (6 observations deleted due to missingness)
## Multiple R-squared: 0.3732, Adjusted R-squared: 0.373
## F-statistic: 1486 on 1 and 2496 DF, p-value: < 2.2e-16
ffrench2 <- lm(`Rp-Rf` ~ MKT + HML + SMB, data = fatores[-outliers_ffrench, ])
summary(ffrench2)
##
## Call:
## lm(formula = `Rp-Rf` ~ MKT + HML + SMB, data = fatores[-outliers_ffrench,
## ])
##
## Residuals:
## Min 1Q Median 3Q Max
## -0.231328 -0.009729 0.000040 0.009623 0.089260
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -0.0003399 0.0003437 -0.989 0.323
## MKT 0.8700760 0.0325098 26.763 < 2e-16 ***
## HML 0.8026690 0.0547316 14.666 < 2e-16 ***
## SMB -0.3359015 0.0493906 -6.801 1.3e-11 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.01713 on 2480 degrees of freedom
## (9 observations deleted due to missingness)
## Multiple R-squared: 0.3725, Adjusted R-squared: 0.3717
## F-statistic: 490.6 on 3 and 2480 DF, p-value: < 2.2e-16
# A) Teste de normalidade dos resíduos de Jarque-Bera
jarqueberaTest(residuals(capm2))
##
## Title:
## Jarque - Bera Normalality Test
##
## Test Results:
## STATISTIC:
## X-squared: 14186.0263
## P VALUE:
## Asymptotic p Value: < 2.2e-16
##
## Description:
## Mon Jul 12 22:59:21 2021 by user: Bruno Marcelino
jarqueberaTest(residuals(ffrench2))
##
## Title:
## Jarque - Bera Normalality Test
##
## Test Results:
## STATISTIC:
## X-squared: 20108.7444
## P VALUE:
## Asymptotic p Value: < 2.2e-16
##
## Description:
## Mon Jul 12 22:59:21 2021 by user: Bruno Marcelino
# Os dois testes indicam que os resíduos de ambos os modelos sem outliers seguem uma distribuição normal, consertando o problema anterior.
# B) Medidas de Influência das observações
# Gráficos de identificação dos outliers
par(mfrow=c(2,3))
plot(capm2, which=1:6)
par(mfrow=c(2,3))
plot(ffrench2, which=1:6)
# ainda que tenhamos retirado os outliers obtidos com a função influence.measures, os dois modelos apresentaram no mínimo três dados que apresentaram distância de Cook mais alta que os demais. O restante dos gráficos relata o mesmo.
# C) Testes de heterocedasticidade de Breusch-Pagan
bptest(capm2)
##
## studentized Breusch-Pagan test
##
## data: capm2
## BP = 0.26599, df = 1, p-value = 0.606
bptest(ffrench2)
##
## studentized Breusch-Pagan test
##
## data: ffrench2
## BP = 3.9857, df = 3, p-value = 0.263
# Os dois testes indicam a ausência do problema da heterocedasticidade nos dois modelos, o que é um grande benefício para o ajuste da regressão de maneira próxima da realidade.
# D) Testes de Correlação Serial
# Teste de Durbin-Watson
dwtest(capm2)
##
## Durbin-Watson test
##
## data: capm2
## DW = 2.066, p-value = 0.9505
## alternative hypothesis: true autocorrelation is greater than 0
dwtest(ffrench2)
##
## Durbin-Watson test
##
## data: ffrench2
## DW = 1.9965, p-value = 0.4645
## alternative hypothesis: true autocorrelation is greater than 0
# Entretanto, o modelo der Durbin-Watson indica que há altíssima correlação serial nos modelos, sobretudo no CAPM (95% de aceitação da hipótese nula). Ainda que seja um problema, era um problema a se esperar dado que se tratam de dados de séries temporais, e o retorno passado tem grande influência no retorno futuro.
# Teste de Breusch-Godfrey
bgtest(capm2)
##
## Breusch-Godfrey test for serial correlation of order up to 1
##
## data: capm2
## LM test = 2.7254, df = 1, p-value = 0.09877
bgtest(ffrench2)
##
## Breusch-Godfrey test for serial correlation of order up to 1
##
## data: ffrench2
## LM test = 0.0046572, df = 1, p-value = 0.9456
# Já no teste de Breusch-Godfrey, os modelos apresentaram ausência de correlação serial após a retirada dos outliers, tendo em vista que não rejeitaram a hipótese nula. No caso do CAPM foi por pouco, mas para o Fama-French houveram mudanças significativas no ajuste do que fizeram o teste apresentar p-valor de 95%)
### Correção da heterocedasticidade -> erros-padrão de White ###
# sem correção
summary(capm2)
##
## Call:
## lm(formula = `Rp-Rf` ~ `Rm-Rf`, data = df_capm[-outliers_capm,
## ])
##
## Residuals:
## Min 1Q Median 3Q Max
## -0.222470 -0.009627 -0.000059 0.009771 0.076152
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -0.0001153 0.0003430 -0.336 0.737
## `Rm-Rf` 1.0063167 0.0261028 38.552 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.01714 on 2496 degrees of freedom
## (6 observations deleted due to missingness)
## Multiple R-squared: 0.3732, Adjusted R-squared: 0.373
## F-statistic: 1486 on 1 and 2496 DF, p-value: < 2.2e-16
summary(ffrench2)
##
## Call:
## lm(formula = `Rp-Rf` ~ MKT + HML + SMB, data = fatores[-outliers_ffrench,
## ])
##
## Residuals:
## Min 1Q Median 3Q Max
## -0.231328 -0.009729 0.000040 0.009623 0.089260
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -0.0003399 0.0003437 -0.989 0.323
## MKT 0.8700760 0.0325098 26.763 < 2e-16 ***
## HML 0.8026690 0.0547316 14.666 < 2e-16 ***
## SMB -0.3359015 0.0493906 -6.801 1.3e-11 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.01713 on 2480 degrees of freedom
## (9 observations deleted due to missingness)
## Multiple R-squared: 0.3725, Adjusted R-squared: 0.3717
## F-statistic: 490.6 on 3 and 2480 DF, p-value: < 2.2e-16
# com correção
coeftest(capm2, vcov. = vcovHAC)
##
## t test of coefficients:
##
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -0.00011528 0.00032998 -0.3493 0.7269
## `Rm-Rf` 1.00631675 0.03287387 30.6115 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
coeftest(ffrench2, vcov. = vcovHAC)
##
## t test of coefficients:
##
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -0.00033991 0.00034071 -0.9976 0.3186
## MKT 0.87007602 0.04229203 20.5730 < 2.2e-16 ***
## HML 0.80266903 0.06151166 13.0491 < 2.2e-16 ***
## SMB -0.33590147 0.06132341 -5.4775 4.747e-08 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
# Nesse teste de erros-padrão de White, é gerado um modelo que atribui menores pesos a informações discrepantes. Sendo assim, são corrigidos os valores dos coeficientes e os seus erros-padrão, tornando-o mais robusto (ou seja, menos sensível à presença de outliers).
### Correção da normalidade dos resíduos -> dummy que equivale a 1 se a observação for identificada como um outlier ###
df_capm <- df_capm %>%
mutate("position" = seq(1:length(date))) %>%
mutate("D1" = ifelse(position %in% outliers_capm, 1, 0))
fatores <- fatores %>%
mutate("position" = seq(1:length(date))) %>%
mutate("D2" = ifelse(position %in% outliers_capm, 1, 0))
# Análise da regressão com dummy que só vale para os outliers
capm3 <- lm(`Rp-Rf` ~ `Rm-Rf` + D1, data = df_capm)
summary(capm3)
##
## Call:
## lm(formula = `Rp-Rf` ~ `Rm-Rf` + D1, data = df_capm)
##
## Residuals:
## Min 1Q Median 3Q Max
## -0.221259 -0.010287 -0.000292 0.010373 0.108657
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -0.0001164 0.0004144 -0.281 0.779
## `Rm-Rf` 1.0586713 0.0249690 42.399 < 2e-16 ***
## D1 0.0061311 0.0014476 4.235 2.36e-05 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.02071 on 2718 degrees of freedom
## (6 observations deleted due to missingness)
## Multiple R-squared: 0.4, Adjusted R-squared: 0.3996
## F-statistic: 906.1 on 2 and 2718 DF, p-value: < 2.2e-16
ffrench3 <- lm(`Rp-Rf` ~ MKT + HML + SMB + D2, data = fatores)
summary(ffrench3)
##
## Call:
## lm(formula = `Rp-Rf` ~ MKT + HML + SMB + D2, data = fatores)
##
## Residuals:
## Min 1Q Median 3Q Max
## -0.230059 -0.010303 -0.000156 0.010232 0.127411
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -0.0003120 0.0004145 -0.753 0.452
## MKT 0.9932494 0.0293984 33.786 < 2e-16 ***
## HML 0.7704764 0.0550243 14.002 < 2e-16 ***
## SMB -0.3923388 0.0507044 -7.738 1.42e-14 ***
## D2 0.0063151 0.0014497 4.356 1.37e-05 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.0207 on 2713 degrees of freedom
## (9 observations deleted due to missingness)
## Multiple R-squared: 0.4018, Adjusted R-squared: 0.4009
## F-statistic: 455.5 on 4 and 2713 DF, p-value: < 2.2e-16
# Concluimos que os outliers não estão contidos nos padrões gerais da amostra, pois a variável dummy é significativa. Isso significa que sua presença não contribui para as especificações do modelo, estando muito fora dos padrões gerais da amostra (podendo ser incluída como outra variável e assim melhorando o ajuste do modelo por ser significativa)
### Correção da correlação serial
# Segundo os testes realizados em modelos que retiraram os outliers, pudemos concluir que a correlação serial já foi corrigida. Entretanto, a função "coeftest" gera um modelo corrigido para esse problema com o parâmetro (vcov. = vcovHAC)
O modelo Fama-French se mostrou melhor que o CAPM no sentido de incluir a mesmas variável (prêmio de risco do mercado) em junção com outras que se demonstraram também significantes ao longo do processo, como o SMB e o HML. Além disso, vale ressaltar que vale a pena estipular sua regressão sem intercepto pois ele já se apresenta estatisticamente igual a zero. Seguem abaixo os problemas enfrentados por nós no módulo e suas respectivas conclusões:
A retirada dos outliers do modelo Fama-French tornou seus resultados de testes de heteroedasticidade altamente conclusivos para homoedasticidade, o que é ótimo e um pressuposto para a regressão de variáveis pelo método linear e de mínimos quadrados.
Os testes indicaram que os modelos sem outliers indicam confirmação do pressuposto de normalidade dos resíduos no novo modelo, o que é um forte benefício para seu ajuste à vida real.
Os testes não forneceram estatísticas realmente conclusivas de que o modelo sem outliers apresentou ou não correlação serial, o que ainda deve ser estudado mais a fundo por nós, os responsáveis pelos métodos econométricos. Como não foi possível armazenar os dados do modelo que corrige esse problema por meio do método de erros-padrão de White, a correção ou não da correlação (que deveria ter sido feita) ainda é uma incógnita de forma empírica.