Commit
·
6ae8536
1
Parent(s):
0845ab1
Publicação do dashboard Portfólio Eficiente com Docker no Hugging Face
Browse files- Dashboard_portfolio_eficiente.py +0 -279
- Dockerfile +28 -0
- README.md +14 -7
Dashboard_portfolio_eficiente.py
DELETED
|
@@ -1,279 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env python
|
| 2 |
-
# coding: utf-8
|
| 3 |
-
|
| 4 |
-
# 
|
| 5 |
-
|
| 6 |
-
# # BI - Distribuições de Probabilidade
|
| 7 |
-
|
| 8 |
-
# ## Bibliotecas
|
| 9 |
-
|
| 10 |
-
# In[8]:
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
import streamlit as st
|
| 14 |
-
import pandas as pd
|
| 15 |
-
import numpy as np
|
| 16 |
-
import plotly.express as px
|
| 17 |
-
import yfinance as yf
|
| 18 |
-
from scipy.stats import norm
|
| 19 |
-
import seaborn as sns
|
| 20 |
-
import plotly.graph_objects as go
|
| 21 |
-
from PIL import Image
|
| 22 |
-
import subprocess
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
# ### Dashboard
|
| 26 |
-
|
| 27 |
-
# In[22]:
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
def load_data(tickers, start_date, end_date):
|
| 31 |
-
data = pd.DataFrame()
|
| 32 |
-
for ticker in tickers:
|
| 33 |
-
data[ticker] = yf.download(ticker, start=start_date, end=end_date)['Close']
|
| 34 |
-
data.reset_index(inplace=True)
|
| 35 |
-
return data
|
| 36 |
-
|
| 37 |
-
def get_ret_vol_sr(weights, log_ret):
|
| 38 |
-
weights = np.array(weights)
|
| 39 |
-
ret = np.sum(log_ret.mean() * weights * 252)
|
| 40 |
-
vol = np.sqrt(np.dot(weights.T, np.dot(log_ret.cov() * 252, weights)))
|
| 41 |
-
sr = ret / vol
|
| 42 |
-
return np.array([ret, vol, sr])
|
| 43 |
-
|
| 44 |
-
def check_sum(weights):
|
| 45 |
-
return np.sum(weights) - 1
|
| 46 |
-
|
| 47 |
-
# Função ajustada de alocação de ativos
|
| 48 |
-
def alocacao_ativos(dataset, dinheiro_total, seed=0, melhores_pesos=[]):
|
| 49 |
-
dataset = dataset.copy()
|
| 50 |
-
|
| 51 |
-
if seed != 0:
|
| 52 |
-
np.random.seed(seed)
|
| 53 |
-
|
| 54 |
-
if len(melhores_pesos) > 0:
|
| 55 |
-
pesos = melhores_pesos
|
| 56 |
-
else:
|
| 57 |
-
pesos = np.random.random(len(dataset.columns) - 1)
|
| 58 |
-
pesos = pesos / np.sum(pesos)
|
| 59 |
-
|
| 60 |
-
colunas = dataset.columns[1:]
|
| 61 |
-
|
| 62 |
-
for i, acao in enumerate(colunas):
|
| 63 |
-
dataset[acao] = (dataset[acao] / dataset[acao][0]) * pesos[i] * dinheiro_total
|
| 64 |
-
|
| 65 |
-
dataset['soma valor'] = dataset[colunas].sum(axis=1)
|
| 66 |
-
dataset['taxa retorno'] = dataset['soma valor'].pct_change() * 100
|
| 67 |
-
|
| 68 |
-
# Criando um novo DataFrame para 'datas' e 'patrimônio'
|
| 69 |
-
patrimonio_df = pd.DataFrame({'Date': dataset['Date'], 'Patrimônio': dataset['soma valor']})
|
| 70 |
-
|
| 71 |
-
return dataset, patrimonio_df, dataset['soma valor'].iloc[-1]
|
| 72 |
-
|
| 73 |
-
# Função para calcular o VaR
|
| 74 |
-
def calcular_var(returns, confidence_level):
|
| 75 |
-
returns = np.array(returns)
|
| 76 |
-
z_score = norm.ppf(confidence_level)
|
| 77 |
-
stdev = np.std(returns)
|
| 78 |
-
var = -(returns.mean() + z_score * stdev)
|
| 79 |
-
return var
|
| 80 |
-
|
| 81 |
-
# Configuração da página
|
| 82 |
-
st.set_page_config(page_title="Teoria do Portfólio Eficiente",
|
| 83 |
-
layout="wide",
|
| 84 |
-
initial_sidebar_state="expanded")
|
| 85 |
-
|
| 86 |
-
# Slider CSS customization for green color
|
| 87 |
-
st.markdown(
|
| 88 |
-
"""
|
| 89 |
-
<style>
|
| 90 |
-
.stSlider > div > div > div > div > div > div {
|
| 91 |
-
background-color: #4CAF50 !important; /* Verde para o slider */
|
| 92 |
-
}
|
| 93 |
-
</style>
|
| 94 |
-
""",
|
| 95 |
-
unsafe_allow_html=True,
|
| 96 |
-
)
|
| 97 |
-
|
| 98 |
-
# Carregar as logos
|
| 99 |
-
logo_unb = Image.open("Logo/MARCADOR.png")
|
| 100 |
-
|
| 101 |
-
# Título e Logos
|
| 102 |
-
col1, col2, col3 = st.columns([1, 6, 1])
|
| 103 |
-
with col1:
|
| 104 |
-
st.image(logo_unb, use_column_width=True)
|
| 105 |
-
with col2:
|
| 106 |
-
st.markdown("<h1 style='text-align: center; color: #003366;'>Teoria do Portfólio Eficiente</h1>",
|
| 107 |
-
unsafe_allow_html=True)
|
| 108 |
-
st.markdown("<h3 style='text-align: center; color: #003366;'>Professor João Gabriel de Moraes Souza</h3>",
|
| 109 |
-
unsafe_allow_html=True) # Inserir nome do professor
|
| 110 |
-
with col3:
|
| 111 |
-
st.image(logo_unb, use_column_width=True)
|
| 112 |
-
|
| 113 |
-
st.markdown("---")
|
| 114 |
-
|
| 115 |
-
tickers = st.multiselect('Selecione as ações:', ['ITUB4.SA', 'BBAS3.SA', 'BBDC4.SA', 'PETR3.SA',
|
| 116 |
-
'CSNA3.SA'],
|
| 117 |
-
default=['ITUB4.SA'])
|
| 118 |
-
|
| 119 |
-
start_date = st.date_input('Data de Início', value=pd.to_datetime('2012-01-01'))
|
| 120 |
-
end_date = st.date_input('Data de Fim', value=pd.to_datetime('2024-12-31'))
|
| 121 |
-
|
| 122 |
-
data = load_data(tickers, start_date, end_date)
|
| 123 |
-
|
| 124 |
-
if st.checkbox('Mostrar Gráfico de Preço'):
|
| 125 |
-
st.subheader('Histórico do Preço das Ações')
|
| 126 |
-
figura = px.line(data, x='Date', y=[data[ticker] for ticker in tickers],
|
| 127 |
-
labels={'value': 'Preço', 'variable': 'Ações'})
|
| 128 |
-
st.plotly_chart(figura)
|
| 129 |
-
|
| 130 |
-
if st.checkbox('Mostrar Histórico de Retorno das Ações'):
|
| 131 |
-
st.subheader('Histórico de Retorno das Ações')
|
| 132 |
-
data.set_index('Date', inplace=True)
|
| 133 |
-
log_retornos = np.log(data / data.shift(1))
|
| 134 |
-
figura_retornos = px.line(log_retornos, labels={'value': 'Retorno Logarítmico', 'variable': 'Ações'})
|
| 135 |
-
st.plotly_chart(figura_retornos)
|
| 136 |
-
|
| 137 |
-
# Adicionando seletores para o número de portfólios e valor inicial de investimento
|
| 138 |
-
num_ports = st.selectbox('Selecione o número de portfólios para a simulação:',
|
| 139 |
-
[100, 1000, 10000], index=2)
|
| 140 |
-
valor_inicial = st.number_input('Valor Inicial de Investimento:', min_value=1000,
|
| 141 |
-
max_value=100000, value=10000, step=1000)
|
| 142 |
-
|
| 143 |
-
global max_sr_ret, max_sr_vol # Definindo variáveis globais
|
| 144 |
-
|
| 145 |
-
if st.checkbox('Realizar Simulação de Portfólio'):
|
| 146 |
-
st.subheader('Simulação de Portfólio')
|
| 147 |
-
|
| 148 |
-
data.dropna(inplace=True)
|
| 149 |
-
log_ret = np.log(data / data.shift(1))
|
| 150 |
-
|
| 151 |
-
np.random.seed(42)
|
| 152 |
-
all_weights = np.zeros((num_ports, len(data.columns)))
|
| 153 |
-
ret_arr = np.zeros(num_ports)
|
| 154 |
-
vol_arr = np.zeros(num_ports)
|
| 155 |
-
sharpe_arr = np.zeros(num_ports)
|
| 156 |
-
|
| 157 |
-
for x in range(num_ports):
|
| 158 |
-
weights = np.array(np.random.random(len(data.columns)))
|
| 159 |
-
weights = weights / np.sum(weights)
|
| 160 |
-
|
| 161 |
-
all_weights[x, :] = weights
|
| 162 |
-
ret_arr[x], vol_arr[x], sharpe_arr[x] = get_ret_vol_sr(weights, log_ret)
|
| 163 |
-
|
| 164 |
-
max_sr_loc = sharpe_arr.argmax()
|
| 165 |
-
max_sr_ret = ret_arr[max_sr_loc]
|
| 166 |
-
max_sr_vol = vol_arr[max_sr_loc]
|
| 167 |
-
|
| 168 |
-
figura_port = px.scatter(x=vol_arr, y=ret_arr, color=sharpe_arr,
|
| 169 |
-
color_continuous_scale='viridis',
|
| 170 |
-
labels={'x': 'Volatilidade', 'y': 'Retorno'}, title='Simulação de Portfólio')
|
| 171 |
-
figura_port.add_scatter(x=[max_sr_vol], y=[max_sr_ret], marker=dict(color='red', size=20),
|
| 172 |
-
name='Melhor Sharpe Ratio')
|
| 173 |
-
st.plotly_chart(figura_port)
|
| 174 |
-
|
| 175 |
-
# Após a simulação de portfólio e identificação dos melhores pesos
|
| 176 |
-
if st.checkbox('Mostrar Pesos do Melhor Portfólio'):
|
| 177 |
-
# Organizando os pesos em um DataFrame para melhor visualização
|
| 178 |
-
ativos = data.columns # Pegando os nomes dos ativos selecionados
|
| 179 |
-
pesos = all_weights[max_sr_loc] # Melhores pesos do portfólio
|
| 180 |
-
pesos_percentuais = pesos * 100 # Convertendo para porcentagem
|
| 181 |
-
|
| 182 |
-
# Criando um DataFrame com os ativos e seus pesos
|
| 183 |
-
df_pesos = pd.DataFrame({
|
| 184 |
-
'Ativo': ativos,
|
| 185 |
-
'Peso (%)': pesos_percentuais
|
| 186 |
-
})
|
| 187 |
-
|
| 188 |
-
# Exibindo o DataFrame com st.table para uma visualização estática
|
| 189 |
-
st.write("### Pesos do Melhor Portfólio")
|
| 190 |
-
st.dataframe(df_pesos.style.format({'Peso (%)': '{:.2f}%'}))
|
| 191 |
-
|
| 192 |
-
# Carregando os dados e executando a função de alocação de ativos
|
| 193 |
-
if st.button('Performance Portfólio'):
|
| 194 |
-
data = load_data(tickers, start_date, end_date)
|
| 195 |
-
dataset, patrimonio_df, soma_valor_final = alocacao_ativos(data, valor_inicial)
|
| 196 |
-
|
| 197 |
-
# Plotando a evolução do patrimônio
|
| 198 |
-
figura_valor_port = px.line(patrimonio_df, x='Date', y='Patrimônio', title='Evolução do Patrimônio da Carteira')
|
| 199 |
-
st.plotly_chart(figura_valor_port)
|
| 200 |
-
|
| 201 |
-
st.write(f'Valor final do portfólio: R$ {soma_valor_final:.2f}')
|
| 202 |
-
|
| 203 |
-
# Seção para cálculo e exibição do VaR
|
| 204 |
-
if 'max_sr_ret' in globals() and 'max_sr_vol' in globals():
|
| 205 |
-
st.markdown("## Cálculo do Value at Risk (VaR)")
|
| 206 |
-
|
| 207 |
-
# Configuração do nível de confiança para o cálculo do VaR
|
| 208 |
-
confidence_level = st.slider('Selecione o Nível de Confiança para o VaR:', min_value=0.80, max_value=0.99, value=0.90)
|
| 209 |
-
|
| 210 |
-
# Seletor para escolher entre retorno percentual e valores monetários
|
| 211 |
-
modo_var = st.radio("Escolha a exibição do VaR:", ("Retorno Percentual", "Valores Monetários"))
|
| 212 |
-
|
| 213 |
-
# Gerar lista de retornos para calcular o VaR (usando os retornos logarítmicos do portfólio simulado)
|
| 214 |
-
returns = np.random.normal(max_sr_ret * 100, max_sr_vol * 100, 10000)
|
| 215 |
-
|
| 216 |
-
# Calcular o VaR no nível de confiança especificado
|
| 217 |
-
var_value = calcular_var(returns, confidence_level)
|
| 218 |
-
|
| 219 |
-
# Ajuste para valores monetários se selecionado
|
| 220 |
-
if modo_var == "Valores Monetários":
|
| 221 |
-
var_value = valor_inicial * (var_value / 100)
|
| 222 |
-
returns = returns * (valor_inicial / 100)
|
| 223 |
-
y_label = "Densidade (Valores Monetários)"
|
| 224 |
-
x_label = "Valores Monetários"
|
| 225 |
-
else:
|
| 226 |
-
y_label = "Densidade (Retorno Percentual)"
|
| 227 |
-
x_label = "Retorno Percentual"
|
| 228 |
-
|
| 229 |
-
st.write(f'VaR no intervalo de confiança de {confidence_level * 100:.0f}%: {var_value:.2f}')
|
| 230 |
-
|
| 231 |
-
# Plotar a distribuição dos retornos com o nível de VaR usando Plotly
|
| 232 |
-
fig = go.Figure()
|
| 233 |
-
|
| 234 |
-
# Adicionando o histograma dos retornos
|
| 235 |
-
fig.add_trace(go.Histogram(
|
| 236 |
-
x=returns,
|
| 237 |
-
nbinsx=50,
|
| 238 |
-
histnorm='probability',
|
| 239 |
-
name='Distribuição de Retornos',
|
| 240 |
-
marker=dict(color='skyblue'),
|
| 241 |
-
opacity=0.75
|
| 242 |
-
))
|
| 243 |
-
|
| 244 |
-
# Linha vertical para indicar o VaR
|
| 245 |
-
fig.add_shape(
|
| 246 |
-
type="line",
|
| 247 |
-
x0=var_value,
|
| 248 |
-
y0=0,
|
| 249 |
-
x1=var_value,
|
| 250 |
-
y1=0.1,
|
| 251 |
-
line=dict(color="red", width=2, dash="dash"),
|
| 252 |
-
name=f'VaR {confidence_level*100:.0f}%'
|
| 253 |
-
)
|
| 254 |
-
|
| 255 |
-
# Anotação para o VaR
|
| 256 |
-
fig.add_annotation(
|
| 257 |
-
x=var_value,
|
| 258 |
-
y=0.1,
|
| 259 |
-
text=f'VaR {confidence_level*100:.0f}%',
|
| 260 |
-
showarrow=True,
|
| 261 |
-
arrowhead=2,
|
| 262 |
-
ax=40,
|
| 263 |
-
ay=-40
|
| 264 |
-
)
|
| 265 |
-
|
| 266 |
-
# Configurações do layout
|
| 267 |
-
fig.update_layout(
|
| 268 |
-
title=f'Distribuição dos Retornos com VaR até {confidence_level * 100:.0f}% de Nível de Confiança',
|
| 269 |
-
xaxis_title=x_label,
|
| 270 |
-
yaxis_title=y_label,
|
| 271 |
-
bargap=0.2,
|
| 272 |
-
)
|
| 273 |
-
|
| 274 |
-
st.plotly_chart(fig)
|
| 275 |
-
else:
|
| 276 |
-
st.warning("Realize a simulação do portfólio para calcular o VaR.")
|
| 277 |
-
|
| 278 |
-
|
| 279 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Dockerfile
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Dockerfile para Dashboard de Portfólio Eficiente com Streamlit via Docker
|
| 2 |
+
# syntax=docker/dockerfile:1
|
| 3 |
+
|
| 4 |
+
# Imagem base leve com Python
|
| 5 |
+
FROM python:3.10-slim
|
| 6 |
+
|
| 7 |
+
# Variáveis de ambiente
|
| 8 |
+
ENV PYTHONUNBUFFERED=1 \
|
| 9 |
+
PYTHONDONTWRITEBYTECODE=1
|
| 10 |
+
|
| 11 |
+
# Diretório de trabalho
|
| 12 |
+
WORKDIR /app
|
| 13 |
+
|
| 14 |
+
# Copiar e instalar dependências
|
| 15 |
+
COPY requirements.txt .
|
| 16 |
+
RUN pip install --upgrade pip \
|
| 17 |
+
&& pip install --no-cache-dir -r requirements.txt
|
| 18 |
+
|
| 19 |
+
# Copiar logo e script principal
|
| 20 |
+
COPY Logo ./Logo
|
| 21 |
+
COPY Dashboard_portfolio_eficiente_online.py .
|
| 22 |
+
|
| 23 |
+
# Expor porta do Streamlit
|
| 24 |
+
EXPOSE 8501
|
| 25 |
+
|
| 26 |
+
# Comando para iniciar o Streamlit
|
| 27 |
+
CMD ["streamlit", "run", "Dashboard_portfolio_eficiente_online.py", "--server.port=8501", "--server.enableCORS=false"]
|
| 28 |
+
|
README.md
CHANGED
|
@@ -1,11 +1,18 @@
|
|
| 1 |
---
|
| 2 |
-
title: Portfólio Eficiente
|
| 3 |
-
emoji: 📈
|
| 4 |
-
colorFrom: blue
|
| 5 |
-
colorTo: green
|
| 6 |
-
sdk:
|
| 7 |
-
|
| 8 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
pinned: false
|
|
|
|
|
|
|
| 10 |
---
|
| 11 |
|
|
|
|
| 1 |
---
|
| 2 |
+
title: "Portfólio Eficiente"
|
| 3 |
+
emoji: "📈"
|
| 4 |
+
colorFrom: "blue"
|
| 5 |
+
colorTo: "green"
|
| 6 |
+
sdk: "docker"
|
| 7 |
+
app_file: "Dockerfile"
|
| 8 |
+
app_port: 8501
|
| 9 |
+
tags:
|
| 10 |
+
- streamlit
|
| 11 |
+
- finanças
|
| 12 |
+
- portfólio
|
| 13 |
+
- estatística
|
| 14 |
pinned: false
|
| 15 |
+
short_description: "Alocação Eficiente de Portfólio"
|
| 16 |
+
license: mit
|
| 17 |
---
|
| 18 |
|