Olá, sou Helvio Siqueira, estudante de Ciencia de Dados. Nos ultimos meses me interessei em investimentos pela bolsa, e para metodo de treino comprei algumas ações e fundos imobiliarios usando análise fundamentalista(ou tentando), e decidi agora usar o Python para uma análise de retornos. Fiz a compra desses ativos em momentos que achei propicio para obter algum lucro, mas como todo bom analista sabe, o mercado é extremamente volatil podendo não corresponder às nossa expectativas :)
Faço aqui uma análise de ativos usando Python e algumas bibliotecas para análise de dados (Pandas, numpy, matplotlib, seaborn), deixo claro que a análise desses ativos não compõe nenhuma especie de recomendação. Desde já digo que aceito criticas e sugestões à respeito do código ;)
Nesse inicio de codigo importo todas as bibliotecas que serão necessarias para fazer a análise(Pandas, Numpy) e plotar graficos(Matplotlib, Seaborn), a biblioteca pandas_datareader é responsavel por extrair os dados da API do Yahoo Finance(ainda ativa em 17/03/2021), API gratuita para a extração de dados do mercado financeiro.
import pandas as pd
import pandas_datareader as web
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
sns.set(style= 'whitegrid')
Os ativos que vou fazer a análise seram: ITUB3(Itaú), TAEE3(Taesa), MXRF11(Fundo Maxi Renda), RECT11(Fundo UBS (BR) OFFICE), onde obtenho o fechamento do dia ajustado(com rendimentos, e JCP) desde 2015
ativos = ['ITUB3.SA', 'TAEE3.SA', 'MXRF11.SA', 'RECT11.SA']
dados = pd.DataFrame()
for a in ativos:
dados[a] = web.DataReader(a, 'yahoo', '2015')['Adj Close']
dados.reset_index(inplace= True)
dados
| Date | ITUB3.SA | TAEE3.SA | MXRF11.SA | RECT11.SA | |
|---|---|---|---|---|---|
| 0 | 2015-01-02 | 12.297894 | 6.219817 | 7.886515 | NaN |
| 1 | 2015-01-05 | 12.159048 | 6.219817 | 7.782810 | NaN |
| 2 | 2015-01-06 | 12.278060 | 6.219817 | 7.753180 | NaN |
| 3 | 2015-01-07 | 12.555760 | 6.219817 | 7.754168 | NaN |
| 4 | 2015-01-08 | 12.734275 | 6.219817 | 7.756143 | NaN |
| ... | ... | ... | ... | ... | ... |
| 1539 | 2021-03-22 | 25.158648 | 11.840000 | 10.310000 | 85.790001 |
| 1540 | 2021-03-23 | 24.729525 | 11.880000 | 10.310000 | 85.959999 |
| 1541 | 2021-03-24 | 24.450094 | 11.790000 | 10.420000 | 86.040001 |
| 1542 | 2021-03-25 | 24.809361 | 11.950000 | 10.470000 | 86.739998 |
| 1543 | 2021-03-26 | 24.830000 | 11.960000 | 10.500000 | 86.680000 |
1544 rows × 5 columns
Aqui colho uma amostra de 10% dos dados sem reposição e reorganizo-os pela data para criar um grafico de linha mais resumido e bonito de se olhar.
resumo_itub= dados[['ITUB3.SA', 'Date']].sample(frac= 0.1, replace= False).sort_values(by= 'Date')
resumo_taesa= dados[['TAEE3.SA', 'Date']].sample(frac= 0.1, replace= False).sort_values(by= 'Date')
resumo_mxrf= dados[['MXRF11.SA', 'Date']].sample(frac= 0.1, replace= False).sort_values(by= 'Date')
resumo_rect= dados[['RECT11.SA', 'Date']].sample(frac= 0.1, replace= False).sort_values(by= 'Date')
resumo_itub.set_index('Date', inplace= True)
resumo_taesa.set_index('Date', inplace= True)
resumo_mxrf.set_index('Date', inplace= True)
resumo_rect.set_index('Date', inplace= True)
fig= plt.figure(figsize= (20, 5))
ax= fig.add_subplot(1, 1, 1)
resumo_itub.plot(ax= ax)
ax.set_ylabel('Preço(R$)', fontdict = {'fontsize':15})
resumo_taesa.plot(ax= ax)
ax.tick_params(axis= 'x', labelrotation= 0)
fig= plt.figure(figsize= (20, 5))
ax= fig.add_subplot(1, 2, 1)
ax.set_ylabel('Preço(R$)', fontdict = {'fontsize':15})
resumo_mxrf.plot(ax= ax)
ax= fig.add_subplot(1, 2, 2)
resumo_rect.plot(ax= ax, color= 'red', alpha= 0.6)
<AxesSubplot:xlabel='Date'>
Começo a análise calculando o retorno dos ativos, que é exatamente o retorno diarios que os ativos dão, para anualizar esse valor basta tirar a media(.mean()) e multiplicar por 246(que é a quantidade de pregões medio em um ano). O retorno dos ativos foi obtido usando retorno logaritmico, mas a função .pct_change() que todo objeto DataFrame do pandas tem tambem poderia ser usada.
retorno = np.log(dados[['ITUB3.SA', 'TAEE3.SA', 'MXRF11.SA', 'RECT11.SA']] / dados[['ITUB3.SA', 'TAEE3.SA', 'MXRF11.SA', 'RECT11.SA']].shift(1))
ret_anual = retorno.mean() * 246
ret_anual
ITUB3.SA 0.112019 TAEE3.SA 0.104239 MXRF11.SA 0.045632 RECT11.SA -0.074453 dtype: float64
plt.figure(figsize= (10, 5))
sns.barplot(x= ret_anual.index, y= ret_anual.values)
<AxesSubplot:>
O risco dos ativos é a quantificação da chance de que um ativo tem de apresentar risco, nessa análise eu utilizo o calculo de desvio padrão(.std()), e anualizo esse valor multiplicando por 246 elevado a 0.5(é preciso tirar a raiz da quantidade de pregões medio já que o calculo de desvio padrão também contém uma raiz).
desvio = retorno.std()
desv_anual = retorno.std() * (246 ** 0.5)
desv_anual
ITUB3.SA 0.298538 TAEE3.SA 0.312209 MXRF11.SA 0.153066 RECT11.SA 0.201556 dtype: float64
retorno
| ITUB3.SA | TAEE3.SA | MXRF11.SA | RECT11.SA | |
|---|---|---|---|---|
| 0 | NaN | NaN | NaN | NaN |
| 1 | -0.011354 | 0.000000 | -0.013237 | NaN |
| 2 | 0.009740 | 0.000000 | -0.003814 | NaN |
| 3 | 0.022366 | 0.000000 | 0.000127 | NaN |
| 4 | 0.014118 | 0.000000 | 0.000255 | NaN |
| ... | ... | ... | ... | ... |
| 1539 | 0.004771 | 0.009334 | -0.001938 | -0.001980 |
| 1540 | -0.017204 | 0.003373 | 0.000000 | 0.001980 |
| 1541 | -0.011364 | -0.007605 | 0.010613 | 0.000930 |
| 1542 | 0.014587 | 0.013480 | 0.004787 | 0.008103 |
| 1543 | 0.000832 | 0.000836 | 0.002861 | -0.000692 |
1544 rows × 4 columns
Plotando em um grafico
plt.subplots(1, 2, figsize= (15, 5))
plt.subplot(1, 2, 1)
sns.barplot(x= ret_anual.index, y= ret_anual.values)
plt.subplot(1, 2, 2)
sns.barplot(x= desv_anual.index, y= desv_anual.values)
<AxesSubplot:>
Correlação e covariancia são medidas para quantificar o comportamento de um ativo em relação a outro, podendo ter uma relação positiva(se um ativo sobre, o outro ativo também sobe), negativa(se um ativo sobre, o outro ativo desce) ou neutra(sem nenhuma relação entre o dois ativos). Todo objeto DataFrame do pandas tem um metodo .corr() e .cov() responsavel por calcular a correlação e a covariancia respectivamente.
correlacao = retorno.corr()
covarian = retorno.cov()
covarian
| ITUB3.SA | TAEE3.SA | MXRF11.SA | RECT11.SA | |
|---|---|---|---|---|
| ITUB3.SA | 0.000362 | 0.000037 | 0.000041 | 0.000081 |
| TAEE3.SA | 0.000037 | 0.000396 | 0.000014 | 0.000049 |
| MXRF11.SA | 0.000041 | 0.000014 | 0.000095 | 0.000068 |
| RECT11.SA | 0.000081 | 0.000049 | 0.000068 | 0.000165 |
correlacao
| ITUB3.SA | TAEE3.SA | MXRF11.SA | RECT11.SA | |
|---|---|---|---|---|
| ITUB3.SA | 1.000000 | 0.098071 | 0.218484 | 0.283149 |
| TAEE3.SA | 0.098071 | 1.000000 | 0.069794 | 0.243353 |
| MXRF11.SA | 0.218484 | 0.069794 | 1.000000 | 0.420135 |
| RECT11.SA | 0.283149 | 0.243353 | 0.420135 | 1.000000 |
O grafico heatmap é um grafico para a visualização de uma tabela de correlação, onde o espaço com a cor mais clara representa uma maior correlação entre os ativos e com a cor mais escura uma correlação negativa.
plt.figure(figsize= (9, 8))
sns.heatmap(correlacao, vmax= 1, vmin= -1, annot= True, linewidths= 1)
<AxesSubplot:>
ret_anual
ITUB3.SA 0.112019 TAEE3.SA 0.104239 MXRF11.SA 0.045632 RECT11.SA -0.074453 dtype: float64
desv_anual
ITUB3.SA 0.298538 TAEE3.SA 0.312209 MXRF11.SA 0.153066 RECT11.SA 0.201556 dtype: float64
Sharpe Ratio é uma variavel criada para auxiliar na comparação de dois ativos, um ativo com uma taxa de retorno alta e taxa de risco baixa resulta em um Sharpe ratio alto.
sharpe_ratio = ((ret_anual - 0.027) / desv_anual)
sharpe_ratio.sort_values(ascending= False, inplace= True)
sharpe_ratio
ITUB3.SA 0.284785 TAEE3.SA 0.247397 MXRF11.SA 0.121726 RECT11.SA -0.503347 dtype: float64
sns.barplot(x= sharpe_ratio.values, y= sharpe_ratio.index)
<AxesSubplot:>
Variavel para quantificar o risco do ativo com relação ao mercado(IBOVESPA), quanto maior o Beta maior é a variancia do ativo em relação ao mercado, ou seja o ativo tem maior exposição ao mercado.
mercado_bova = web.DataReader('BOVA11.SA', 'yahoo', start= '2015')['Adj Close']
mercado_bova= pd.DataFrame(mercado_bova)
mercado_bova.rename(columns={'Adj Close': 'BOVA'}, inplace= True)
mercado_bova.reset_index(inplace= True)
dados_mercado = pd.merge(dados, mercado_bova)
dados_mercado
| Date | ITUB3.SA | TAEE3.SA | MXRF11.SA | RECT11.SA | BOVA | |
|---|---|---|---|---|---|---|
| 0 | 2015-01-02 | 12.297894 | 6.219817 | 7.886515 | NaN | 47.259998 |
| 1 | 2015-01-05 | 12.159048 | 6.219817 | 7.782810 | NaN | 46.320000 |
| 2 | 2015-01-06 | 12.278060 | 6.219817 | 7.753180 | NaN | 46.580002 |
| 3 | 2015-01-07 | 12.555760 | 6.219817 | 7.754168 | NaN | 48.150002 |
| 4 | 2015-01-08 | 12.734275 | 6.219817 | 7.756143 | NaN | 48.509998 |
| ... | ... | ... | ... | ... | ... | ... |
| 1537 | 2021-03-18 | 25.018932 | 11.570000 | 10.290000 | 86.040001 | 110.498611 |
| 1538 | 2021-03-19 | 25.038893 | 11.730000 | 10.330000 | 85.959999 | 111.830147 |
| 1539 | 2021-03-22 | 25.158648 | 11.840000 | 10.310000 | 85.790001 | 110.636162 |
| 1540 | 2021-03-23 | 24.729525 | 11.880000 | 10.310000 | 85.959999 | 108.985153 |
| 1541 | 2021-03-26 | 24.830000 | 11.960000 | 10.500000 | 86.680000 | 109.339996 |
1542 rows × 6 columns
dados_mercado_retorno= np.log(dados_mercado[['ITUB3.SA', 'TAEE3.SA', 'MXRF11.SA', 'RECT11.SA', 'BOVA']] / dados_mercado[['ITUB3.SA', 'TAEE3.SA', 'MXRF11.SA', 'RECT11.SA', 'BOVA']].shift(1))
cov_com_mercado= dados_mercado_retorno.cov() * 246
mercado_var= dados_mercado_retorno['BOVA'].var() * 246
cov_com_mercado
| ITUB3.SA | TAEE3.SA | MXRF11.SA | RECT11.SA | BOVA | |
|---|---|---|---|---|---|
| ITUB3.SA | 0.089189 | 0.009112 | 0.010016 | 0.019942 | 0.062749 |
| TAEE3.SA | 0.009112 | 0.097570 | 0.003361 | 0.012070 | 0.015920 |
| MXRF11.SA | 0.010016 | 0.003361 | 0.023490 | 0.016843 | 0.012074 |
| RECT11.SA | 0.019942 | 0.012070 | 0.016843 | 0.040800 | 0.021823 |
| BOVA | 0.062749 | 0.015920 | 0.012074 | 0.021823 | 0.074583 |
beta_ativos= cov_com_mercado.iloc[4][:4] / mercado_var
beta_ativos
ITUB3.SA 0.841330 TAEE3.SA 0.213457 MXRF11.SA 0.161887 RECT11.SA 0.292599 Name: BOVA, dtype: float64
sns.barplot(x= beta_ativos.index, y= beta_ativos.values)
<AxesSubplot:>
O retorno da carteira é o retorno da carteira de ativos, ele depende muito do peso de cada ativo na carteira. Podendo variar dependendo do retorno de cada ativo naquele momento e da quantidade de cada ativo.
dados.set_index('Date', inplace= True)
preco_ativos= dados.iloc[-1]
preco_ativos
ITUB3.SA 24.83 TAEE3.SA 11.96 MXRF11.SA 10.50 RECT11.SA 86.68 Name: 2021-03-26 00:00:00, dtype: float64
total_ativos= preco_ativos[0] * 2 + preco_ativos[1] * 4 + preco_ativos[2] * 11 + preco_ativos[3] * 1
pesos = np.array([preco_ativos[0] * 2 / total_ativos, preco_ativos[1] * 4 / total_ativos, preco_ativos[2] * 11 / total_ativos, preco_ativos[3] * 1 / total_ativos])
pesos
array([0.16571009, 0.15963695, 0.3854111 , 0.28924186])
ret_anual
ITUB3.SA 0.112019 TAEE3.SA 0.104239 MXRF11.SA 0.045632 RECT11.SA -0.074453 dtype: float64
retorno_carteira= np.dot(ret_anual, pesos)
print(f'{(retorno_carteira * 100):.2f}%')
3.13%
Semelhante ao retorno da carteira, é uma média ponderada do risco de todos os ativos.
Variância
carteira_var= (np.dot(pesos.T, np.dot(retorno.cov() * 246, pesos)))
print(f'{(carteira_var * 100):.2f}%')
2.07%
Volatividade
carteira_vol= (np.dot(pesos.T, np.dot(retorno.cov() * 246, pesos))) ** 0.5
print(f'{(carteira_vol * 100):.2f}%')
14.40%
O risco sistematico é a representação do risco que não é posivel ser controlado(desastres naturais, guerras e pandemias), já o risco não sistemático é a representação do risco inerente ao mercado na carteira(preço do petróleo, preço do dolar, economia interna), ele pode ser reduzido com uma boa diversificação de ativos.
r_diversificavel= carteira_var - ((pesos[0] ** 2 * ret_anual[0]) - (pesos[1] ** 2 * ret_anual[1]) - (pesos[2] ** 2 * ret_anual[2]) - (pesos[3] ** 2 * ret_anual[3]))
r_diversificavel * 100
2.085567989632801
r_n_diversificavel = carteira_var - r_diversificavel
r_n_diversificavel * 100
-0.012991168573479525