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}")