A qualidade dos dados divulgados no mercado financeiro de uma forma geral é ruim. Os dados Brasileiros são muito ruins. As empresas que geram informação em nosso mercado não possuem processos adequados e robustos para a divulgação dos dados, sem falar nas ferramentas utilizadas nos processos. O mercado financeiro, de uma forma geral, é dominado pelo Excel de forma que uma quantidade significativa de informação (quase 100%) passa por planilhas e em diversos casos o formato dos dados é a própria planilha. É um cenário thrash onde a falta de estrutura contribui para a proliferação do risco operacional que é responsável por diversas inconsistências encontradas nos dados.

Hoje eu estava fazendo uma análise da série temporal do índice BOVESPA em frequência mensal e de repente identifiquei uma inconsistência nos dados. Os dados foram obtidos no site de séries temporais do Banco Central do Brasil (BCB), ou seja, um lugar sério. Eu utilizei o Quandl para baixar estes dados e já falei a respeito dele em outro post sobre como obter dados estruturados utilizando Python.

Batendo as informações

O BCB divulga as séries mensais do índice e da variação percentual do índice. No Quandl são as séries BCB/7845 e BCB/7832, respectivamente e é possível baixar as duas séries no mesmo DataFrame pandas com o método Quandl.get.

In [97]:
import numpy as np
import pandas as pd
import Quandl
import matplotlib.pyplot as plt
import seaborn as sb
%pylab inline
sb.set_context("talk")
Populating the interactive namespace from numpy and matplotlib
In [98]:
data = Quandl.get(["BCB/7845", "BCB/7832"])

Observando os dados já encontrei a primeira inconsistência. O método head retorna o começo da série (assim com tail traz o final) e aqui é possível observar que a série do índice é maior do que a série das diferenças percentuais. O que é muito estranho dado que ambas as séries são referentes ao mesmo ativo.

In [99]:
data.head()
Out[99]:
BCB.7845 - Value BCB.7832 - Value
Date
1983-07-31 62064 NaN
1983-08-31 67535 NaN
1983-09-30 90427 NaN
1983-10-31 1258 NaN
1983-11-30 1678 NaN

Dado que ambas as séries são referentes ao mesmo ativo resolvi calcular a variação percentual do índice para bater com a série de variação fornecida pelo BCB.

Olhando o rabo da série temos:

In [100]:
data.tail(10)
Out[100]:
BCB.7845 - Value BCB.7832 - Value
Date
2014-02-28 47094 -1.14
2014-03-31 50414 7.05
2014-04-30 51626 2.40
2014-05-31 51239 -0.75
2014-06-30 53168 3.76
2014-07-31 55829 5.00
2014-08-31 61288 9.78
2014-09-30 54115 -11.70
2014-10-31 54628 0.95
2014-11-30 54724 0.18

Agora eu calculo a variação percentual da série do índice, o que utilizando o pandas é bem fácil. Para chegar aos mesmo valores fornecidos pelo BCB eu preciso calcular a variação percentual, multiplicar por 100 e arredondar na segunda casa decimal.

In [101]:
vari = data['BCB.7845 - Value'].diff()/data['BCB.7845 - Value']
np.round(100*vari, 2).tail(10)
Out[101]:
Date
2014-02-28    -1.16
2014-03-31     6.59
2014-04-30     2.35
2014-05-31    -0.76
2014-06-30     3.63
2014-07-31     4.77
2014-08-31     8.91
2014-09-30   -13.26
2014-10-31     0.94
2014-11-30     0.18
Name: BCB.7845 - Value, dtype: float64

Bem, olhando assim já dá pra ver que as informações não batem. Elas quase batem, mas aqui quase não é suficiente afinal estamos lidando com informação quantitativa e esse cálculo não tem incerteza, ele tem que bater.

Vamos colocar todas as colunas no mesmo DataFrame.

In [102]:
data['Perc.change'] = np.round(100*vari, 2)
In [103]:
data.tail(10)
Out[103]:
BCB.7845 - Value BCB.7832 - Value Perc.change
Date
2014-02-28 47094 -1.14 -1.16
2014-03-31 50414 7.05 6.59
2014-04-30 51626 2.40 2.35
2014-05-31 51239 -0.75 -0.76
2014-06-30 53168 3.76 3.63
2014-07-31 55829 5.00 4.77
2014-08-31 61288 9.78 8.91
2014-09-30 54115 -11.70 -13.26
2014-10-31 54628 0.95 0.94
2014-11-30 54724 0.18 0.18

Só pra avaliar o tamanho do estrago vamos calcular a diferença das variações percentuais e olhar o histograma e aí vamos ter uma idéia do comportamento dessa diferença.

Aqui eu já tenho uma suspeita: diferenças desse tipo aparecem quando há arredondamento nos cálculos e dessa maneira as diferenças deveriam ser da ordem de grandeza da precisão adotada, que neste caso é de 0.01. Não acho que seja isso, pois na tabela acima observamos diferenças muito maiores como em 2014-09-30. De qualquer forma, vamos ao histograma.

In [104]:
diff_change = data['BCB.7832 - Value'] - data['Perc.change']
diff_change.head(10)
Out[104]:
Date
1983-07-31   NaN
1983-08-31   NaN
1983-09-30   NaN
1983-10-31   NaN
1983-11-30   NaN
1983-12-31   NaN
1984-01-31   NaN
1984-02-29   NaN
1984-03-31   NaN
1984-04-30   NaN
dtype: float64

Temos diversos valores nulos devido a diferença de tamanho das séries, vamos remover os valores nulos antes de fazer o histograma.

In [105]:
diff_change = diff_change[~diff_change.isnull()]
In [106]:
diff_change.hist(bins=50);

Os dados apresentam um conjunto de valores extremos que não deveriam estar aí. Só pra olhar o problema mais de perto resolvi fazer alguns agrupamentos pra entender melhor o que acontece. Comecei contando a quantidade de valores negativos e positivos e já vi que a maioria é positiva.

In [107]:
diff_change.groupby(diff_change >= 0).count()
Out[107]:
False      1
True     334
dtype: int64

Isso já indica um viés na amostra o que já é informação para a análise do problema, pois as diferenças calculadas são maiores (em módulo) do que as diferenças divulgadas. Ou seja, se os dados divulgados são confiáveis, as diferenças divulgadas tiveram um desconto, e assumindo isso me leva a hipótese de que a série de índice não foi ajustada (ou corrigida) para o tratamento de eventos corportativos (eventos de pagamentos de dividendos, juros sobre capital e agrupamento ou desmembramento do lote de ações negociado). O índice é composto por ações e o valor destas ações é afetado por eventos corporativos que descontam ou incorporam valor ao papel negociado. Os eventos de desmembramento, pagamento de dividendos ou juros sobre capital próprio, são os mais comuns e descontam valor do papel negociado, já o evento de agrupamento aumenta o valor do papel negociado. No entanto é importante entender que o dono de ações não perde nem ganha com estes eventos, pois o número de ações é ajustado de maneira que o valor da companhia não seja alterado (nos eventos de agrupamento e desmembramento) ou o acionista recebe em caixa (nos eventos de pagamento de dividendos e juros sobre capital).

Como os eventos mais comuns são: pagamento de dividendos e juros sobre capital e desmembramento, temos parte do problema explicado. O que ainda me incomoda é saber que em 335 pontos (quase 28 anos) a maioria dos impactos observados é devido a eventos de down sizing. Por outro lado estes dados são mensais, o que também dificulta a análise.

Bem, o diagnóstico até agora é de que série do índice não tem o seu valor ajustado, o que introduz variações muito maiores do que as que podem ter ocorrido de fato. Dessa forma os valores do índice divulgados são os valores observados nas datas de fim de mês. Assumindo que a série de diferenças percentuais está correta é possível escolher qualquer data e reconstruir o índice com base nas diferenças. Entretanto, ainda resta verifica se a série de diferenças percentuais é coerente e para verificar isso vou olhar o histograma da série.

In [108]:
data['BCB.7832 - Value'].hist(bins=25);

Observamos algumas variações grandes, no entanto é importante lembrar que a frequência de observação é mensal.

Bem, como fiquei incomodado com alguns retornos grandes resolvi olhar a série crua.

In [109]:
data['BCB.7832 - Value'].plot(linewidth=1, style='k-');

Realmente no período nebuloso pré-1994 tivemos variações grandes. Este período é marcado pela hiper-inflação que talvez possa explicar estas oscilações extremas. Para introduzir inflação na análise temos um porém, temos diversos índices de inflação de forma que não sei ao certo qual é o melhor para esta análise e mesmo os índices sofrem alterações na sua metodologia o que também compromente a avaliação histórica. Atualmente o IPCA é utilizado pelo governo para o acompanhamento da meta de inflação e por isso foi escolhido para a análise.

Deflacionando o IBOVESPA

Vamos começar baixando a série mensal do IPCA no Quandl.

In [110]:
ipca = Quandl.get("BCB/433")
In [111]:
ipca.head()
Out[111]:
Value
Date
1980-01-31 6.62
1980-02-29 4.62
1980-03-31 6.04
1980-04-30 5.29
1980-05-31 5.70
In [112]:
ipca.tail()
Out[112]:
Value
Date
2014-07-31 0.01
2014-08-31 0.25
2014-09-30 0.57
2014-10-31 0.42
2014-11-30 0.51

Felizmente temos uma série longa (desde jan/80). Vamos juntar esta série com os dados de BOVESPA, mas antes vamos remover a série de variações calculadas, pois já considero este problema entendido e não vou mais precisar dela.

In [113]:
data = data.loc[:, ['BCB.7845 - Value', 'BCB.7832 - Value']]
In [115]:
data = pd.merge(ipca, data, how='left', left_index=True, right_index=True)

A função merge do pandas junta os e funciona como um join do SQL. Aqui eu passo os DataFrame e informo que eu quero utilizar os índices como chaves na junção. O parâmetro how='left' me dá algo como um left-join onde eu trago todos os índices de ipca para o novo DataFrame.

In [116]:
data.head()
Out[116]:
Value BCB.7845 - Value BCB.7832 - Value
Date
1980-01-31 6.62 NaN NaN
1980-02-29 4.62 NaN NaN
1980-03-31 6.04 NaN NaN
1980-04-30 5.29 NaN NaN
1980-05-31 5.70 NaN NaN

Como observamos as segunda e terceira colunas são nulas no começo da série por não possuirem informação neste período. Vamos aproveitar para renomear as séries, pois estes nomes dificultam a análise.

In [117]:
data.columns = ['IPCA', 'IBOVESPA', 'IBOVESPA_D']
In [118]:
data.tail()
Out[118]:
IPCA IBOVESPA IBOVESPA_D
Date
2014-07-31 0.01 55829 5.00
2014-08-31 0.25 61288 9.78
2014-09-30 0.57 54115 -11.70
2014-10-31 0.42 54628 0.95
2014-11-30 0.51 54724 0.18

Como podemos observar o fim da série está completo e o DataFrame tem suas colunas com nomes que fazem mais sentido.

Na análise de dados as colunas de tabelas (ou DataFrame em nosso caso), assim como as variáveis em um programa, devem fazer sentido. As colunas são as variáveis de fato e são o objeto de interação. Por isso é fundamental que todo data scientist trabalhe com os dados de forma higiênica, afinal uma nomenclatura confusa gera uma análise confusa e consequentemente um resultado que fica difícil de explicar. Uma análise higiênica torna-se fácil de explicar e todo o processo de compreenssão é suave e homogêneo, por que de uma forma geral as coisas simplesmente fazem sentido.

Bem, continuando, temos IBOVESPA_D com a variação percentual mensal do índice e IPCA é a variação percentual mensal da índice de preços. A idéia aqui é deflacionar o índice, ou seja, obter o ganho real do índice no período, pois se o índice em jul/2014 teve uma variação positiva de 5%, essa variação foi corroída pela inflação de 1%, então o ganho real não foi 5%, é algo menor pois houve perda do poder de comprar devido ao aumento de preços registrado pelo IPCA. A relação entre ganho real e inflação pode ser explicada pela Fórmula de Fisher que aqui vamos utilizar da seguinte maneira:

$$ 1 + IBOVESPA\_D\_DEFL = \frac{1 + IBOVESPA\_D}{1 + IPCA} $$

onde IBOVESPA_D_DEFL é a variação do índice descontada da inflação, ou seja, deflacionada.

In [127]:
ibovespa_d_defl = ((1 + data["IBOVESPA_D"]/100)/(1 + data["IPCA"]/100) - 1)*100

Para facilitar a visualização vamos juntar tudo em 1 DataFrame antes de gerar os gráficos.

In [128]:
ibovespa_d = pd.DataFrame({'IBOVESPA_DEFL': ibovespa_d_defl, 'IBOVESPA_D': data['IBOVESPA_D']}, index=ibovespa_d_defl.index)

Como ibovespa_d_defl e IBOVESPA_D foram gerados por colunas de um mesmo DataFrame então eles possuem os mesmos índices, de forma que para juntar ambos eu criei um novo DataFrame utilizando as séries como colunas e informando qual índice utilizar.

Abaixo apresento as duas séries, IBOVESPA_D e IBOVESPA_DEFL. Podemos observar que os retornos positivos foram reduzidos no período de maior inflação, assim como o pior retorno negativo foi amplificado.

In [129]:
ibovespa_d.plot(subplots=True, ylim=(-120, 120), figsize=(18, 9), layout=(1, 2), linewidth=1);

Olhando o histograma das duas séries observa-se que as variações deflacionadas estão mais simétricas. De fato, considerando a frequência dos dados este é um resultado esperado, pois observando dados diários temos variações independentes e sendo os dados mensais somatórios de dados diários este é um resultado esperado (para os mais atentos vale a pena dar uma olhada no Teorema do Limite Central).

In [130]:
subplots(1, 2, figsize=(18,9))
subplot(121)
ibovespa_d["IBOVESPA_D"].hist(bins=25);
subplot(122)
ibovespa_d["IBOVESPA_DEFL"].hist(bins=25);

Apenas para confirmar a observação acima, eu calculo a assimetria das séries (skewness) e realmente observo que houve uma redução nesta medida indicando uma distribuição menos assimétrica (ou mais simétrica).

In [131]:
ibovespa_d.skew()
Out[131]:
IBOVESPA_D       1.689262
IBOVESPA_DEFL    0.518155
dtype: float64

Conclusão

Eu comecei este post com o título: No Brasil até o passado é duvidoso, que é uma sentença atribuída ao Ministro da Fazendo Pedro Malan, mais para expressar o meu descontentamento com a qualidade e o formato dos dados de mercado divulgados no Brasil. No fim vi que os dados nem eram tão ruins, apesar de ser necessário uma certa dose de interpretação e hipóteses para utilizá-los. O fato de que a série do índice não está ajustada não chega a ser um problema dado que temos a série com as variações e com isso é possível reconstruir o índice utilizando qualquer data como valor de referência. No entanto, o BCB não divulga série de variações diárias, e a série diária do índice também não é ajustada. Então dados os datasets observados tenho duas hipóteses:

  • A série do índice não está ajustada para eventos corporativos, ou seja, os valores divulgados são exatamente os valores observados nas datas de observação;
  • A série de variações percentuais traz a variação real do índice, tratando os eventos corporativos.

Com o que apresento aqui não tenho como confrontar estas hipóteses, elas simplismente fazem sentido e essa conclusão se baseia nos gráficos apresentados.

Vimos ainda que parte dos retornos elevados no período de hiper-inflação podem ser explicados pela inflação, mas não tudo. De posse das variações deflacionadas podemos avaliar os retornos de investimentos no índice levando em conta inflação, que é uma análise de toda a importância para que se entenda melhor a dinâmica de uma carteira em ambientes com inflação e como isso impacta o ganho real.