import pandas as pd
import yfinance as yf
from statsmodels import api as sm
import seaborn as sns
import matplotlib.pyplot as plt
from mplfinance.original_flavor import candlestick_ohlc
import matplotlib.dates as mdates
import numpy as np
from scipy.optimize import minimize
import plotly.graph_objects as go
from plotly.subplots import make_subplots
tickers = ['VALE', 'RIG', 'RIO', 'BBD', 'DVN', 'PBR-A', 'EC', 'BP', 'EQNR', 'E', 'SHEL', 'CTRA', 'TTE', 'CTRA', 'PXD', 'OXY', 'MRO', 'XOM', 'ITUB', 'EGIE3.SA']
Selecionamos as seguintes ações para análise:
A escolha, foi feita com base no setor de energia, que é um dos setores mais importantes da economia mundial, e que tem grande influência no mercado financeiro. Além disso, a escolha de ações de empresas de diferentes países, como Brasil, Estados Unidos, Noruega, Itália, Reino Unido e Colômbia, o que permite uma análise mais ampla do mercado.
Para a análise técnica, vamos utilizar a biblioteca "mplfinance" para plotar os gráficos de candlestick, além disso vamor criar uma função para computar o RSI (Relative Strength Index) e o MACD (Moving Average Convergence Divergence).
def compute_RSI(data, time_window):
diff = data.diff(1).dropna()
# this preservers dimensions off diff values
up_chg = 0 * diff
down_chg = 0 * diff
# up change is equal to the positive difference, otherwise equal to zero
up_chg[diff > 0] = diff[ diff>0 ]
# down change is equal to negative deifference, otherwise equal to zero
down_chg[diff < 0] = diff[ diff < 0 ]
up_chg_avg = up_chg.ewm(com=time_window-1 , min_periods=time_window).mean()
down_chg_avg = down_chg.ewm(com=time_window-1 , min_periods=time_window).mean()
rs = abs(up_chg_avg/down_chg_avg)
rsi = 100 - 100/(1+rs)
return rsi
def compute_macd(dados):
# janela curta, longa e sinal
short_window = 12
long_window = 26
signal_window = 9
# ewm do pandas para o cálculo da média móvel exponencialmente ponderada
short_media = dados.ewm(span=short_window, adjust=False).mean()
long_media = dados.ewm(span=long_window, adjust=False).mean()
macd = short_media - long_media
signal = macd.ewm(span=signal_window, adjust=False).mean()
return macd, signal
Nos gráficos abaixo, podemos ver o comportamento dos indicadores RSI e MACD para cada ativo. A linha azul representa o RSI, a linha amarela representa o MACD e a linha magenta representa o sinal do MACD. As linhas tracejadas vermelhas e verdes representam o candlestick do ativo, sendo vermelho para queda e verde para alta.
RSI (Índice de Força Relativa):
MACD (Convergência/Divergência de Médias Móveis):
Candlestick:
fig, axes = plt.subplots(nrows=10, ncols=2, figsize=(16, 28))
axes = axes.flatten()
for i, ax1 in enumerate(axes):
# Coletando dados de cada ativo
df = yf.download(tickers[i], start='2023-01-01', end='2023-10-01', progress=False)
df['Date'] = df.index.map(mdates.date2num)
df['RSI'] = compute_RSI(df['Close'], 14)
df['MACD'], df['Signal'] = compute_macd(df['Close'])
ax2 = ax1.twinx()
ax3 = ax1.twinx()
# Definindo data como eixo x
ax1.xaxis_date()
ax1.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d'))
ax1.set_ylabel('Price', color='k')
plt.setp(ax1.get_xticklabels(), rotation=45, ha='right', visible=True)
# Plotting Candlestick
candlestick_ohlc(ax1,zip(df['Date'],
df['Open'],
df['High'],
df['Low'],
df['Close']),
width=0.4, colorup='g', colordown='r')
# Plotting RSI
ax2.plot(df.index, df['RSI'], label='RSI', color='b', linewidth=1.2)
ax2.axhline(y=70, color='r', linestyle='--')
ax2.axhline(y=30, color='r', linestyle='--')
ax2.set_ylim([0, 100])
ax2.set_yticks([30, 70])
ax2.legend(loc='upper left')
ax1.set_title(tickers[i])
# Plotting MACD
ax3.plot(df.index, df['MACD'], label='MACD', color='y', linewidth=0.8)
ax3.plot(df.index, df['Signal'], label='Signal', color='m', linewidth=0.8)
ax3.legend(loc='upper right')
plt.tight_layout()
plt.show()
Escolhemos os mêses de 2023, pois é um período recente que permite uma análise mais ampla.
Iremos prestar dar mais prioridade para um RSI abaixo de 30 e acima de 70, pois são os valores que indicam uma possível reversão de tendência. Além disso, iremos prestar atenção na linha do MACD cruzando a linha de sinal, acimas de 0. E observar como os sticks do candlestick estão consecutivamente vermelhos ou verdes.
Daí, podemos ter maior confiabilidade nas ações:
Para nossa análise fundamentalista, vamos utilizar a biblioteca "yfinance" para obter os dados financeiros da empresa, além disso vamos criar uma função para retornar os indicadores desejados. São eles:
Iremos avaliar os indicadores da seguinte forma:
1 - Liquidez
2 - Volume e Receita
2 - Resultados
3 - Endividamento
4 - Dividendos
5 - Cotação
# Função para formatar valores
def formatar_valor(valor, tipo='moeda'):
if valor is None:
return None
if tipo == 'moeda':
# O :,.2f indica que o valor deve ser formatado como float com 2 casas decimais e separador de milhar
return f"R$ {valor:,.2f}"
elif tipo == 'porcentagem':
# O :.2f indica que o valor deve ser formatado como float com 2 casas decimais
return f"{valor * 100:.2f}%"
# Função para extrair indicadores
def obter_indicadores(ticker):
# yf.Ticker(ticker) cria um objeto com informações do ticker
estoque = yf.Ticker(ticker)
# .info extrai as informações do objeto
informacoes = estoque.info
# Criando um dicionário com os indicadores desejados
indicadores = {
'Setor': informacoes.get('sector', None),
'Liquidez Corrente': informacoes.get('currentRatio', None),
'Margem de Lucro': informacoes.get('profitMargins', None),
'Receita Total': informacoes.get('totalRevenue', None),
'EBITDA': informacoes.get('ebitda', 1),
'Relação Dívida-Patrimônio': informacoes.get('debtToEquity', None),
'P/L (Relação Preço-Lucro)': informacoes.get('trailingPE', None),
'P/B (Relação Preço-Valor Contábil)': informacoes.get('priceToBook', None),
'Dividend Yield (Rendimento de Dividendo)': informacoes.get('dividendYield', None),
'Cap. de Mercado': informacoes.get('marketCap', None),
'Volume Médio': informacoes.get('averageVolume', None),
'ROA (Retorno sobre Ativos)': informacoes.get('returnOnAssets', None),
'ROE (Retorno sobre o Patrimônio Líquido)': informacoes.get('returnOnEquity', None),
}
divida_total = informacoes.get('totalDebt', 0) # Assume 0 se não disponível
caixa_e_equivalentes = informacoes.get('cash', 0) # Assume 0 se não disponível
divida_liquida = divida_total - caixa_e_equivalentes
indicadores['Dívida Líquida / EBITDA'] = formatar_valor(divida_liquida / indicadores["EBITDA"])
# Aplicando a formatação
for chave in indicadores:
if "Yield" in chave or "ROA" in chave or "ROE" in chave or "Margem" in chave or "Relação" in chave:
indicadores[chave] = formatar_valor(indicadores[chave], 'porcentagem')
elif "Liquidez Corrente" in chave or "Cap. de Mercado" in chave or "Receita Total" in chave:
indicadores[chave] = formatar_valor(indicadores[chave], 'moeda')
return indicadores
data = {}
# Coletando indicadores para cada ticker
for ticker in tickers:
data[ticker] = obter_indicadores(ticker)
# Transformando o dicionário em um DataFrame
df = pd.DataFrame(data)
df
| VALE | RIG | RIO | BBD | DVN | PBR-A | EC | BP | EQNR | E | SHEL | CTRA | TTE | PXD | OXY | MRO | XOM | ITUB | EGIE3.SA | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Setor | Basic Materials | Energy | Basic Materials | Financial Services | Energy | Energy | Energy | Energy | Energy | Energy | Energy | Energy | Energy | Energy | Energy | Energy | Energy | Financial Services | Utilities |
| Liquidez Corrente | R$ 1.15 | R$ 1.96 | R$ 1.99 | None | R$ 0.98 | R$ 0.90 | R$ 1.21 | R$ 1.17 | R$ 1.83 | R$ 1.44 | R$ 1.46 | R$ 1.74 | R$ 1.17 | R$ 0.73 | R$ 1.04 | R$ 0.80 | R$ 1.48 | None | R$ 1.28 |
| Margem de Lucro | 27.58% | -37.68% | 16.39% | 20.72% | 29.83% | 26.89% | 15.54% | 8.19% | 18.70% | 7.87% | 8.09% | 40.53% | 8.18% | 28.39% | 21.55% | 30.56% | 14.06% | 26.66% | 28.48% |
| Receita Total | R$ 206,414,004,224.00 | R$ 2,675,000,064.00 | R$ 52,445,999,104.00 | R$ 72,893,186,048.00 | R$ 16,023,000,064.00 | R$ 581,562,990,592.00 | None | R$ 227,586,998,272.00 | R$ 128,649,003,008.00 | R$ 116,525,998,080.00 | R$ 358,589,005,824.00 | R$ 7,683,999,744.00 | R$ 238,681,997,312.00 | R$ 20,387,000,320.00 | R$ 31,536,001,024.00 | R$ 6,695,000,064.00 | R$ 367,977,005,056.00 | R$ 118,938,001,408.00 | R$ 11,371,320,320.00 |
| EBITDA | 85363998720 | 866000000 | 18742999040 | 1 | 8830999552 | 291086991360 | 1 | 51136000000 | 66170998784 | 18786000896 | 68989001728 | 5608999936 | 52544000000 | 10110999552 | 16304000000 | 4560999936 | 79138996224 | 1 | 6102162944 |
| Relação Dívida-Patrimônio | 4087.60% | 7157.10% | 2638.40% | None | 6041.30% | 7504.40% | 10647.10% | 7090.70% | 6152.20% | 6026.30% | 4391.90% | 2021.80% | 4844.20% | 2595.00% | 7032.80% | 5333.50% | 2004.70% | None | 20550.90% |
| P/L (Relação Preço-Lucro) | 544.03% | None | 1189.75% | 1033.33% | 623.63% | 290.09% | 301.89% | 633.50% | 420.60% | 561.18% | 770.80% | 662.97% | 840.31% | 953.93% | 1052.36% | 792.26% | 925.04% | 828.13% | 1033.50% |
| P/B (Relação Preço-Valor Contábil) | 30.71% | 58.06% | 196.87% | 17.84% | 265.52% | 23.68% | 126.98% | 628.13% | 394.76% | 103.67% | 112.03% | 156.98% | 138.12% | 237.87% | 268.99% | 137.92% | 232.55% | 29.04% | 401.31% |
| Dividend Yield (Rendimento de Dividendo) | 5.49% | None | 6.32% | 6.69% | 7.23% | 42.46% | 28.59% | 4.11% | 3.66% | 6.09% | 3.59% | 2.96% | 4.71% | 7.17% | 1.11% | 1.50% | 3.10% | 5.49% | 2.86% |
| Cap. de Mercado | R$ 56,965,111,808.00 | R$ 6,134,776,832.00 | R$ 101,718,212,608.00 | R$ 29,646,260,224.00 | R$ 29,247,956,992.00 | R$ 90,839,924,736.00 | R$ 23,025,295,360.00 | R$ 109,814,530,048.00 | R$ 95,585,411,072.00 | R$ 52,119,687,168.00 | R$ 211,145,310,208.00 | R$ 19,872,811,008.00 | R$ 157,538,402,304.00 | R$ 52,330,827,776.00 | R$ 55,115,685,888.00 | R$ 15,499,530,240.00 | R$ 460,958,990,336.00 | R$ 51,942,543,360.00 | R$ 33,477,525,504.00 |
| Volume Médio | 21022598 | 14843403 | 2866726 | 19636626 | 8316106 | 7682339 | 1862403 | 7493620 | 2393706 | 247633 | 4541599 | 5728219 | 1177552 | 1697265 | 8935066 | 10972133 | 15663249 | 17697888 | 1363535 |
| ROA (Retorno sobre Ativos) | 9.98% | 0.11% | 8.97% | 0.85% | 17.02% | 15.43% | 11.99% | 8.39% | 24.58% | 4.71% | 7.88% | 12.29% | 8.13% | 12.78% | 7.83% | 8.26% | 10.51% | 1.42% | 8.12% |
| ROE (Retorno sobre o Patrimônio Líquido) | 29.59% | -9.34% | 15.53% | 9.48% | 44.96% | 39.99% | 29.13% | 23.39% | 52.91% | 17.25% | 15.14% | 25.04% | 16.89% | 25.29% | 23.74% | 17.97% | 27.20% | 18.30% | 37.62% |
| Dívida Líquida / EBITDA | R$ 0.93 | R$ 8.60 | R$ 0.75 | R$ 622,578,565,120.00 | R$ 0.76 | R$ 0.96 | R$ 0.00 | R$ 1.19 | R$ 0.46 | R$ 1.78 | R$ 1.22 | R$ 0.46 | R$ 1.07 | R$ 0.56 | R$ 1.27 | R$ 1.31 | R$ 0.52 | R$ 827,033,976,832.00 | R$ 3.13 |
Observando os indicadores, notamos uma grande variação entre as empresas. Para avaliar as ações, é crucial considerar o setor em que operam, pois diferentes setores têm normas diferentes. Por exemplo, ações no setor de energia tendem a ter níveis mais elevados de dívida, dada a natureza capital-intensiva do setor.
Empresas como VALE exibe alto valorvde Relação Dívida-Patrimônio, o que pode indicar alavancagem elevada, mas isso precisa ser analisado no contexto do setor e dos retornos gerados. O Dividend Yield de VALE também é atraente, sugerindo bons retornos para investidores.
A ação EQNR apresenta um Dividend Yield de 3,66%, o que é atraente para investidores que buscam rendimento. Além disso, o seu ROA (Retorno sobre Ativos) de 8,39% e ROE (Retorno sobre o Patrimônio Líquido) de 52,91% são significativamente altos, indicando eficiência na geração de lucro tanto em relação aos ativos quanto ao patrimônio. Isso sugere que a empresa tem sido rentável e tem gerenciado seus recursos de maneira eficaz. Além disso, sua relação Dívida Líquida / EBITDA é de 0,462363, o que é relativamente baixo, indicando menor risco em termos de alavancagem operacional.
A ação XOM tem um Dividend Yield sólido de 7,17%, tornando-a uma das mais altas da lista, o que é atraente para aqueles que buscam renda. Além disso, seu P/B (Relação Preço-Valor Contábil) é de 0,925, o que pode sugerir que a ação está ligeiramente subvalorizada. Embora sua relação Dívida-Patrimônio seja alta, é importante considerar que empresas do setor de energia muitas vezes operam com níveis mais elevados de dívida devido à natureza capital-intensiva do setor. Além disso, o ROA de 10,51% e o ROE de 27,20% são impressionantes, indicando uma boa rentabilidade.
Por outro lado, ações como PBR-A e DVN mostram alguns valores ausentes, o que requer investigação adicional. A ausência de certos dados pode ser devido a questões contábeis, reportagens financeiras ou outras razões.
Para nossa última métrica, iremos avaliar a correlação entre os ativos que estamos priorizando. Para montar nosso portfólio, é interessante que os ativos não sejam fortemente correlacionados, pois assim, conseguimos diversificar nosso portfólio e reduzir o risco.
# iniciar os retornos
all_stock_returns = pd.DataFrame()
tickers = ["VALE", "EQNR", "BBD", "EC", "CTRA", "ITUB", "RIG", "XOM"]
for stock in tickers:
data = yf.Ticker(stock)
start_date = "2023-01-01" ### melhorar o intervalo
end_date = "2023-10-01"
data = data.history(start=start_date, end=end_date)
data['returns'] = data['Close'].pct_change()
# adicionar os retornos ao data frame
all_stock_returns[stock] = data['returns']
# tirar os NAs em cima dos retornos, a primeira linha, por exemplo.
all_stock_returns = all_stock_returns.dropna()
plt.figure(figsize=(7,7))
correlation = all_stock_returns.corr()
sm.graphics.plot_corr(correlation,xnames=list(correlation.columns))
None
<Figure size 700x700 with 0 Axes>
Com base em nossa análise, podemos concluir que as ações que indico para montagem de um portfólio de risco baixo são:
Em todas as métricas, essas ações se destacaram, mesmo sem crescimento significativo, apresentaram estabilidade e bons indicadores. Além disso, a correlação entre elas é baixa, o que permite uma diversificação do portfólio.