Create app.py
Browse files
app.py
ADDED
@@ -0,0 +1,307 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
|
3 |
+
|
4 |
+
|
5 |
+
|
6 |
+
from flask import Flask, render_template, request, send_file
|
7 |
+
import matplotlib.pyplot as plt
|
8 |
+
import io
|
9 |
+
import base64
|
10 |
+
from datetime import datetime
|
11 |
+
import pandas as pd
|
12 |
+
|
13 |
+
app = Flask(__name__)
|
14 |
+
|
15 |
+
# Função para formatar valor em reais
|
16 |
+
def formatar_brl(valor):
|
17 |
+
return f"R$ {valor:,.2f}".replace(",", "X").replace(".", ",").replace("X", ".")
|
18 |
+
|
19 |
+
# Função para gerar gráfico e retornar base64
|
20 |
+
def gerar_grafico(valores, labels):
|
21 |
+
plt.figure(figsize=(8,4))
|
22 |
+
for label in labels:
|
23 |
+
plt.plot(valores['Ano'], valores[label], label=label)
|
24 |
+
plt.title("Projeção dos Investimentos ao longo dos anos")
|
25 |
+
plt.xlabel("Ano")
|
26 |
+
plt.ylabel("Valor (R$)")
|
27 |
+
plt.legend()
|
28 |
+
plt.grid(True)
|
29 |
+
plt.tight_layout()
|
30 |
+
|
31 |
+
img = io.BytesIO()
|
32 |
+
plt.savefig(img, format='png')
|
33 |
+
plt.close()
|
34 |
+
img.seek(0)
|
35 |
+
img_base64 = base64.b64encode(img.getvalue()).decode()
|
36 |
+
return img_base64
|
37 |
+
|
38 |
+
# Rota principal com formulário e relatório
|
39 |
+
@app.route("/", methods=["GET", "POST"])
|
40 |
+
def index():
|
41 |
+
if request.method == "POST":
|
42 |
+
try:
|
43 |
+
capital = float(request.form["capital"])
|
44 |
+
studio_ret = float(request.form["studio_ret"])
|
45 |
+
valorizacao = float(request.form["valorizacao"])
|
46 |
+
franquia_ret = float(request.form["franquia_ret"])
|
47 |
+
acoes_ret = float(request.form["acoes_ret"])
|
48 |
+
renda_fixa = float(request.form["renda_fixa"])
|
49 |
+
inflacao = float(request.form["inflacao"])
|
50 |
+
anos = int(request.form["anos"])
|
51 |
+
|
52 |
+
# Cálculo dos valores ao longo dos anos
|
53 |
+
anos_lista = list(range(0, anos + 1))
|
54 |
+
|
55 |
+
df = pd.DataFrame({"Ano": anos_lista})
|
56 |
+
# Studio: capital * (1 + retorno + valorização)^ano
|
57 |
+
df["Studio"] = capital * ((1 + (studio_ret + valorizacao)/100) ** df["Ano"])
|
58 |
+
# Franquia: capital + retorno fixo anual * ano
|
59 |
+
df["Franquia"] = capital + franquia_ret * df["Ano"]
|
60 |
+
# Ações: capital * (1 + retorno)^ano
|
61 |
+
df["Ações"] = capital * ((1 + acoes_ret/100) ** df["Ano"])
|
62 |
+
# Renda Fixa: capital * (1 + retorno)^ano
|
63 |
+
df["Renda Fixa"] = capital * ((1 + renda_fixa/100) ** df["Ano"])
|
64 |
+
|
65 |
+
# Valores finais para comparação
|
66 |
+
valores_finais = df.iloc[-1, 1:]
|
67 |
+
investimento_mais_valorizado = valores_finais.idxmax()
|
68 |
+
valor_mais_alto = valores_finais.max()
|
69 |
+
|
70 |
+
# Preparar resumo tabela
|
71 |
+
resumo = []
|
72 |
+
for investimento in ["Studio", "Franquia", "Ações", "Renda Fixa"]:
|
73 |
+
valor_final = valores_finais[investimento]
|
74 |
+
retorno_abs = valor_final - capital
|
75 |
+
retorno_pct = (retorno_abs / capital) * 100
|
76 |
+
resumo.append({
|
77 |
+
"Investimento": investimento,
|
78 |
+
"Valor Final": formatar_brl(valor_final),
|
79 |
+
"Retorno Absoluto": formatar_brl(retorno_abs),
|
80 |
+
"Retorno (%)": f"{retorno_pct:.2f}%"
|
81 |
+
})
|
82 |
+
|
83 |
+
# Montar tabela html para PDF
|
84 |
+
tabela_html = """
|
85 |
+
<table>
|
86 |
+
<thead>
|
87 |
+
<tr>
|
88 |
+
<th style="text-align:left;">Investimento</th>
|
89 |
+
<th>Valor Final</th>
|
90 |
+
<th>Retorno Absoluto</th>
|
91 |
+
<th>Retorno (%)</th>
|
92 |
+
</tr>
|
93 |
+
</thead>
|
94 |
+
<tbody>
|
95 |
+
"""
|
96 |
+
for r in resumo:
|
97 |
+
tabela_html += f"""
|
98 |
+
<tr>
|
99 |
+
<td style="text-align:left;">{r['Investimento']}</td>
|
100 |
+
<td>{r['Valor Final']}</td>
|
101 |
+
<td>{r['Retorno Absoluto']}</td>
|
102 |
+
<td>{r['Retorno (%)']}</td>
|
103 |
+
</tr>
|
104 |
+
"""
|
105 |
+
tabela_html += "</tbody></table>"
|
106 |
+
|
107 |
+
# Gerar gráfico
|
108 |
+
img_base64 = gerar_grafico(df, ["Studio", "Franquia", "Ações", "Renda Fixa"])
|
109 |
+
|
110 |
+
analise_final = (f"Após análise dos cenários projetados para {anos} anos, o investimento "
|
111 |
+
f"<strong>{investimento_mais_valorizado}</strong> apresenta o melhor desempenho, "
|
112 |
+
f"com um valor final estimado de <strong>{formatar_brl(valor_mais_alto)}</strong>, "
|
113 |
+
"equivalente a um retorno significativo sobre o capital inicial.")
|
114 |
+
|
115 |
+
comentario_extra = None # Caso queira adicionar algo extra
|
116 |
+
|
117 |
+
return render_template("relatorio.html",
|
118 |
+
capital=capital,
|
119 |
+
studio_ret=studio_ret,
|
120 |
+
valorizacao=valorizacao,
|
121 |
+
franquia_ret=franquia_ret,
|
122 |
+
acoes_ret=acoes_ret,
|
123 |
+
renda_fixa=renda_fixa,
|
124 |
+
inflacao=inflacao,
|
125 |
+
anos=anos,
|
126 |
+
resumo=resumo,
|
127 |
+
tabela_html=tabela_html,
|
128 |
+
grafico=img_base64,
|
129 |
+
analise_final=analise_final,
|
130 |
+
comentario_extra=comentario_extra,
|
131 |
+
investimento_mais_valorizado=investimento_mais_valorizado,
|
132 |
+
valor_mais_alto=valor_mais_alto,
|
133 |
+
now=datetime.now(),
|
134 |
+
formatar_brl=formatar_brl)
|
135 |
+
except Exception as e:
|
136 |
+
return f"Erro no processamento: {e}"
|
137 |
+
|
138 |
+
# GET: apenas mostra o formulário
|
139 |
+
return '''
|
140 |
+
<h2>Simulação de Investimentos</h2>
|
141 |
+
<form method="post">
|
142 |
+
<label>Capital Inicial (R$): <input name="capital" type="number" step="0.01" value="100000" required></label><br><br>
|
143 |
+
<label>Retorno Studio (% ao ano): <input name="studio_ret" type="number" step="0.01" value="8" required></label><br><br>
|
144 |
+
<label>Valorização Studio (% ao ano): <input name="valorizacao" type="number" step="0.01" value="5" required></label><br><br>
|
145 |
+
<label>Retorno Franquia (R$ ao ano): <input name="franquia_ret" type="number" step="0.01" value="7000" required></label><br><br>
|
146 |
+
<label>Retorno Ações (% ao ano): <input name="acoes_ret" type="number" step="0.01" value="10" required></label><br><br>
|
147 |
+
<label>Retorno Renda Fixa (% ao ano): <input name="renda_fixa" type="number" step="0.01" value="6" required></label><br><br>
|
148 |
+
<label>Inflação (% ao ano): <input name="inflacao" type="number" step="0.01" value="4" required></label><br><br>
|
149 |
+
<label>Anos de Projeção: <input name="anos" type="number" value="5" required></label><br><br>
|
150 |
+
<button type="submit">Gerar Relatório</button>
|
151 |
+
</form>
|
152 |
+
'''
|
153 |
+
|
154 |
+
# Página do relatório com template HTML
|
155 |
+
@app.route("/relatorio")
|
156 |
+
def relatorio():
|
157 |
+
# Esta rota só funciona se o formulário for submetido,
|
158 |
+
# por isso recomendamos usar a rota '/' para gerar o relatório.
|
159 |
+
return "Acesse a página inicial e preencha o formulário para gerar o relatório."
|
160 |
+
|
161 |
+
# Geração do PDF com WeasyPrint ou pdfkit (exemplo com pdfkit)
|
162 |
+
from flask import make_response
|
163 |
+
import pdfkit
|
164 |
+
|
165 |
+
@app.route("/download_pdf", methods=["POST"])
|
166 |
+
def download_pdf():
|
167 |
+
try:
|
168 |
+
# Receber os dados do form para gerar relatório no pdf
|
169 |
+
capital = float(request.form["capital"])
|
170 |
+
studio_ret = float(request.form["studio_ret"])
|
171 |
+
valorizacao = float(request.form["valorizacao"])
|
172 |
+
franquia_ret = float(request.form["franquia_ret"])
|
173 |
+
acoes_ret = float(request.form["acoes_ret"])
|
174 |
+
renda_fixa = float(request.form["renda_fixa"])
|
175 |
+
inflacao = float(request.form["inflacao"])
|
176 |
+
anos = int(request.form["anos"])
|
177 |
+
|
178 |
+
# Repetir cálculo para gerar dados e gráfico
|
179 |
+
anos_lista = list(range(0, anos + 1))
|
180 |
+
|
181 |
+
df = pd.DataFrame({"Ano": anos_lista})
|
182 |
+
df["Studio"] = capital * ((1 + (studio_ret + valorizacao)/100) ** df["Ano"])
|
183 |
+
df["Franquia"] = capital + franquia_ret * df["Ano"]
|
184 |
+
df["Ações"] = capital * ((1 + acoes_ret/100) ** df["Ano"])
|
185 |
+
df["Renda Fixa"] = capital * ((1 + renda_fixa/100) ** df["Ano"])
|
186 |
+
|
187 |
+
valores_finais = df.iloc[-1, 1:]
|
188 |
+
investimento_mais_valorizado = valores_finais.idxmax()
|
189 |
+
valor_mais_alto = valores_finais.max()
|
190 |
+
|
191 |
+
resumo = []
|
192 |
+
for investimento in ["Studio", "Franquia", "Ações", "Renda Fixa"]:
|
193 |
+
valor_final = valores_finais[investimento]
|
194 |
+
retorno_abs = valor_final - capital
|
195 |
+
retorno_pct = (retorno_abs / capital) * 100
|
196 |
+
resumo.append({
|
197 |
+
"Investimento": investimento,
|
198 |
+
"Valor Final": formatar_brl(valor_final),
|
199 |
+
"Retorno Absoluto": formatar_brl(retorno_abs),
|
200 |
+
"Retorno (%)": f"{retorno_pct:.2f}%"
|
201 |
+
})
|
202 |
+
|
203 |
+
tabela_html = """
|
204 |
+
<table>
|
205 |
+
<thead>
|
206 |
+
<tr>
|
207 |
+
<th style="text-align:left;">Investimento</th>
|
208 |
+
<th>Valor Final</th>
|
209 |
+
<th>Retorno Absoluto</th>
|
210 |
+
<th>Retorno (%)</th>
|
211 |
+
</tr>
|
212 |
+
</thead>
|
213 |
+
<tbody>
|
214 |
+
"""
|
215 |
+
for r in resumo:
|
216 |
+
tabela_html += f"""
|
217 |
+
<tr>
|
218 |
+
<td style="text-align:left;">{r['Investimento']}</td>
|
219 |
+
<td>{r['Valor Final']}</td>
|
220 |
+
<td>{r['Retorno Absoluto']}</td>
|
221 |
+
<td>{r['Retorno (%)']}</td>
|
222 |
+
</tr>
|
223 |
+
"""
|
224 |
+
tabela_html += "</tbody></table>"
|
225 |
+
|
226 |
+
img_base64 = gerar_grafico(df, ["Studio", "Franquia", "Ações", "Renda Fixa"])
|
227 |
+
|
228 |
+
analise_final = (f"Após análise dos cenários projetados para {anos} anos, o investimento "
|
229 |
+
f"<strong>{investimento_mais_valorizado}</strong> apresenta o melhor desempenho, "
|
230 |
+
f"com um valor final estimado de <strong>{formatar_brl(valor_mais_alto)}</strong>, "
|
231 |
+
"equivalente a um retorno significativo sobre o capital inicial.")
|
232 |
+
|
233 |
+
consideracoes_finais = f"""
|
234 |
+
<h2>Considerações Finais</h2>
|
235 |
+
<p>{analise_final}</p>
|
236 |
+
<p>
|
237 |
+
O investimento que apresentou o maior retorno neste cenário foi o <strong>{investimento_mais_valorizado}</strong>,
|
238 |
+
atingindo um valor patrimonial final estimado em <strong>{formatar_brl(valor_mais_alto)}</strong>.
|
239 |
+
</p>
|
240 |
+
<p>
|
241 |
+
<strong>Valor Patrimonial</strong> refere-se ao valor total acumulado do investimento, incluindo a valorização do ativo e os rendimentos obtidos ao longo do tempo.
|
242 |
+
Conforme as normas contábeis e práticas de avaliação de investidores profissionais, o valor patrimonial é fundamental para mensurar a saúde financeira e o crescimento do patrimônio líquido do investidor.
|
243 |
+
Ele serve como indicador da capacidade do investimento de gerar riqueza real, levando em conta tanto a valorização de mercado quanto a geração de renda.
|
244 |
+
</p>
|
245 |
+
"""
|
246 |
+
|
247 |
+
html = f"""
|
248 |
+
<html>
|
249 |
+
<head>
|
250 |
+
<meta charset="utf-8">
|
251 |
+
<style>
|
252 |
+
body {{ font-family: Arial, sans-serif; font-size: 14px; margin: 20px; }}
|
253 |
+
h2 {{ color: #2c3e50; }}
|
254 |
+
table {{ width: 100%; border-collapse: collapse; margin-top: 20px; }}
|
255 |
+
th, td {{ border: 1px solid #ccc; padding: 6px; text-align: right; }}
|
256 |
+
th {{ background-color: #f0f0f0; }}
|
257 |
+
.destaque {{ background: #e8f5e9; padding: 10px; margin-top: 20px; border-left: 6px solid #2e7d32; }}
|
258 |
+
</style>
|
259 |
+
</head>
|
260 |
+
<body>
|
261 |
+
<h2>Relatório de Simulação de Investimentos</h2>
|
262 |
+
<p>Data da Simulação: {datetime.today().strftime('%d/%m/%Y')}</p>
|
263 |
+
<p>Capital Inicial: <strong>{formatar_brl(capital)}</strong></p>
|
264 |
+
|
265 |
+
<h3>Dados de Entrada</h3>
|
266 |
+
<table>
|
267 |
+
<tr><th>Parâmetro</th><th>Valor</th></tr>
|
268 |
+
<tr><td>Retorno Studio (%)</td><td>{studio_ret}%</td></tr>
|
269 |
+
<tr><td>Valorização Studio (%)</td><td>{valorizacao}%</td></tr>
|
270 |
+
<tr><td>Retorno Franquia (R$)</td><td>{formatar_brl(franquia_ret)}</td></tr>
|
271 |
+
<tr><td>Retorno Ações (%)</td><td>{acoes_ret}%</td></tr>
|
272 |
+
<tr><td>Retorno Renda Fixa (%)</td><td>{renda_fixa}%</td></tr>
|
273 |
+
<tr><td>Inflação (%)</td><td>{inflacao}%</td></tr>
|
274 |
+
</table>
|
275 |
+
|
276 |
+
<div class="destaque">{analise_final}</div>
|
277 |
+
|
278 |
+
<img src="data:image/png;base64,{img_base64}" width="100%" />
|
279 |
+
|
280 |
+
<h3>Resumo dos Investimentos</h3>
|
281 |
+
{tabela_html}
|
282 |
+
|
283 |
+
{consideracoes_finais}
|
284 |
+
</body>
|
285 |
+
</html>
|
286 |
+
"""
|
287 |
+
|
288 |
+
# Gerar PDF com pdfkit (você precisa do wkhtmltopdf instalado)
|
289 |
+
options = {
|
290 |
+
'encoding': 'UTF-8',
|
291 |
+
'page-size': 'A4',
|
292 |
+
'margin-top': '10mm',
|
293 |
+
'margin-bottom': '10mm',
|
294 |
+
'margin-left': '10mm',
|
295 |
+
'margin-right': '10mm',
|
296 |
+
}
|
297 |
+
pdf = pdfkit.from_string(html, False, options=options)
|
298 |
+
|
299 |
+
response = make_response(pdf)
|
300 |
+
response.headers['Content-Type'] = 'application/pdf'
|
301 |
+
response.headers['Content-Disposition'] = 'attachment; filename=relatorio_investimentos.pdf'
|
302 |
+
return response
|
303 |
+
except Exception as e:
|
304 |
+
return f"Erro na geração do PDF: {e}"
|
305 |
+
|
306 |
+
if __name__ == "__main__":
|
307 |
+
app.run(debug=True)
|