DEV Community

Cover image for Predição de ações na bolsa de valores com Python e Facebook Prophet
Lucas Diogo
Lucas Diogo

Posted on

10 1

Predição de ações na bolsa de valores com Python e Facebook Prophet

Predição de ações na bolsa de valores com Python e Facebook Prophet

Usando Machine Learning para projetar valores futuros.

(Crédito: B3/Divulgação)

Em meio a pandemia da COVID 19 e a crise econômica muitas pessoas (inclusive eu) começaram a despertar o interesse por investir em renda variável na bolsa de valores, quantidade de pessoas essa que cresceu exponencialmente nos últimos 3 anos, e hoje em 2022 a B₃(BM&FBOVESPA) contabiliza a marca de 5 milhões de contas físicas cadastradas.

Evolução de quantidade de pessoas físicas cadastradas na B3. Fonte: B3

O que é bolsa de valores?

A bolsa de valores é um ambiente de negociação onde investidores podem **negociar **seus títulos emitidos por empresas, sejam elas com capitais públicos, mistos ou privados. Esse processo é intermediado com auxílio de correspondentes de negociações através de corretoras.

Operar nesse mercado sem conhecimento prévio pode ser bastante complexo, principalmente porque no papel de acionista é extremamente importante realizar o acompanhamento, fazer análises fundamentalistas sobre a empresa e o momento, para somente após isso decidir aonde colocará o seu suado dinheiro.

Cenário

Atualmente machine learning (e até mesmo redes neurais) tem entrado nos “trend topics” de investimento, pois investidores buscam automatizar não todo, mas parte do processo, pois é uma área que estuda a capacidade de aprendizado de um computador sobre conjuntos de dados e vem sendo muito utilizado para construção de robôs operacionais na área de investimentos.

Em minhas recentes pesquisas sobre o assunto encontrei alguns modelos de implementação utilizando a SciKit Learn (biblioteca para aprendizado de máquina em python) com métodos de regressão que buscam predizer valores de ações na bolsa.

O problema de todas as implementações e o maior erro em minha opinião é que, na verdade, não estão predizendo nada, deixe me explicar.

A maior parte dos exemplos faz a divisão dos dados de uma ação em treinamento e teste limitando a um simples aprendizado para “predições” de dados históricos. E quando falamos em predição de valores na bolsa, também temos em mente a projeção para datas futuras, pois queremos saber qual o valor que terá uma determinada ação em um determinado dia, o que não é abordado.

Em busca de uma solução para o problema encontrei o Prophet, uma biblioteca criada pelo Facebook com implementações em R e Python para fornecer previsões automatizadas, e é com ela que trabalharemos.

Pré processamento

Como requisito, precisaremos das bibliotecas listadas abaixo, as instruções de instalação das mesmas podem ser encontradas nos links.

  • Pandas: Manipulação de dataframes.

  • Pandas T.A: Biblioteca Pandas para análise técnica.

  • Yahoo Query: Busca de dados do mercado financeiro.

  • Plotly: Visualização de gráficos.

  • Prophet: Automação preditiva.

No primeiro passo, realizaremos a importação das bibliotecas necessárias.

from prophet import Prophet
from yahooquery import Ticker
import pandas as pd
from datetime import datetime
import pandas_ta as ta
import plotly.express as px
import plotly.graph_objs as go
from prophet.diagnostics import cross_validation
from prophet.diagnostics import performance_metrics
view raw imports.py hosted with ❤ by GitHub

Após então, utilizaremos o yahooquery que é uma biblioteca excelente para realizar extrações de dados do mercado financeiro do site Yahoo Finance e nos devolve informações extremamente úteis com rapidez e facilidade. Realizaremos uma busca dos dados de pregão de uma determinada ação na bolsa de valores e após isso o devido pré processamento.
# Name by which it is represented on the stock exchange.
symbol = "WEGE3.SA"
# Query stock in yahoo finance
stock = Ticker(symbol)
# Get 48 months data for enough sample terms
history = stock.history(period="48mo")
# Let only date as index
history.reset_index(level=["symbol"], inplace=True)
# Create date column
history['date'] = history.index
# Reindex data using a DatetimeIndex
history.set_index(pd.DatetimeIndex(history.index), inplace=True)
# select features that have interest to us
data = history[['date','adjclose']].copy();
# use technical analyses using 21 one days and append to our dataset
data.ta.ema(close='adjclose', length=21, append=True)
# Drop empty values
data.dropna(inplace=True)
view raw dataset.py hosted with ❤ by GitHub

**Linha 1: **A informação que está sendo atribuída a variável symbol é a identificação do ativo. Você irá achar esse código acessando o Yahoo Finance.

Yahoo Finance: [https://finance.yahoo.com/](https://finance.yahoo.com/)

**Linha 8. **Realização da busca referente ao ativo em um período determinado, no caso escolhido foi 48 meses.

**Linha 10. **Esse dataframe possui dois índices, o primeiro é o código do ativo e o segundo a data do pregão, removeremos o primeiro.

**Linha 14. **Aqui copiamos o índex de data para uma coluna no nosso dataframe e por fim reindexamos novamente.

**Linha 20. **Cópia das features de interesse para um novo dataframe.

**Linha 23. **Existem diversas análises técnicas que podemos fazer para acompanhar o valor de uma ação, no nosso caso optaremos pela **Exponential Move Avarage (EMA) **utilizando 21 dias como amostra.

Exponential Move Avarage ou também conhecido por média móvel, é semelhante à média móvel simples (SMA), que mede a direção da tendência ao longo de um período. No entanto, enquanto a SMA simplesmente calcula uma média dos dados de preços, a EMA aplica mais peso aos dados mais atuais. Devido ao seu cálculo exclusivo, a EMA seguirá os preços mais de perto do que uma SMA.

Esse cálculo é feito pelo Pandas TA que é a biblioteca escolhida por conter a maioria das análises técnicas implementadas.

Após o pré processamento, este será o nosso dataframe com o qual trabalharemos.

Para obter uma melhor visualização, realizamos a plotagem da informação dos nossos dados em gráfico.

#Plot
fig = px.line(data, x='date', y='adjclose')
fig.update_xaxes(
rangeslider_visible=True,
rangeselector=dict(
buttons=list([
dict(count=1, label="1m", step="month", stepmode="backward"),
dict(count=6, label="6m", step="month", stepmode="backward"),
dict(count=1, label="YTD", step="year", stepmode="todate"),
dict(count=1, label="1y", step="year", stepmode="backward"),
dict(step="all")
])
)
)

Aqui podemos observar mais nitidamente o desempenho do valor da ação temporalmente.

Processamento

Nessa segunda etapa prepararemos nosso modelo utilizando técnicas de Machine Learning para predizer os valores de ações com base em uma projeção de x dias.

# Add all dataset as training model
df_train = data[['date','adjclose','EMA_21']]
df_train = df_train.rename(columns={"date": "ds", "adjclose": "y"})
# Fit model
m = Prophet(daily_seasonality=True)
# Train
m.fit(df_train)
# Get dates 30 days in the future
future = m.make_future_dataframe(periods=30)
# Drop weekends
future['day'] = future['ds'].dt.weekday
future = future[future['day'] <=4]
# Predict dates
forecast = m.predict(future)
view raw create_model.py hosted with ❤ by GitHub

**Linha 2. **Seleciona features que vão compor o treinamento do modelo.

**Linha 7. **Instanciado o Prophet com parâmetro baseado em cálculo diário.

**Linha 14. **Treinamento no modelo com base no dataframe. Note que estamos usando todos os dados e não particionando-os, pois a ideia é que utilizemos todo como treinamento, e a projeção como teste.

Linha 16. Removemos os finais de semana da nossa projeção, pois não há pregões nesses dias e pode interferir no resultado.

Se acompanharmos no console enquanto o nosso modelo é treinado, podemos visualizar algumas informações úteis como o número de iterações realizadas no aprendizado.

Treinamento visualizado no console.

**Linha 20. **Criação da projeção para os próximos 30 dias após a última data de pregão do dataset.

**Linha 23. **Nesse momento, o modelo irá tentar predizer os valores com base no que aprendeu previamente no seu treinamento e irá nos retornar o valor do ativo projetado para o tempo estipulado.

Para entendermos melhor, iremos plotar os resultados em um gráfico.

# Plot linear regression result
fig = go.Figure([
go.Scatter(x=df_train['ds'], y=df_train['y'], name='Actual', mode='lines'),
go.Scatter(x=forecast['ds'], y=forecast['yhat'], name='Predicted', mode='lines'),
go.Scatter(x=forecast['ds'], y=df_train['EMA_21'], name='EMA', mode='lines')
])
fig.update_xaxes(
rangeslider_visible=True,
rangeselector=dict(
buttons=list([
dict(count=1, label="1d", step="day", stepmode="backward"),
dict(count=1, label="1m", step="month", stepmode="backward"),
dict(count=6, label="6m", step="month", stepmode="backward"),
dict(count=1, label="YTD", step="year", stepmode="todate"),
dict(count=1, label="1y", step="year", stepmode="backward"),
dict(step="all")
])
)
)
fig.show()

Após isso conduziremos uma breve análise.

Gráfico da regressão realizada onde os pontos em preto são os dados já observados e a linha azul é a predição.

Se olharmos atentamente ao gráfico veremos que nos próximos 30 dias não possuímos nenhuma comparação de valor atual, mas sim somente a linha de predição e é exatamente aqui que está contido a informação de valores futuros.

Valores projetados

A variável forecast possui os dados da predição, utilizaremos ela para filtrar por registros com a data maiores que a atual a nossa projeção para um dataframe.

# Get all predictions
pred_df = forecast[forecast['ds'] > datetime.today()][['ds','yhat']]
# Reset Index
pred_df.reset_index(inplace=True)
# Drop index column
pred_df.drop(labels='index', axis=1,inplace=True)
# Rename columns
pred_df.rename(columns={'ds': 'date', 'yhat': 'predicted price'}, inplace=True)
# Show first elements
pred_df

Aqui está o resultado:

Como removemos os finais de semana, sobraram na nossa projeção 20 dias úteis com o respectivo valor de fechamento.

Validação

Para avaliar o desempenho do nosso modelo é preciso elaborar algumas validações e destacar algumas métricas de assertividade.

A validação cruzada é uma técnica usada com frequência no machine learning para avaliar a variabilidade de um conjunto de dados e a confiabilidade dos modelos treinado com esses dados.

O Prophet inclui uma funcionalidade para validação cruzada de séries temporais para medir o erro de previsão usando dados históricos. Isso é feito selecionando pontos de corte no histórico, e para cada um deles ajustando o modelo usando dados somente até aquele ponto de corte. Assim, você pode entender se o modelo é suscetível a variações nos dados.

Executando o código abaixo obteremos o resultado da cross-validation e das métricas de desempenho do modelo.

# execute cross validation
# reference
# https://facebook.github.io/prophet/docs/diagnostics.html#:~:text=Cross%20validation,up%20to%20that%20cutoff%20point.
df_cv = cross_validation(m, initial='720 days', period='30 days', horizon = '365 days')
# visualize data
print(df_cv.head())
# measure performance
df_p = performance_metrics(df_cv)
print(df_p.head())

E esses são os resultados de desempenho do nosso modelo.

Execução da validação cruzada

As métricas geradas podem também ser visualizas em gráfico.

O código completo pode ser encontrado no meu Github:
GitHub — LucasDiogo96/Stock-Market-Prediction

O valor de mercado de uma ação na bolsa de valores também pode ser afetado por fatores externos ao de uma análise, como uma decisão política ou uma pandemia como foi o ano de 2020.

Da mesma forma, com o avanço constante na área de aprendizado de máquina, estamos munidos de cada vez mais ferramentas e técnicas para automatizar análises que nos ajudem a garantir um bom desempenho como acionistas.

Hora de ficar rico, até a próxima!

Time is money

Referências:

O que é bolsa de valores e como funciona: https://www.btgpactualdigital.com/como-investir/artigos/investimentos/tudo-sobre-bolsa-de-valores

5 milhões de contas de investidores: https://www.b3.com.br/pt_br/noticias/5-milhoes-de-contas-de-investidores.htm

Exponential Moving Average: https://www.fidelity.com/learning-center/trading-investing/technical-analysis/technical-indicator-guide/ema

Cross-validation: evaluating estimator performance: https://scikit-learn.org/stable/modules/cross_validation.html

B3 atinge 5 milhões de contas de investidores em renda variável em janeiro: https://www.b3.com.br/pt_br/noticias/5-milhoes-de-contas-de-investidores.htm

Top comments (2)

Collapse
 
tinesife94 profile image
Felipe

Boa noite Lucas, tudo bem?

Eu estou apenas começando a aprender sobre data science e tenho grande interesse no mercado financeiro. Achei bem interessante seu artigo, mas me deixou com uma dúvida.

Pelo conhecimento que eu tenho, os dados normalmente são separados entre treinamento e teste para evitar o problema de overfitting. Ao treinar o modelo com todos os dados disponíveis, você não teme que os modelos fiquem "muito bons em prever o passado" e, portanto, sem confiabilidade para prever o futuro?

Collapse
 
lucasdiogo96 profile image
Lucas Diogo • Edited

Bom dia Felipe, tudo ótimo e com você?

Obrigado pela contribuição!

Normalmente para predições é usado uma estratégia 80/20 o que pode variar com o tamanho do dataset, e com certeza, utilizar todo o dataset pode fazer com que os resultados sofram de overfitting.

Por trabalharmos com sazonalidade diária, também descontantando o finais de semana temos uma amostragem muito pequena, o que pode fazer com que sofra underfitting.

Um exemplo é se pegarmos a stock NU (Nubank) que fez IPO em dezembro, não teremos dados o suficiente para trabalharmos em cima.

Com base nisso, optei por nesse pequeno exemplo utilizar o meu dataframe de datas futuras como teste, assim como na documentação.

facebook.github.io/prophet/docs/qu...

Abaixo colei um pequeno trecho onde é usado as datas futuras como entrada para o ajuste.

"You can get a suitable dataframe that extends into the future a specified number of days using the helper method Prophet.make_future_dataframe. By default it will also include the dates from the history, so we will see the model fit as well."

O prophet é uma biblioteca especificamente construída para trabalhar com séries temporais e utiliza o sci kit learn por baixo dos panos e tem diversas funções que nos ajudam a trabalhar com esse categoria de dados.