FernandezUNB commited on
Commit
208a0a7
·
verified ·
1 Parent(s): 4af80f0

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +97 -116
app.py CHANGED
@@ -6,60 +6,54 @@ import plotly.graph_objects as go
6
  from scipy import optimize, stats
7
  from datetime import datetime
8
  import requests
9
- import yfinance as yf
10
  import warnings
11
-
12
  warnings.filterwarnings("ignore")
13
 
14
- # ================= CONFIGURAÇÃO DO BRAPI =================
15
- BRAPI_API_KEY = "dCDdw4V35VedwMPBQCLM71" # Sua chave BRAPI
 
 
16
  BRAPI_HEADERS = {"Authorization": f"Bearer {BRAPI_API_KEY}"} if BRAPI_API_KEY else {}
17
 
18
- # ================= CLASSE DO SIMULADOR =================
 
 
19
  class PortfolioSimulator:
20
  def __init__(self):
21
  self.available_stocks = {
22
- 'BBAS3.SA': 'Banco do Brasil',
23
- 'ITSA4.SA': 'Itaúsa',
24
- 'TAEE11.SA': 'Taesa',
25
- 'TTEN3.SA': '3tentos',
26
- 'BPAC11.SA': 'BTG Pactual',
27
- 'PETR4.SA': 'Petrobras',
28
- 'VALE3.SA': 'Vale',
29
- 'ITUB4.SA': 'Itaú Unibanco',
30
- 'BBDC4.SA': 'Bradesco',
31
- 'WEGE3.SA': 'WEG',
32
- 'MGLU3.SA': 'Magazine Luiza',
33
- 'B3SA3.SA': 'B3',
34
- 'RENT3.SA': 'Localiza',
35
- 'ABEV3.SA': 'Ambev',
36
- '^BVSP': 'IBOVESPA'
37
  }
38
 
39
- def download_data(self, selected_stocks, start_date, end_date):
40
- """Baixa dados históricos usando BRAPI (ou yfinance como fallback)"""
41
  df = pd.DataFrame()
42
- for stock in selected_stocks:
43
- ticker = stock.replace('.SA','')
44
  try:
45
- if stock != '^BVSP':
46
- # BRAPI request
47
- url = f"https://brapi.dev/api/quote/{ticker}?range=5y&interval=1d"
48
- resp = requests.get(url, headers=BRAPI_HEADERS)
49
- resp.raise_for_status()
50
- data = pd.DataFrame(resp.json()['results'][0]['historical'])
51
- data['datetime'] = pd.to_datetime(data['datetime'])
52
- data.set_index('datetime', inplace=True)
53
- df[stock] = data['close']
54
- print(f"✓ {stock} baixado via BRAPI")
55
- else:
56
- # yfinance fallback para IBOV
57
- data = yf.download(stock, start=start_date, end=end_date)['Close']
58
- df[stock] = data
59
- print(f"✓ {stock} baixado via yfinance")
60
  except Exception as e:
61
- print(f"✗ Erro ao baixar {stock}: {e}")
62
- df = df.sort_index().dropna()
63
  return df
64
 
65
  def calculate_returns(self, prices_df):
@@ -88,111 +82,98 @@ class PortfolioSimulator:
88
  mean_returns = returns_df.mean()
89
  cov_matrix = returns_df.cov()
90
 
91
- def neg_sharpe(weights):
92
  port_return = np.sum(weights * mean_returns)
93
  port_vol = np.sqrt(np.dot(weights.T, np.dot(cov_matrix, weights)))
94
  return -port_return / port_vol if port_vol > 0 else 0
95
 
96
- constraints = {'type': 'eq', 'fun': lambda x: np.sum(x) - 1}
97
- bounds = tuple((0,1) for _ in range(n_assets))
98
- init_guess = [1/n_assets]*n_assets
99
- result = optimize.minimize(neg_sharpe, init_guess, method='SLSQP', bounds=bounds, constraints=constraints)
 
 
 
 
 
 
 
100
  return result.x
101
 
102
  def simulate_portfolio(self, prices_df, weights, initial_investment=35000):
103
  norm_prices = prices_df / prices_df.iloc[0]
104
- portfolio = norm_prices * weights * initial_investment
105
- portfolio['Total'] = portfolio.sum(axis=1)
106
- portfolio['Daily_Return'] = portfolio['Total'].pct_change()
107
- return portfolio
108
 
109
- # ================= FUNÇÃO DE ANÁLISE =================
 
 
110
  def run_analysis(selected_stocks, num_simulations, initial_investment, start_date_str, end_date_str):
 
111
  try:
112
  start_date = pd.to_datetime(start_date_str)
113
  end_date = pd.to_datetime(end_date_str)
114
-
115
  if not isinstance(selected_stocks, list):
116
  selected_stocks = [selected_stocks]
117
- if '^BVSP' not in selected_stocks:
118
- selected_stocks.append('^BVSP')
119
 
120
- sim = PortfolioSimulator()
121
- prices = sim.download_data(selected_stocks, start_date, end_date)
122
- returns = sim.calculate_returns(prices)
123
- weights_mc, ret_arr, vol_arr, sharpe_arr = sim.monte_carlo_simulation(returns, int(num_simulations))
124
  max_idx = np.argmax(sharpe_arr)
125
  optimal_weights = weights_mc[max_idx]
126
 
127
- portfolio = sim.simulate_portfolio(prices, optimal_weights, float(initial_investment))
128
-
129
- # Gráfico 1: Composição da Carteira
130
- fig1 = px.pie(
131
- names=selected_stocks,
132
- values=optimal_weights,
133
- title="Composição da Carteira Ótima"
134
- )
135
-
136
- # Gráfico 2: Evolução do Patrimônio
137
- fig2 = px.line(
138
- portfolio,
139
- y="Total",
140
- title="Evolução do Patrimônio",
141
- labels={"Total":"Valor (R$)", "index":"Data"}
142
- )
143
-
144
- # Gráfico 3: Fronteira Eficiente
145
- fig3 = go.Figure()
146
- fig3.add_trace(go.Scatter(x=vol_arr, y=ret_arr, mode='markers',
147
- marker=dict(color=sharpe_arr, colorscale='Viridis', showscale=True),
148
- name='Portfólios Simulados'))
149
- fig3.add_trace(go.Scatter(x=[vol_arr[max_idx]], y=[ret_arr[max_idx]], mode='markers',
150
- marker=dict(color='red', size=15, symbol='star'), name='Máx Sharpe'))
151
- fig3.update_layout(title="Fronteira Eficiente", xaxis_title="Volatilidade", yaxis_title="Retorno")
152
-
153
- # Gráfico 4: Preços Normalizados
154
- norm_prices = prices / prices.iloc[0]
155
- fig4 = px.line(norm_prices, title="Preços Normalizados das Ações")
156
 
157
- return "✅ Simulação concluída com sucesso!", fig1, fig2, fig3, fig4
 
 
 
 
 
158
 
 
159
  except Exception as e:
160
- return f"❌ Erro na simulação: {str(e)}", None, None, None, None
161
-
162
- # ================= INTERFACE GRADIO =================
163
- sim = PortfolioSimulator()
164
-
165
- with gr.Blocks(title="Simulador de Portfólio Interativo") as demo:
166
- gr.Markdown("# 🎯 Simulador de Portfólio Interativo")
167
- gr.Markdown("Monte Carlo + Fronteira Eficiente + Plotly interativo")
168
-
169
  with gr.Row():
170
  with gr.Column():
171
- selected_stocks = gr.CheckboxGroup(
172
- choices=list(sim.available_stocks.keys()),
173
- label="Selecione Ações",
174
- value=['BBAS3.SA', 'ITSA4.SA', 'TAEE11.SA', '^BVSP']
175
- )
176
- num_simulations = gr.Slider(1000,200000,value=50000,step=1000,label="Número de Simulações")
177
- initial_investment = gr.Number(value=35000,label="Investimento Inicial (R$)")
178
- start_date = gr.Textbox("2020-01-01", label="Data Inicial (YYYY-MM-DD)")
179
- end_date = gr.Textbox("2024-01-01", label="Data Final (YYYY-MM-DD)")
180
  run_btn = gr.Button("🚀 Executar Simulação", variant="primary")
181
-
182
  with gr.Column():
183
- result_text = gr.Markdown()
184
-
185
  with gr.Row():
186
  pie_chart = gr.Plot(label="Composição da Carteira")
187
  evolution_plot = gr.Plot(label="Evolução do Patrimônio")
188
  efficient_frontier = gr.Plot(label="Fronteira Eficiente")
189
- prices_plot = gr.Plot(label="Preços Normalizados")
190
-
191
- run_btn.click(
192
- fn=run_analysis,
193
- inputs=[selected_stocks,num_simulations,initial_investment,start_date,end_date],
194
- outputs=[result_text,pie_chart,evolution_plot,efficient_frontier,prices_plot]
195
- )
196
 
197
  if __name__ == "__main__":
198
  demo.launch(share=True)
 
6
  from scipy import optimize, stats
7
  from datetime import datetime
8
  import requests
 
9
  import warnings
 
10
  warnings.filterwarnings("ignore")
11
 
12
+ # =====================
13
+ # CONFIGURAÇÃO BRAPI
14
+ # =====================
15
+ BRAPI_API_KEY = "dCDdw4V35VedwMPBQCLM71" # Coloque sua chave aqui
16
  BRAPI_HEADERS = {"Authorization": f"Bearer {BRAPI_API_KEY}"} if BRAPI_API_KEY else {}
17
 
18
+ # =====================
19
+ # CLASSE SIMULADOR
20
+ # =====================
21
  class PortfolioSimulator:
22
  def __init__(self):
23
  self.available_stocks = {
24
+ 'BBAS3': 'Banco do Brasil',
25
+ 'ITSA4': 'Itaúsa',
26
+ 'TAEE11': 'Taesa',
27
+ 'TTEN3': '3tentos',
28
+ 'BPAC11': 'BTG Pactual',
29
+ 'PETR4': 'Petrobras',
30
+ 'VALE3': 'Vale',
31
+ 'ITUB4': 'Itaú Unibanco',
32
+ 'BBDC4': 'Bradesco',
33
+ 'WEGE3': 'WEG',
34
+ 'MGLU3': 'Magazine Luiza',
35
+ 'B3SA3': 'B3',
36
+ 'RENT3': 'Localiza',
37
+ 'ABEV3': 'Ambev',
38
+ 'IBOV': 'IBOVESPA'
39
  }
40
 
41
+ def download_data(self, tickers, start_date, end_date):
 
42
  df = pd.DataFrame()
43
+ for ticker in tickers:
 
44
  try:
45
+ url = f"https://brapi.dev/api/quote/{ticker}?range=5y&interval=1d"
46
+ r = requests.get(url, headers=BRAPI_HEADERS, timeout=10)
47
+ r.raise_for_status()
48
+ data = r.json().get('results', [])[0]['historical']
49
+ temp_df = pd.DataFrame(data)
50
+ temp_df['date'] = pd.to_datetime(temp_df['date'])
51
+ temp_df = temp_df.set_index('date')
52
+ df[ticker] = temp_df['close']
53
+ print(f"✓ {ticker} baixado via BRAPI")
 
 
 
 
 
 
54
  except Exception as e:
55
+ print(f"✗ Erro ao baixar {ticker} via BRAPI: {e}")
56
+ df = df[start_date:end_date].dropna()
57
  return df
58
 
59
  def calculate_returns(self, prices_df):
 
82
  mean_returns = returns_df.mean()
83
  cov_matrix = returns_df.cov()
84
 
85
+ def negative_sharpe(weights):
86
  port_return = np.sum(weights * mean_returns)
87
  port_vol = np.sqrt(np.dot(weights.T, np.dot(cov_matrix, weights)))
88
  return -port_return / port_vol if port_vol > 0 else 0
89
 
90
+ constraints = ({'type': 'eq', 'fun': lambda x: np.sum(x) - 1})
91
+ bounds = tuple((0, 1) for _ in range(n_assets))
92
+ initial_guess = [1/n_assets] * n_assets
93
+
94
+ result = optimize.minimize(
95
+ negative_sharpe,
96
+ initial_guess,
97
+ method='SLSQP',
98
+ bounds=bounds,
99
+ constraints=constraints
100
+ )
101
  return result.x
102
 
103
  def simulate_portfolio(self, prices_df, weights, initial_investment=35000):
104
  norm_prices = prices_df / prices_df.iloc[0]
105
+ portfolio_value = (norm_prices * weights * initial_investment).sum(axis=1)
106
+ portfolio_return = portfolio_value.pct_change().dropna()
107
+ return portfolio_value, portfolio_return
 
108
 
109
+ # =====================
110
+ # FUNÇÃO PRINCIPAL
111
+ # =====================
112
  def run_analysis(selected_stocks, num_simulations, initial_investment, start_date_str, end_date_str):
113
+ simulator = PortfolioSimulator()
114
  try:
115
  start_date = pd.to_datetime(start_date_str)
116
  end_date = pd.to_datetime(end_date_str)
 
117
  if not isinstance(selected_stocks, list):
118
  selected_stocks = [selected_stocks]
119
+ prices_df = simulator.download_data(selected_stocks, start_date, end_date)
120
+ returns_df = simulator.calculate_returns(prices_df)
121
 
122
+ weights_mc, ret_arr, vol_arr, sharpe_arr = simulator.monte_carlo_simulation(returns_df, num_simulations)
 
 
 
123
  max_idx = np.argmax(sharpe_arr)
124
  optimal_weights = weights_mc[max_idx]
125
 
126
+ math_weights = simulator.optimize_portfolio(returns_df)
127
+ portfolio_value, portfolio_return = simulator.simulate_portfolio(prices_df, optimal_weights, initial_investment)
128
+
129
+ # ==== GRÁFICOS INTERATIVOS PLOTLY ====
130
+ fig_weights = px.pie(names=selected_stocks, values=optimal_weights, title="Composição da Carteira Ótima")
131
+ fig_evolution = go.Figure()
132
+ fig_evolution.add_trace(go.Scatter(x=portfolio_value.index, y=portfolio_value.values, mode='lines', name='Patrimônio'))
133
+ fig_evolution.update_layout(title='Evolução do Patrimônio', yaxis_title='Valor (R$)')
134
+ fig_frontier = go.Figure()
135
+ fig_frontier.add_trace(go.Scatter(x=vol_arr, y=ret_arr, mode='markers', marker=dict(color=sharpe_arr, colorscale='Viridis', showscale=True), name='Carteiras Simuladas'))
136
+ fig_frontier.add_trace(go.Scatter(x=[vol_arr[max_idx]], y=[ret_arr[max_idx]], mode='markers', marker=dict(color='red', size=15, symbol='star'), name='Carteira Ótima'))
137
+ fig_frontier.update_layout(title='Fronteira Eficiente', xaxis_title='Volatilidade', yaxis_title='Retorno')
138
+ fig_prices = go.Figure()
139
+ for stock in selected_stocks:
140
+ fig_prices.add_trace(go.Scatter(x=prices_df.index, y=prices_df[stock], mode='lines', name=stock))
141
+ fig_prices.update_layout(title='Preços das Ações', yaxis_title='Preço')
 
 
 
 
 
 
 
 
 
 
 
 
 
142
 
143
+ result_text = f"""
144
+ ## 📊 Resultados
145
+ **Investimento Inicial:** R$ {initial_investment:,.2f}
146
+ **Valor Final:** R$ {portfolio_value.iloc[-1]:,.2f}
147
+ **Retorno Total:** {(portfolio_value.iloc[-1]/initial_investment - 1)*100:.2f}%
148
+ """
149
 
150
+ return result_text, fig_weights, fig_evolution, fig_frontier, fig_prices
151
  except Exception as e:
152
+ return f"❌ Erro na simulação: {e}", None, None, None, None
153
+
154
+ # =====================
155
+ # INTERFACE GRADIO
156
+ # =====================
157
+ simulator = PortfolioSimulator()
158
+ with gr.Blocks(title="Simulador de Portfólio - BRAPI") as demo:
159
+ gr.Markdown("# 🎯 Simulador de Portfólio - BRAPI")
 
160
  with gr.Row():
161
  with gr.Column():
162
+ selected_stocks = gr.CheckboxGroup(label="Selecione as Ações", choices=list(simulator.available_stocks.keys()), value=['BBAS3','ITSA4','TAEE11'])
163
+ num_simulations = gr.Slider(label="Número de Simulações", minimum=1000, maximum=200000, value=50000, step=1000)
164
+ initial_investment = gr.Number(label="Investimento Inicial (R$)", value=35000)
165
+ start_date = gr.Textbox(label="Data Inicial (YYYY-MM-DD)", value="2018-01-01")
166
+ end_date = gr.Textbox(label="Data Final (YYYY-MM-DD)", value="2024-01-01")
 
 
 
 
167
  run_btn = gr.Button("🚀 Executar Simulação", variant="primary")
 
168
  with gr.Column():
169
+ results_text = gr.Markdown()
 
170
  with gr.Row():
171
  pie_chart = gr.Plot(label="Composição da Carteira")
172
  evolution_plot = gr.Plot(label="Evolução do Patrimônio")
173
  efficient_frontier = gr.Plot(label="Fronteira Eficiente")
174
+ prices_plot = gr.Plot(label="Preços das Ações")
175
+ run_btn.click(fn=run_analysis, inputs=[selected_stocks, num_simulations, initial_investment, start_date, end_date],
176
+ outputs=[results_text, pie_chart, evolution_plot, efficient_frontier, prices_plot])
 
 
 
 
177
 
178
  if __name__ == "__main__":
179
  demo.launch(share=True)