AEDI_Atividade2 / src /streamlit_app.py
Pedroni's picture
Update src/streamlit_app.py
0c450bf verified
import streamlit as st
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import norm
import plotly.graph_objects as go
from PIL import Image
from yahooquery import Ticker
from datetime import date
from pandas.tseries.offsets import BDay
from typing import Tuple
# Função com cache para buscar dados históricos do Yahoo Finance
@st.cache_data(ttl=3600)
def load_data(tickers: Tuple[str, ...], start_date: date, end_date: date) -> pd.DataFrame:
try:
tq = Ticker(tickers, asynchronous=False)
hist = tq.history(
start=start_date.strftime('%Y-%m-%d'),
end=end_date.strftime('%Y-%m-%d')
)
if hist.empty:
st.warning("Nenhum dado retornado pelo Yahoo Finance. Verifique os tickers e as datas.")
return pd.DataFrame()
df = hist.reset_index().pivot(index='date', columns='symbol', values='close')
df.reset_index(inplace=True)
df.rename(columns={col: col.upper() for col in df.columns}, inplace=True)
df.rename(columns={'DATE': 'Date'}, inplace=True)
return df
except Exception as e:
st.error(f"Erro ao buscar dados com yahooquery: {e}")
return pd.DataFrame()
st.title("Análise de Ativos - Portfólio Eficiente")
st.write("Foram escolhidas 5 ações. O primeiro critério de escolha foi o V/P das ações.")
st.write("Um V/P baixo pode indicar que a ação está sendo negociada abaixo do seu valor real")
st.write("O segundo critério completa o primeiro, um V/P baixo pode indicar envidiamento, então, foi escolhido ações de empresas com risco baixo de endividamento.")
st.write("Exemplo é Bradesco, Caixa e Banco do Brasil na área de seguros, que sempre tem receitas mensais independente maiores que gastos. Pois o segurado só terá a acesso ao benefício se pagar corretamente.")
st.write("O Terceiro critério foi empresas que sempre pagaram dividendos ao longo do tempo.")
acoes = ["BBAS3.SA", "BBSE3.SA", "CXSE3.SA", "BBDC4.SA", "VALE3.SA"]
indice = '^BVSP'
#Download
ticket_acoes = Ticker(acoes + [indice])
dados = ticket_acoes.history(start="2020-01-01", end="2025-01-01", interval='1d' )
precos = dados.reset_index().pivot(index="date", columns="symbol", values = "adjclose")
fig1,ax = plt.subplots(figsize= (10,6))
for ativo in acoes:
ax.plot(precos.index, precos[ativo], label=ativo)
ax.set_title("Histórico de Preços")
ax.set_xlabel('Data')
ax.set_ylabel("Preço em R$")
ax.legend()
st.pyplot(fig1)
#Retorno logaritmo
precos.columns = [col.upper() for col in precos.columns]
acoes_teste = [teste.upper() for teste in acoes if teste.upper() in precos.columns]
retorno_log = np.log(precos/precos.shift(1).dropna())
if st.button("Rodar Retorno Logaritmo Diário"):
st.write("As ações sofrem evoluções multiplicativas, ou seja o preço da ação varia so preço do dia anterior multiplicado pela variação do dia atual.")
st.write("O Retorno Logaritmo facilita, transformando essa multiplicação em soma, assim fica mais fácil descobrir o retorno das ações diariamente, mensalmente ou anualmente.")
st.write("Este retorno também se comporta mais próximo de uma distrubuição normal (diferente da variação das ações, que tem tendências, crescem ou diminuem com o tempo), sendo possível assim usar metódos estatisticos clássicos. Calcular a Média, o desvio-padrão e o VaR.")
st.write("Retorno diário em 252 dias - Convenção da B3 sobre a quantidade de dias úteis para calculo de Juros:")
st.write("https://www.b3.com.br/pt_br/market-data-e-indices/servicos-de-dados/market-data/consultas/mercado-de-derivativos/precos-referenciais/taxas-referenciais-bm-fbovespa/")
st.subheader('Retorno Diario')
st.line_chart(retorno_log[acoes_teste])
#Simualações Monte Carlo e Indice de Sharpe
carteiras = 50000
ativos = len(acoes_teste)
retornos_anuais = retorno_log[acoes_teste].mean() * 252 #252 são os dias que são comercializados os ativos.
cov_matrix = retorno_log[acoes_teste].cov() * 252
simulados = []
risco = []
pesos_lista = []
sharpe = []
for _ in range(carteiras):
pesos = np.random.random(ativos)
pesos /= np.sum(pesos)
retorno_esperado = np.dot(pesos, retornos_anuais)
risco_calculado = np.sqrt(np.dot(pesos.T, np.dot(cov_matrix, pesos)))
indice_sharpe = retorno_esperado/risco_calculado
simulados.append(retorno_esperado)
risco.append(risco_calculado)
sharpe.append(indice_sharpe)
pesos_lista.append(pesos)
df_carteiras = pd.DataFrame({
"Retorno": simulados,
"Risco": risco,
"Sharpe": sharpe,
"Pesos": pesos_lista
})
id_otimo = df_carteiras["Sharpe"].idxmax()
carteira_otima = df_carteiras.loc[id_otimo]
if st.button("Rodar Simulação com Carteiras"):
st.write("Usando o logaritmo dos retornos diários foi possível calcular a matriz de covariância e medir como os ativos se movem juntos.")
st.write("Foi utilizado pesos aleatórios, uma proporção para cada ativo e sempre garantindo que a soma dos pesos seja 100% da carteira.")
st.write("retorno_esperado = np.dot(pesos, retornos_anuais)")
st.write("Com o Produto escalar foi possível exibir o vetor de retorno e o vetor de pesos. Conseguindo obter a média ponderada dos retornos dos ativos")
st.write("risco_calculado = np.sqrt(np.dot(pesos.T, np.dot(cov_matrix, pesos)))")
st.write("Calcula-se o desvio padrão dos retornos da carteira e usa a matriz de covariância para fazer a correlação entre os ativos")
st.write("Se os ativos forem descorrelacionados, o risco da carteira pode ser menor que a média dos ricos.")
st.write("Depois foi realizado o calculo do indice de Sharp, que mede a eficiência do investimento, quanto de retorno é obitido para cada unidade de risco assumido")
st.write("Com a simulação de Monte Carlo simula-se diferentes carteiras cada rodada com peso diferente.")
st.write("E no final calcula-se a carteira ótima, ou seja a fronteira eficiente de Markowitz")
st.slider(label = 'Número de Carteiras Simuladas - Monte Carlo', min_value=1000, max_value=100000,value=50000,step=1000)
fig = go.Figure()
fig.add_trace(go.Scattergl(
x=df_carteiras["Risco"],
y=df_carteiras["Retorno"],
mode="markers",
marker=dict(color="blue", size=5, opacity=0.5),
name="Carteiras Simulada"
))
fig.add_trace(go.Scattergl(
x=[carteira_otima["Risco"]],
y=[carteira_otima["Retorno"]],
mode = "markers",
marker=dict(color="red", size=7, opacity=0.5),
name = "Carteira Ótima - Indice de Sharpe",
))
fig.update_layout(
title='Simulação Monte Carlo - Risco x Retorno',
xaxis_title = "Risco - Desvio Padrão Anual",
yaxis_title = "Retorno Esperado Anual",
showlegend=True
)
st.write('Melhor Carteira')
st.write(f'Retorno anual esperado: {carteira_otima['Retorno']*100:.1f}%')
st.write(f'Risco anual: esperado {carteira_otima['Risco']*100:.1f}%')
st.write(f'Indice de Sharpe: {carteira_otima['Sharpe']}')
peso_carteira = {acoes_teste[i]: f"{peso*100:.2f}%" for i, peso in enumerate(carteira_otima['Pesos'])}
st.write(peso_carteira)
st.plotly_chart(fig)
#Evolução do Patrimonio
if st.button("Rodar Evolução do Patrimonio acumulado com VaR"):
investimento = st.slider(label ="Valor Inicial do Investimento em R$:", min_value=2000, max_value = 500000, value=35000, step=5000)
retorno_diario_carteira = retorno_log[acoes_teste].dot(carteira_otima['Pesos']).dropna()
patrimonio = investimento * np.exp(np.cumsum(retorno_diario_carteira))
df_patrimonio = pd.DataFrame({
"Data": retorno_diario_carteira.index,
"patrimonio": patrimonio
})
st.subheader('Evolução da Carteira Ótima')
fig, ax = plt.subplots(figsize=(10,6))
ax.plot(df_patrimonio["Data"], df_patrimonio["patrimonio"], color ="blue")
ax.set_xlabel("Data")
ax.set_ylabel("Patrimônio R$")
ax.set_title("Simulação da Evolução")
st.pyplot(fig)
retorno_acumulado = (patrimonio[-1]/investimento) * 100
st.write(f'{retorno_acumulado:.1f}%')
st.write("A Evolução do patrimonio utilizou a ótima carteira calculada pelo Indice de Sharpe.")
st.write("Com o retorno logaritmo diário foi possível combinar o pesos dos ativos da carteira ótima e mostrar o retorno acumulado, o valor inicial do investimento e final.")
st.write("Após isso foi calculado o Var Histórico, ou seja o máximo de perdas em um único dia.")
st.write("Para 95% de confiança utiliza-se o quinto percentil (pior 5% dos retornos)")
#Value at Risk
# (95% e/ou 99%) CONTINUA NO BOTÃO DA EVOLUÇÃO
conf1 = 0.05
conf2 = 0.01
var_historico = np.percentile(retorno_diario_carteira, conf1*100)
var_historico2 = np.percentile(retorno_diario_carteira, conf2*100)
var_em_real = investimento * (- var_historico)
var_em_real2 = investimento * (- var_historico2)
st.write(f"VaR diário com 95% de intervalo de confiança: {var_historico*100:.1f}% || R${var_em_real:,.1f}")
st.write(f"VaR diário com 99% de intervalo de confiança: {var_historico2*100:.1f}% || R${var_em_real2:,.1f}")