Baixando Cotações e Plotando Médias Móveis com Python

Por Rafael Quintanilha
Em 30/10/2020

Jupyter Notebooks permitem que análises de Data Science sejam feitas de forma dinâmica e colaborativa. Aliados com o poder do pandas para manipulação de dados em grandes quantidades, e matplotlib para rápida e fácil visualização, Notebooks são a ferramenta ideal para quem deseja fazer suas próprias análises.

O objetivo do QuantBrasil é compartilhar essas análises, aprendendo e ensinando sobre mercado financeiro e programação durante o processo. Mesmo que você não seja familiar com a tecnologia, fique por aqui! Queremos ser didáticos o suficiente para engajar pessoas com pouco ou nenhum conhecimento técnico, e ainda assim trazer valor com nossas análises mesmo que você seja experiente com análise quantitativa ou programação em Python.

Nota sobre Notebooks

Os posts desse blog são, eles mesmos, Jupyter Notebooks. Todo o código fonte pode ser acessado diretamente no Github. A forma mais simples de reproduzir o que é discutido nesses posts é através do Google Colab, um ambiente Jupyter online, onde você pode rodar suas análises sem precisar baixar nada.

No entanto, para projetos mais completas ou para mais performance, recomendamos a instalação de um ambiente local. Uma sugestão para quem deseja mais recursos é a instalação do Anaconda, que, além do Jupyter, também conta com várias outras ferramentas para Data Science.

Esqueleto de uma Análise

A maior parte dos nossos artigos será composto por três partes:

  1. Aquisição de dados
  2. Manipulação de dados
  3. Análise e/ou Apresentação de resultados

O primeiro post desse site unirá o básico dos 3 — aprenderemos a baixar cotações das empresas via Yahoo Finance e plotaremos as primeiras médias móveis.

1. Aquisição de Dados

Existem várias formas de se conseguir as cotações históricas dos papeis. A melhor, claro, vai depender do contexto e de sua necessidade. A forma mais fácil é através da API do Yahoo Finance, que permite baixar os dados com bastante confiança e de forma muito rápida.

O primeiro passo é instalar a biblioteca:

# %%capture means we suppress the output
%%capture

!pip install yfinance 
import yfinance as yf

Com a biblioteca instalada, baixar os dados é tão simples quanto:

data = yf.download("VALE3.SA", start="2019-01-01", end="2019-12-31")
data
[*********************100%***********************] 1 of 1 completed
Open High Low Close Adj Close Volume
Date
2019-01-02 50.009998 51.369999 49.790001 51.090000 47.774075 17319600
2019-01-03 50.799999 50.939999 48.400002 49.000000 45.819725 30120000
2019-01-04 49.820000 52.450001 49.820000 52.189999 48.802681 43360100
2019-01-07 52.869999 53.650002 51.720001 51.910000 48.540855 20998900
2019-01-08 52.200001 52.799999 51.619999 52.410000 49.008404 19925600
... ... ... ... ... ... ...
2019-12-20 54.770000 54.990002 54.150002 54.790001 51.233932 42124200
2019-12-23 54.900002 54.980000 54.400002 54.580002 51.037563 10225700
2019-12-26 54.810001 55.000000 54.509998 54.790001 51.233932 20410000
2019-12-27 53.990002 54.000000 53.419998 53.599998 51.449287 13920400
2019-12-30 53.650002 53.860001 53.200001 53.299999 51.161327 11928100

247 rows × 6 columns

Pronto! Nós baixamos as cotações da VALE3 do ano de 2019 e armazenamos numa estrutura chamada dataframe. Um dataframe é uma espécie de tabela que permite acesso e manipulação das linhas e colunas de forma eficiente (vetorial), tornando-se ideal para análises de datasets muito grandes.

Caso nosso interesse seja apenas no valor de fechamento, nós podemos acessar colunas específicas do nosso dataframe:

all_closes = data["Adj Close"]
print(all_closes)
Date 2019-01-02 47.774075 2019-01-03 45.819725 2019-01-04 48.802681 2019-01-07 48.540855 2019-01-08 49.008404 ... 2019-12-20 51.233932 2019-12-23 51.037563 2019-12-26 51.233932 2019-12-27 51.449287 2019-12-30 51.161327 Name: Adj Close, Length: 247, dtype: float64

Repare que quando selecionamos apenas uma coluna, o retorno é uma instância da classe Series. Series são fundamentalmente diferentes de dataframes — elas podem ser entendidas como vetores unidimensionais. Explorar a diferença entre dataframes e series está além do escopo deste post, mas é importante entender que nem todos os métodos que aplicam-se a um, funcionará no outro.

Caso você deseje explicitamente manipular um dataframe de apenas uma coluna, uma alternativa possível é a seguinte:

df = data[["Adj Close"]]
df
Adj Close
Date
2019-01-02 47.774075
2019-01-03 45.819725
2019-01-04 48.802681
2019-01-07 48.540855
2019-01-08 49.008404
... ...
2019-12-20 51.233932
2019-12-23 51.037563
2019-12-26 51.233932
2019-12-27 51.449287
2019-12-30 51.161327

247 rows × 1 columns

2. Manipulação de Dados

De posse do nosso dataset, o próximo passo é manipular os dados de acordo com a análise desejada. Nesse post introdutório, iremos calcular duas médias móveis — uma aritmética e outra exponencial.

Usando nosso dataset reduzido, vamos calcular a média móvel de 21 períodos:

df['MM21'] = df["Adj Close"].rolling(21).mean()
df
Adj Close MM21
Date
2019-01-02 47.774075 NaN
2019-01-03 45.819725 NaN
2019-01-04 48.802681 NaN
2019-01-07 48.540855 NaN
2019-01-08 49.008404 NaN
... ... ...
2019-12-20 51.233932 48.456697
2019-12-23 51.037563 48.659301
2019-12-26 51.233932 48.834298
2019-12-27 51.449287 49.003519
2019-12-30 51.161327 49.187526

247 rows × 2 columns

Simples, não? O método rolling do dataframe retorna uma janela de período n (21 no nosso exemplo). O método mean() é utilizado para computar a média da janela.

Note que os primeiros valores do nosso dataframe são NaN, ou seja, nulos. Isso é esperado, uma vez que para o primeiro valor de média de n períodos só será possível ser calculado uma vez que haja n linhas. Podemos verificar isso analisando as primeiras 21 linhas do nosso dataframe:

df.head(21)
Adj Close MM21
Date
2019-01-02 47.774075 NaN
2019-01-03 45.819725 NaN
2019-01-04 48.802681 NaN
2019-01-07 48.540855 NaN
2019-01-08 49.008404 NaN
2019-01-09 50.205326 NaN
2019-01-10 49.653622 NaN
2019-01-11 48.980354 NaN
2019-01-14 49.186069 NaN
2019-01-15 48.952297 NaN
2019-01-16 49.232830 NaN
2019-01-17 50.710278 NaN
2019-01-18 51.205879 NaN
2019-01-21 51.692131 NaN
2019-01-22 51.505112 NaN
2019-01-23 52.038120 NaN
2019-01-24 52.505665 NaN
2019-01-28 39.610687 NaN
2019-01-29 39.966022 NaN
2019-01-30 43.575493 NaN
2019-01-31 42.546886 48.167262

O cálculo da média exponencial é ligeiramente diferente, mas não menos simples:

df["MME9"] = df["Adj Close"].ewm(span=9, min_periods=9).mean()
df
Adj Close MM21 MME9
Date
2019-01-02 47.774075 NaN NaN
2019-01-03 45.819725 NaN NaN
2019-01-04 48.802681 NaN NaN
2019-01-07 48.540855 NaN NaN
2019-01-08 49.008404 NaN NaN
... ... ... ...
2019-12-20 51.233932 48.456697 49.832455
2019-12-23 51.037563 48.659301 50.073477
2019-12-26 51.233932 48.834298 50.305568
2019-12-27 51.449287 49.003519 50.534312
2019-12-30 51.161327 49.187526 50.659715

247 rows × 3 columns

Dessa vez nós usamos o método ewm, ou Exponential Weighted Functions. Repare que, ao contrário de rolling, dessa vez nós precisamos explicatamente passar o tamanho da janela (span) e a quantidade mínima de dados até iniciarmos a computação (min_periods).

df.head(9)
Adj Close MM21 MME9
Date
2019-01-02 47.774075 NaN NaN
2019-01-03 45.819725 NaN NaN
2019-01-04 48.802681 NaN NaN
2019-01-07 48.540855 NaN NaN
2019-01-08 49.008404 NaN NaN
2019-01-09 50.205326 NaN NaN
2019-01-10 49.653622 NaN NaN
2019-01-11 48.980354 NaN NaN
2019-01-14 49.186069 NaN 49.031055

3. Apresentação de Resultados

Nossa simples manipulação de dados está pronta, o que significa que a etapa final da análise é apresentar os resultados. Hoje, iremos simplesmente explicar como fazer uma rápida plotagem dos dados, de forma a simplificar a visualização.

A ferramenta mais comumente utilizada se chama matplotlib, uma robusta biblioteca de visualização gráfica para Python. Hora de importar a biblioteca e plotar o nosso primeiro gráfico:

import matplotlib.pyplot as plt

df["Adj Close"].plot()
<matplotlib.axes._subplots.AxesSubplot at 0x7fcc35127510>

E nada mais! Com apenas uma linha de código conseguimos plotar uma coluna do nosso dataframe como um gráfico de linha. E se quisermos adicionar mais linhas?

df[["Adj Close", "MME9", "MM21"]].plot()
<matplotlib.axes._subplots.AxesSubplot at 0x7fcc336c2ad0>

Novamente simples, como o esperado. Passando um array de colunas ["Adj Close", "MME9", "MM21"] somos capazes de, em uma linha, plotar os 3 valores e automaticamente adicionar uma legenda.

Melhorando a Apresentação do Gráfico

O último ponto que cobriremos nesse post introdutório é como melhorar a visualização do gráfico. Afinal, uma visualização de dados bem-feita precisa ser intuitiva para o leitor.

Para isso, vamos usar cores diferentes para cada linha, além de aumentarmos sua espessura e o tamanho da imagem:

plt.figure(figsize=(16,8))
plt.plot(df["Adj Close"])
plt.plot(df["MM21"], color="green", lineWidth=3)
plt.plot(df["MME9"], color="magenta", lineWidth=3)
plt.legend(df)
plt.show()

Embora pareça mais complicado, todas as linhas são bastante diretas. Vamos explicá-las uma a uma:

  • plt.figsize determina o tamanho da imagem em polegadas. Nesse caso, nossa figura será 16"x8";
  • plt.plot, separadamente, permite que você customize a linha que está sendo plotada. Embora não façamos nenhuma mudança na linha inicial, repare que alteramos a cor e a espessura das médias;
  • plt.legend permite que você tenha mais controle sobre a legenda do gráfico;
  • Finalmente, plot.show finaliza a customização e exibe a imagem.

Conclusão

Análises quantitativas são uma ferramenta muito poderosa para qualquer projeto de Data Science. Em particular, no mundo do mercado financeiro, elas podem literalmente lhe custar ou gerar dinheiro. Hoje apresentamos a base do que utilizaremos para fazer análises mais complexas no futuro. Espero que o conteúdo tenha sido relevante e que de certa forma seja um incentivo para que você inicie seus estudos na área!