Simulador de Amortização Extraordinária: SAC vs PRICE | O Estrategista Imobiliário
Calcule o impacto de amortizações mensais ou anuais no seu financiamento. Descubra quanto você economiza em juros e quanto tempo reduz no prazo total.
Palavras-chave
simulador amortização extraordinária, quitação antecipada financiamento, reduzir prazo ou parcela, SAC vs PRICE amortização
Simulador de Amortização Estratégica
Esta ferramenta permite visualizar o impacto real de aportes extras no seu saldo devedor.
Dica de Uso: Compare a estratégia de Redução de Prazo (para máxima economia de juros) com a Redução de Valor (para maior fôlego no orçamento mensal).
#| '!! shinylive warning !!': |
#| shinylive does not work in self-contained HTML documents.
#| Please set `embed-resources: false` in your metadata.
#| standalone: true
#| viewerHeight: 820
## file: app.py
#| standalone: true
#| viewerHeight: 820
from shiny import App, render, ui, reactive
import pandas as pd
import plotly.graph_objs as go
from shinywidgets import output_widget, render_widget
# --- MOTOR FINANCEIRO COM AMORTIZAÇÃO ---
def calcular_simulacao(valor_fin, cet_anual, prazo_anos, tipo_amtz, freq_amtz, valor_amtz):
prazo_meses = prazo_anos * 12
taxa_mensal = (1 + cet_anual)**(1/12) - 1
def simular(sistema="SAC"):
dados = []
saldo = valor_fin
pago_acum = 0
m = 1
# Parâmetros iniciais
parcela_base_price = valor_fin * (taxa_mensal * (1 + taxa_mensal)**prazo_meses) / ((1 + taxa_mensal)**prazo_meses - 1)
amort_sac_base = valor_fin / prazo_meses
meses_restantes = prazo_meses
while saldo > 0.01 and m <= 600: # Limite de 50 anos por segurança
juros = saldo * taxa_mensal
# Cálculo da parcela base do sistema
if sistema == "SAC":
amort_sistema = amort_sac_base if tipo_amtz == "Prazo" else saldo / meses_restantes # Amortização fixa do contrato original
parcela = amort_sistema + juros
else: # PRICE
# No recálculo de prazo, a parcela PRICE original se mantém
parcela = parcela_base_price if tipo_amtz == "Prazo" else saldo * (taxa_mensal * (1 + taxa_mensal)**meses_restantes) / ((1 + taxa_mensal)**meses_restantes - 1)
amort_sistema = parcela - juros
# Aplicação da Amortização Extraordinária
extra = 0
if freq_amtz == "Mensal":
extra = valor_amtz
elif freq_amtz == "Anual" and m % 12 == 0:
extra = valor_amtz
# Ajuste se o extra for maior que o saldo
extra = min(extra, max(0, saldo - amort_sistema))
amort_total = amort_sistema + extra
saldo -= amort_total
pago_acum += (parcela + extra)
dados.append({
"Mes": m, "Ano": m/12, "Parcela": parcela,
"Saldo": max(0, saldo), "Total_Pago": pago_acum
})
if tipo_amtz == "Valor":
meses_restantes -= 1
m += 1
return pd.DataFrame(dados)
# Calculamos Original (sem extra) e Amortizada
return {
"sac_orig": simular("SAC") if valor_amtz == 0 else calcular_simulacao(valor_fin, cet_anual, prazo_anos, "Prazo", "Mensal", 0)["sac_orig"],
"sac_amtz": simular("SAC"),
"price_orig": simular("PRICE") if valor_amtz == 0 else calcular_simulacao(valor_fin, cet_anual, prazo_anos, "Prazo", "Mensal", 0)["price_orig"],
"price_amtz": simular("PRICE")
}
# --- UI ---
app_ui = ui.page_fluid(
ui.head_content(
# Meta tag para garantir que o mobile entenda o redimensionamento
ui.tags.meta(name="viewport", content="width=device-width, initial-scale=0.8, maximum-scale=1.0, user-scalable=yes"),
ui.tags.style("""
body { background-color: #f8f9fa; }
.insight-card {
background: white;
border-left: 5px solid #27ae60;
padding: 12px;
border-radius: 8px;
margin-bottom: 15px;
border: 1px solid #eee;
font-size: 0.9rem;
}
/* Ajuste para telas pequenas */
@media (max-width: 768px) {
h2 { font-size: 1.4rem !important; }
.insight-card { font-size: 0.8rem; padding: 8px; }
.control-panel .shiny-input-container { margin-bottom: 5px !important; }
}
""")
),
ui.markdown("## Estrategista Imobiliário: Simulador de Amortização"),
ui.layout_column_wrap(
ui.input_numeric("valor", "Valor Financiado (R$)", 500000),
ui.input_slider("taxa", "Taxa CET Anual (%)", 5.0, 20.0, 13.5, step=0.1),
ui.input_slider("prazo", "Prazo Original (Anos)", 5, 35, 35),
width=1/3
),
ui.layout_column_wrap(
ui.input_select("freq", "Frequência", {"Mensal": "Mensal", "Anual": "Anual (13º/Bônus)"}),
ui.input_select("tipo", "Objetivo", {"Prazo": "Reduzir Prazo (Tempo)", "Valor": "Reduzir Parcela (Fôlego)"}),
ui.input_numeric("extra", "Valor do Aporte Extra (R$)", 1000, step=100),
width=1/3
),
ui.div(ui.output_ui("texto_insights"), class_="insight-card"),
ui.navset_tab(
ui.nav_panel("Parcelas", output_widget("grafico_parcelas")),
ui.nav_panel("Dívida", output_widget("grafico_saldo")),
ui.nav_panel("Despesa", output_widget("grafico_acumulado"))
)
)
def server(input, output, session):
@reactive.calc
def dados():
return calcular_simulacao(
input.valor(), input.taxa()/100, input.prazo(),
input.tipo(), input.freq(), input.extra()
)
@output
@render.ui
def texto_insights():
res = dados()
df_sa = res["sac_amtz"]
df_pa = res["price_amtz"]
df_so = res["sac_orig"]
prazo_sa = df_sa['Mes'].iloc[-1]
prazo_pa = df_pa['Mes'].iloc[-1]
economia_s = res["sac_orig"]["Total_Pago"].iloc[-1] - df_sa["Total_Pago"].iloc[-1]
economia_p = res["price_orig"]["Total_Pago"].iloc[-1] - df_pa["Total_Pago"].iloc[-1]
return ui.markdown(f"""
* **Impacto no Prazo:** Você quita o imóvel em: **SAC - {prazo_sa/12:.1f} anos** e **PRICE - {prazo_pa/12:.1f} anos**
* **Economia de Juros (SAC):** R$ {economia_s:,.2f} evitados.
* **Economia de Juros (PRICE):** R$ {economia_p:,.2f} evitados.
""")
def criar_figura(titulo, y_label):
fig = go.Figure()
fig.update_layout(
template="plotly_white",
font=dict(family="Arial, sans-serif", size=11), # Reduzi um pouco o padrão para mobile
margin=dict(l=50, r=20, t=40, b=50), # Margens mais enxutas
hovermode="x unified",
legend=dict(orientation="h", yanchor="bottom", y=-0.3, xanchor="center", x=0.5), # Legenda embaixo ajuda no mobile
xaxis=dict(title="Anos", showline=True, linewidth=1, dtick=5),
yaxis=dict(showline=True, linewidth=1, tickformat=",.0f"),
title=titulo
)
return fig
@render_widget
def grafico_parcelas():
res = dados()
fig = criar_figura("Evolução da Parcela (Mensalidade + Extra)", "R$")
fig.add_trace(go.Scatter(x=res["sac_orig"]["Ano"], y=res["sac_orig"]["Parcela"], name="SAC Original", line=dict(color='#0984e3', dash='dash', width = 1)))
fig.add_trace(go.Scatter(x=res["sac_amtz"]["Ano"], y=res["sac_amtz"]["Parcela"], name="SAC Amortizada", line=dict(color='#0984e3')))
fig.add_trace(go.Scatter(x=res["price_orig"]["Ano"], y=res["price_orig"]["Parcela"], name="PRICE Original", line=dict(color='#d63031', dash='dash', width = 1)))
fig.add_trace(go.Scatter(x=res["price_amtz"]["Ano"], y=res["price_amtz"]["Parcela"], name="PRICE Amortizada", line=dict(color='#d63031')))
return fig
@render_widget
def grafico_saldo():
res = dados()
fig = criar_figura("Redução do Saldo Devedor", "R$")
fig.add_trace(go.Scatter(x=res["sac_orig"]["Ano"], y=res["sac_orig"]["Saldo"], name="SAC Original", line=dict(color='#0984e3', dash='dash', width = 1)))
fig.add_trace(go.Scatter(x=res["sac_amtz"]["Ano"], y=res["sac_amtz"]["Saldo"], name="SAC Amortizada", line=dict(color='#0984e3')))
fig.add_trace(go.Scatter(x=res["price_orig"]["Ano"], y=res["price_orig"]["Saldo"], name="PRICE Original", line=dict(color='#d63031', dash='dash', width = 1)))
fig.add_trace(go.Scatter(x=res["price_amtz"]["Ano"], y=res["price_amtz"]["Saldo"], name="PRICE Amortizada", line=dict(color='#d63031')))
return fig
@render_widget
def grafico_acumulado():
res = dados()
fig = criar_figura("Despesa Total Acumulada (Juros + Amortização)", "R$")
fig.add_trace(go.Scatter(x=res["sac_orig"]["Ano"], y=res["sac_orig"]["Total_Pago"], name="SAC Original", line=dict(color='#0984e3', dash='dash', width = 1)))
fig.add_trace(go.Scatter(x=res["sac_amtz"]["Ano"], y=res["sac_amtz"]["Total_Pago"], name="SAC Amortizada", line=dict(color='#0984e3')))
fig.add_trace(go.Scatter(x=res["price_orig"]["Ano"], y=res["price_orig"]["Total_Pago"], name="PRICE Original", line=dict(color='#d63031', dash='dash', width = 1)))
fig.add_trace(go.Scatter(x=res["price_amtz"]["Ano"], y=res["price_amtz"]["Total_Pago"], name="PRICE Amortizada", line=dict(color='#d63031')))
return fig
app = App(app_ui, server)
## file: requirements.txt
pandas
plotly
shinywidgets
shiny
exceptiongroup; python_version < "3.11"
Para entender os conceitos técnicos por trás desta simulação, acesse nosso guia completo: SAC vs PRICE: Qual o melhor sistema de financiamento?