Na próxima quinta-feira, 22 de fevereiro, será divulgada, pelo COPOM (Comitê de Política Monetária), o novo valor para a meta da taxa SELIC. Há uma expectativa de que o COPOM reduza em 0.75% a SELIC, mantendo a redução divulgada na última reunião, em 11 de janeiro. Obviamente esta expectativa de redução não é consenso no mercado, há os que acreditam em uma redução mais moderada, de 0.50%, assim como os que acreditam em uma redução de 1.00%.

Há uma forma de obter esta expectativa a partir dos contratos futuros de taxas de juros negociados na BM&FBovespa. A taxa de juros CDI, divulgada pela CETIP, acompanha os movimentos da SELIC, de forma que uma redução na SELIC implica em uma redução no CDI. Na BM&FBovespa são negociados contratos futuros sobre o CDI (são os futuros de DI1—CDI de 1 dia) e nestes contratos está implícita a expectativa dos movimentos futuros da taxa CDI, logo, está implícito nestes contratos as expectativas de intervenções do COPOM.

Vou mostrar neste post as relações entre CDI e SELIC e como observar as expectativas de interveções do COPOM na SELIC presentes nos contrtos futuros de DI1.

Começemos carregando os pacotes utilizados.

library(rbmfbovespa)
library(dplyr)
library(tidyr)
library(ggplot2)
library(ggthemes)
theme_set(theme_fivethirtyeight())

Trago neste post uma novidade, o uso do pacote ggthemes para enriquecer a visualização. Neste pacote há diversos temas para ggplot que incluem: excel, wall street journal, five thirty eight e outros. Aqui vou usar o tema do FiveThirtyEight.

Para realizar esta análise foram necessários diversos arquivos da BM&FBovespa, para diversas datas, por isso fiz as funções abaixo para baixar os arquivos do FTP público da BM&FBovespa e carregar os datasets.

download_rbmfbovespa <- function(url) {
  fdest_dir <- tempdir()
  fdest <- tempfile(tmpdir = fdest_dir)
  ix <- download.file(url, fdest, quiet = TRUE)
  if (ix != 0)
    stop('Download problems: code = ', ix, ' url = ', url)
  unz_fname <- unzip(fdest, exdir = fdest_dir)
  # return unzipped file name
  unz_fname
}

download_and_read <- function(x, url, file_template, rbmfbovespa_template) {
  url_template <- file.path(url, file_template)
  url <- sprintf(url_template, format(x, '%y%m%d'))
  unz_fname <- try(download_rbmfbovespa(url))
  if (is(unz_fname, 'try-error'))
    return(NULL)
  read_marketdata(unz_fname, template = rbmfbovespa_template)
}

Eu carrego os dados desde dezembro de 2016, para mostrar o efeito nos contratos futuros que com e sem expectativas de mudança na taxa básica de juros. Para definir as data de download eu utilizo a função bizseq do pacote bizdays para pegar apenas os dias útes, que no mercado brasileiro estão no calendário divulgado pela ANBIMA, por isso uso o calendário 'Brazil/ANBIMA' em bizdays. Infelizmente existem duas datas neste calendário em que não foram publicados arquivos, são os dias: 31 de dezembro e 25 de janeiro, pois são dias em que não há negociação na Bolsa, mas estão presentes no calendário Brazil/ANBIMA por serem dias em que há divulgação da taxa SELIC pelo Banco Central.

Para executar o download nas datas definidas eu utilizo a função map do purrr, que funciona exatamente como lapply. Em seguida uso a função compact para remover os elementos com NULL que foram gerados pelas datas que não possuiam arquivos.

dates <- bizdays::bizseq('2016-12-01', '2017-02-17', 'Brazil/ANBIMA')
indics <- purrr::map(dates, download_and_read, 'ftp://ftp.bmf.com.br/IndicadoresEconomicos', 'ID%s.ex_', 'Indic')
bd_finals <- purrr::map(dates, download_and_read, 'ftp://ftp.bmf.com.br/ContratosPregaoFinal', 'BF%s.ex_', 'BD_Final')
indics <- purrr::compact(indics)
bd_finals <- purrr::compact(bd_finals)

São criadas duas listas: indics e bd_finals, com os datasets carregados dos arquivos da Bolsa utilizando os templates do rbmfbovespa. Estas listas serão processadas para que sejam extraídos valores das taxas CDI e SELIC diárias e as taxas dos contratos futuros com vencimento mais recente, o primeiro vencimento. Explico, hoje o contrato futuro com vencimento mais recente vence em 1 de fevereiro (ou no próximo dia útil), e este é o primeiro vencimento e este levantamento foi feito para cada data de referência. O primeiro vencimento é importante porque ele traz a expectativa de movimento da CDI para o curto prazo (vencimento em menos de 1 mês) onde já é possível observar os movimentos provocados pela reunião do COPOM. A função process_files seleciona os contratos futuros de DI1 e as taxas SELIC e CDI para cada uma das datas levantadas.

process_files <- function(bdi, indic) {
  # selecionar contratos futuros de DI1 dos arquivos BD_Final
  futuro <- bdi %>% filter(cod_mercadoria == 'DI1') %>%
    arrange(data_vencimento) %>%
    filter(cot_ult_negocio != 0) %>%
    filter(row_number() == 1) # selecionar o primeiro vencimento
  # obter data de referência
  ref_date <- bdi$data_geracao_arquivo[1]
  # obter SELIC e CDI (DI1) dos arquivos Indic
  cdi <- indic %>%
    filter (cod_indicador == 'DI1', data_geracao_arquivo == ref_date)
  selic <- indic %>%
    filter (cod_indicador == 'SEL', data_geracao_arquivo == ref_date)
  # data.frame com as informações necessárias
  data.frame(data_referencia = cdi$data_geracao_arquivo,
             data_vencimento = futuro$data_vencimento,
             futuro = futuro$cot_ult_negocio,
             cdi = cdi$valor_indicador,
             selic = selic$valor_indicador)
}

ls_data <- purrr::map2(bd_finals, indics, process_files)
df <- do.call(rbind, ls_data)

Obtidos os dados no data.frame df podemos observar alguns fatos já mencionados, como a relação próxima entre SELIC e CDI. No gráfico abaixo temos o histórico de SELIC e CDI desde 1 de dezembro de 2016. Note que as taxas andam juntas e no dia seguinte a reunião do COPOM de 11 de janeiro de 2017 ambas as taxas mudaram de nível caindo 0.75%, exatamente resultado da reunião do COPOM do dia 11.

ggplot(df %>% select(-data_vencimento, -futuro) %>% gather(taxa, valor, -data_referencia),
       aes(x = data_referencia, y = valor, colour = taxa, shape = taxa)) +
  geom_vline(xintercept='2017-01-11' %>% as.Date() %>% as.integer(), linetype='dashed') +
  scale_x_date() +
  geom_point()

plot of chunk expectativa_do_copom_na_curva_de_di1-4

Outro ponto interessante de observar é a dinâmica do primeiro vencimento de futuro de DI1. O gráfico abaixo apresenta a dinâmica da taxa CDI com a taxa negociada no primeiro futuro.

ggplot(df %>% select(-data_vencimento, -selic) %>% gather(taxa, valor, -data_referencia),
       aes(x = data_referencia, y = valor, colour = taxa, shape = taxa)) +
  geom_vline(xintercept='2017-01-11' %>% as.Date() %>% as.integer(), linetype='dashed') +
  geom_vline(xintercept=c('2017-01-01', '2017-02-01') %>% as.Date() %>% as.integer(), linetype='dotted') +
  scale_x_date() +
  geom_point()

plot of chunk expectativa_do_copom_na_curva_de_di1-5

Em dezembro de 2016 o primeiro vencimento vence no primeiro dia útil do ano, portanto a expectativa já está dada, não há fator que afete o nível da taxa CDI, por isso a CDI e a taxa do primeiro vencimento andam juntas. Após a virada do ano (primeira linha pontilhada), em janeiro de 2017, o primeiro futuro é o que vence em 1 de fevereiro. Note o descolamento entre a CDI e a taxa negociada no primeiro futuro, que indica uma redução na CDI para o vencimento de fevereiro. Entretanto, em 11 de janeiro ocorreu a primeira reunião do COPOM de 2017, que baixo a SELIC em 0.75% (linha tracejada). Após a reunião do COPOM o descolamento entre CDI e o primeiro vencimento desaparece novamente, pois para este período a taxa já está dada. O descolamento volta a aparecer em fevereiro quando o primeiro vencimento passa a expirar em março de 2017 e há uma reunião do COPOM em 22 de fevereiro de 2017.

Este descolamente entre a CDI e a taxa do primeiro futuro traz a expectativa do resultado da reunião do COPOM. Vou mostrar como calcular essa expectativa. Como é possível observar nos gráficos, a CDI e o primeiro futuro andam juntos quando o cenário está dado, e neste caso o cenário de taxa CDI está dado até a reunião do COPOM, ou seja, assumindo a hipótese de que a CDI não muda até a reunião do COPOM conclui-se que há espaço apenas para prever o nível da taxa após a reunião do COPOM. A taxa do futuro é uma taxa média da data de referência até o seu vencimento. Portanto, assumindo dada a taxa até a reunião do COPOM como a CDI da data de referência, é necessário calcular a taxa a termo da reunião do COPOM até o vencimento do futuro. A função calc_taxa_termo faz esse cálculo

calc_taxa_termo <- function(cdi, fut, duf, duc) {
  f1 <- (1 + cdi/100)^(duc/252)
  f2 <- (1 + fut/100)^(duf/252)
  100*((f2/f1)^(252/(duf - duc)) - 1)
}

A fórmula fica assim:

$$ 100 \left[ \left( \frac{(1 + \frac{fut}{100})^{duf/252}}{(1 + \frac{cdi}{100})^{duc/252}} \right)^{\frac{252}{duf-duc}} - 1\right] $$

Essa é a taxa a termo que está implícita na taxa negociada no primeiro futuro que vence após a reunião do COPOM. Pelo fato de a fórmula considerar os futuros com vencimento após a reunião do COPOM, é necessário remover todas as data onde o vencimento do futuro é anterior a próxima reunião do COPOM. O código abaixo calcula os dias úteis necessários no cálculo e define as datas de reunião do COPOM, em seguida remove as datas e calcula a taxa à termo usando a função calc_taxa_termo.

df_copom <- df %>% 
  mutate(
    du=bizdays::bizdays(data_referencia, data_vencimento, 'Brazil/ANBIMA'),
    data_copom=as.Date(ifelse(data_referencia <= as.Date('2017-01-11'), '2017-01-11', '2017-02-22'))
  ) %>%
  filter(data_vencimento >= data_copom) %>%
  mutate(
    du_copom=bizdays::bizdays(data_referencia, data_copom, 'Brazil/ANBIMA'),
    taxa_termo=calc_taxa_termo(cdi, futuro, du, du_copom)
  )

O gráfico abaixo apresenta uma comparação das taxas CDI, do primeiro vencimento de futuro e da taxa à termo calculada. Observe que a taxa à termo e do futuro quase colapssam na data de reunão do COPOM (linha tracejada), o que é esperado uma vez que o prazo para a reunião está diminuindo. Para o mês de fevereiro temos um padrão semelhante.

ggplot(df_copom %>% select(data_referencia, cdi, futuro, taxa_termo) %>% gather(taxa, valor, -data_referencia),
       aes(x = data_referencia, y = valor, colour = taxa, shape = taxa)) +
  geom_vline(xintercept='2017-01-11' %>% as.Date() %>% as.integer(), linetype='dashed') +
  geom_vline(xintercept=c('2017-01-01', '2017-02-01') %>% as.Date() %>% as.integer(), linetype='dotted') +
  scale_x_date() +
  geom_point()

plot of chunk expectativa_do_copom_na_curva_de_di1-8

A mudança na taxa apresentada na reunião do COPOM é a diferença entre a taxa a termo e a taxa CDI. Calculando esta diferença observamos que as expectativas estavam pouco acima de 0.50% de redução, e para a reunião de fevereiro temos expectativas em torno de 0.50%, houve até um dia que fechou em 0.60%. O gráfico abaixo apresenta esta diferença.

ggplot(data = df_copom, aes(x = data_referencia, y = taxa_termo - cdi)) +
  geom_point(colour = 'red') +
  geom_vline(xintercept='2017-01-11' %>% as.Date() %>% as.integer(), linetype='dashed') +
  geom_hline(yintercept=-0.5)

plot of chunk expectativa_do_copom_na_curva_de_di1-9

Uma pergunta que fica após a observação destes resultados é, mesmo após a redução de 0.75% em janeiro, porque a expectativa está em 0.50%? Bem, os dados observados são referentes ao fechamento dos contratos futuros, obviamente ao longo do dia são negociados valores maiores e menores, que podem refletir movimentos de 0.75%, assim como 0.25%. Por exemplo, poderíamos repetir essa análise para as máximas e mínimas do dia.

Outro ponto interessante é a observação dos demais vencimentos, pois neles há a expectativa das demais reuniões. Por exemplo, após cada reunião do COPOM podemos migrar do primeiro para o segundo vencimento, pois a taxa do primeiro já está dada e toda a expectativa da próxima reunião está no segundo.