Spaces:
Sleeping
Sleeping
| 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 | |
| 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}") | |