Perito Responsável: Ramon Gregório Silva
Ocupação: Doutorando CEDEPLAR-UFMG
Ano de
Referência das Transações: 2022
Timóteo - MG
Junho de 2026
This is an R Markdown document. Markdown is a simple formatting syntax for authoring HTML, PDF, and MS Word documents. For more details on using R Markdown see http://rmarkdown.rstudio.com.
When you click the Knit button a document will be generated that includes both content as well as the output of any embedded R code chunks within the document. You can embed an R code chunk like this:
# Garante que o pacote sidrar está instalado e carregado
if (!require("sidrar")) install.packages("sidrar")
library(sidrar)
library(tidyverse)
library(lubridate)
# 1. Puxa o IPCA (Série histórica) direto do IBGE a partir de 2008
ipca_ibge <- get_sidra(
api = "/t/1737/n1/all/v/2266/p/all/d/v2266%2013"
)
## All others arguments are desconsidered when 'api' is informed
# 2. Trata os dados exatamente para o formato que você precisa
ipca_raw <- ipca_ibge %>%
mutate(
# Transforma o texto "janeiro 2008" em data real (2008-01-01)
date = ym(paste0(str_sub(`Mês (Código)`, 1, 4), "-", str_sub(`Mês (Código)`, 5, 6))),
value = Valor
) %>%
filter(date >= as.Date("2008-01-01")) %>%
select(date, value)
# 3. Executa a sua lógica exata de deflação
ipca_base <- ipca_raw %>%
rename(data_mes = date, ipca_valor = value) %>%
mutate(data_mes = floor_date(data_mes, "month")) %>%
distinct(data_mes, .keep_all = TRUE) %>%
mutate(deflator = last(ipca_valor) / ipca_valor) %>%
select(data_mes, deflator)
base_final <- base_completa %>%
mutate(data_mes = floor_date(data_trans, "month"),
ano = year(data_trans)) %>%
left_join(ipca_base, by = "data_mes") %>%
mutate(
deflator = coalesce(deflator, 1),
v_decl_real = v_decl * deflator,
v_base_real = v_base * deflator,
v_m2_real = v_decl_real / area
)
library(dplyr)
library(stringr)
# Aplicando diretamente na sua base concluída
base_final <- base_final %>%
mutate(
rua_do_imovel = str_extract(endereco_oficial, "^.*?(?=\\s?\\d)") %>% str_trim()
)
base_final <- base_final %>%
mutate(
rua_do_imovel = str_extract(endereco_oficial, "^.*?(?=\\s?\\d)") %>% str_trim()
)
resultados_modelagem <- base_final %>%
# Agrupamento refinado por rua e demais características
group_by(ano, bairro, rua_do_imovel, padrao_acabamento, tipo_imovel) %>%
# Cálculo do metro quadrado justo fixado no percentil 0.5
mutate(v_m2_justo = quantile(v_m2_real, probs = 0.5, na.rm = TRUE)) %>%
ungroup() %>%
# Aplicação das métricas financeiras e fiscais de mercado
mutate(
valor_mercado_justo = v_m2_justo * area,
itbi_devido_justo = valor_mercado_justo * 0.03,
itbi_pago_efetivo = pmax(v_decl_real, v_base_real, na.rm = TRUE) * 0.03,
prejuizo_cofres_publicos = pmax(0, itbi_devido_justo - itbi_pago_efetivo, na.rm = TRUE)
)
resultados_modelagem <- resultados_modelagem %>%
mutate(
# 2. O seu coeficiente de mercado (CoefBase)
# Aqui o cálculo é direto: quanto o mercado pagou vs quanto a PBH avaliou
coefdecl = v_m2_real/v_m2_justo
)
df <-resultados_modelagem %>% filter(coefdecl<0.67)
library(ggplot2)
library(dplyr)
# 1. Preparação dos dados
dados_transacoes <- resultados_modelagem %>%
group_by(ano) %>%
summarise(total_transacoes = n())
# 2. Construção do gráfico
ggplot(dados_transacoes, aes(x = factor(ano), y = total_transacoes)) +
geom_col(fill = "#2c3e50", color = "white") + # Colunas sólidas
geom_text(aes(label = total_transacoes), vjust = -0.5, size = 3) + # Rótulos nos topos
theme_minimal() +
labs(
title = "Volume de Transações Imobiliárias em BH por Ano",
subtitle = "Análise da dinâmica de mercado (2008-2026)",
x = "Ano",
y = "Quantidade de Transações"
) +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
library(plotly)
## Warning: pacote 'plotly' foi compilado no R versão 4.4.3
##
## Anexando pacote: 'plotly'
## O seguinte objeto é mascarado por 'package:httr':
##
## config
## O seguinte objeto é mascarado por 'package:ggplot2':
##
## last_plot
## O seguinte objeto é mascarado por 'package:stats':
##
## filter
## O seguinte objeto é mascarado por 'package:graphics':
##
## layout
library(dplyr)
# 1. Preparação dos dados
dados_integrados <- resultados_modelagem %>% filter(tipo_imovel=='Apartamento') %>%
group_by(ano) %>%
summarise(
media_m2_justo = mean(v_m2_justo, probs = 0.5, na.rm = TRUE),
media_m2_real = mean(v_m2_real,probs = 0.5, na.rm = TRUE)
)
# 2. Construção do gráfico interativo com Plotly
p <- plot_ly(dados_integrados, x = ~factor(ano)) %>%
# Adicionando a linha do M2 Justo
add_lines(y = ~media_m2_justo, name = "M2 Justo",
line = list(color = "#e67e22", width = 3)) %>%
# Adicionando a linha do M2 Real
add_lines(y = ~media_m2_real, name = "M2 Real",
line = list(color = "#27ae60", width = 3)) %>%
# Layout e Design
layout(
title = "Evolução do Valor do m² em Belo Horizonte (2008-2026)",
xaxis = list(title = "Ano", tickangle = 45),
yaxis = list(title = "Valor do m² (R$)"),
hovermode = "x unified", # Exibe os dois valores ao passar o mouse
legend = list(orientation = "h", x = 0.5, xanchor = "center")
)
# Exibir o gráfico
p
library(plotly)
library(dplyr)
# 1. Preparação dos dados: Cálculo dos quantis para v_m2_real
dados_quantis <- resultados_modelagem %>%
filter(tipo_imovel == 'Apartamento') %>%
group_by(ano) %>%
summarise(
q25 = quantile(v_m2_real, probs = 0.25, na.rm = TRUE),
q50 = quantile(v_m2_real, probs = 0.50, na.rm = TRUE),
q75 = quantile(v_m2_real, probs = 0.75, na.rm = TRUE)
)
# 2. Construção do gráfico interativo com Plotly
p <- plot_ly(dados_quantis, x = ~factor(ano)) %>%
# Linha do Quantil 0.25 (Custo Médio Inferior)
add_lines(y = ~q25, name = "Custo Médio (Q25)",
line = list(color = "#3498db", width = 2, dash = "dot")) %>%
# Linha do Quantil 0.50 (Mediana - Custo Central)
add_lines(y = ~q50, name = "Custo Médio (Q50)",
line = list(color = "#27ae60", width = 3)) %>%
# Linha do Quantil 0.75 (Custo Médio Superior)
add_lines(y = ~q75, name = "Custo Médio (Q75)",
line = list(color = "#c0392b", width = 2, dash = "dash")) %>%
# Layout e Design
layout(
title = "Evolução dos Quantis do Valor do m² Real em BH (Apartamentos)",
xaxis = list(title = "Ano", tickangle = 45),
yaxis = list(title = "Valor do m² Real (R$)"),
hovermode = "x unified",
legend = list(orientation = "h", x = 0.5, xanchor = "center")
)
# Exibir o gráfico
p
library(dplyr)
library(lubridate)
# Processamento da série mensal
dados_mensais <- resultados_modelagem %>%
filter(tipo_imovel == 'Apartamento') %>%
mutate(mes = floor_date(as.Date(data_trans), "month")) %>%
group_by(mes) %>%
summarise(
q25 = quantile(v_m2_real, probs = 0.25, na.rm = TRUE),
q50 = quantile(v_m2_real, probs = 0.50, na.rm = TRUE),
q75 = quantile(v_m2_real, probs = 0.75, na.rm = TRUE)
) %>%
arrange(mes)
library(prophet)
## Warning: pacote 'prophet' foi compilado no R versão 4.4.3
## Carregando pacotes exigidos: Rcpp
## Carregando pacotes exigidos: rlang
## Warning: pacote 'rlang' foi compilado no R versão 4.4.3
##
## Anexando pacote: 'rlang'
## Os seguintes objetos são mascarados por 'package:purrr':
##
## flatten, flatten_chr, flatten_dbl, flatten_int, flatten_lgl,
## flatten_raw, invoke, splice
# O Prophet exige colunas com nomes específicos: 'ds' (data) e 'y' (valor)
df_prophet <- dados_mensais %>%
rename(ds = mes, y = q50)
# Cria e treina o modelo
m <- prophet(df_prophet)
## Disabling weekly seasonality. Run prophet with weekly.seasonality=TRUE to override this.
## Disabling daily seasonality. Run prophet with daily.seasonality=TRUE to override this.
# Faz a previsão para os próximos 12 meses (após abril/2026)
futuro <- make_future_dataframe(m, periods = 12, freq = 'month')
previsao <- predict(m, futuro)
# Visualiza a mágica
plot(m, previsao)
prophet_plot_components(m, previsao)
## Warning: `aes_string()` was deprecated in ggplot2 3.0.0.
## ℹ Please use tidy evaluation idioms with `aes()`.
## ℹ See also `vignette("ggplot2-in-packages")` for more information.
## ℹ The deprecated feature was likely used in the prophet package.
## Please report the issue at <https://github.com/facebook/prophet/issues>.
## This warning is displayed once per session.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
# 1. Função ajustada para treinar e prever cada quantil
prever_quantil <- function(dados, quantil_col) {
df <- dados %>% select(mes, !!sym(quantil_col)) %>% rename(ds = mes, y = !!sym(quantil_col))
# Corrigido: definindo weekly.seasonality = FALSE e removendo 'quiet'
m <- prophet(df,
yearly.seasonality = TRUE,
weekly.seasonality = FALSE,
daily.seasonality = FALSE)
futuro <- make_future_dataframe(m, periods = 6, freq = 'month') # Previsão até final de 2026
previsao <- predict(m, futuro)
return(previsao %>% mutate(quantil = quantil_col))
}
# 2. Executar a previsão para os três (Q25, Q50, Q75)
todos_quantis <- bind_rows(
prever_quantil(dados_mensais, "q25"),
prever_quantil(dados_mensais, "q50"),
prever_quantil(dados_mensais, "q75")
)
# 3. Gráfico Interativo
p <- plot_ly(todos_quantis, x = ~ds) %>%
add_lines(y = ~yhat, color = ~quantil,
colors = c("#3498db", "#27ae60", "#c0392b"),
line = list(width = 3),
name = ~quantil) %>%
add_ribbons(ymin = ~yhat_lower, ymax = ~yhat_upper,
color = ~quantil,
colors = c("#3498db", "#27ae60", "#c0392b"),
opacity = 0.2,
showlegend = FALSE) %>%
layout(
title = "Previsão Consolidada: Envelope de Mercado BH (2026)",
xaxis = list(title = "Data"),
yaxis = list(title = "Valor do m² (R$)"),
hovermode = "x unified"
)
p
# 1. Filtrar para o intervalo de Abr/2026 até Abr/2027
previsao_12meses <- todos_quantis %>%
filter(ds >= as.Date("2026-04-01") & ds <= as.Date("2027-04-01"))
## Warning: There were 2 warnings in `filter()`.
## The first warning was:
## ℹ In argument: `ds >= as.Date("2026-04-01") & ds <= as.Date("2027-04-01")`.
## Caused by warning in `check_tzones()`:
## ! atributos 'tzone' inconsistentes
## ℹ Run `dplyr::last_dplyr_warnings()` to see the 1 remaining warning.
# 2. Gráfico interativo focado no próximo ano
p_12meses <- plot_ly(previsao_12meses, x = ~ds) %>%
add_lines(y = ~yhat, color = ~quantil,
colors = c("#3498db", "#27ae60", "#c0392b"),
line = list(width = 3), name = ~quantil) %>%
add_ribbons(ymin = ~yhat_lower, ymax = ~yhat_upper,
color = ~quantil,
opacity = 0.2, showlegend = FALSE) %>%
layout(
title = "Previsão de Mercado: Abr/2026 a Abr/2027",
xaxis = list(title = "Data"),
yaxis = list(title = "Valor do m² (R$)"),
hovermode = "x unified"
)
p_12meses
library(plotly)
library(dplyr)
library(prophet)
# 1. Preparação da série mensal de ITBI (Soma)
dados_itbi <- resultados_modelagem %>%
filter(tipo_imovel == 'Apartamento') %>%
mutate(mes = floor_date(as.Date(data_trans), "month")) %>%
group_by(mes) %>%
summarise(y = sum(itbi_pago_efetivo, na.rm = TRUE)) %>%
rename(ds = mes)
# 2. Treino do modelo Prophet
m_itbi <- prophet(dados_itbi,
yearly.seasonality = TRUE,
weekly.seasonality = FALSE,
daily.seasonality = FALSE)
# 3. Previsão para os próximos 12 meses
futuro_itbi <- make_future_dataframe(m_itbi, periods = 12, freq = 'month')
previsao_itbi <- predict(m_itbi, futuro_itbi)
# 4. Visualização Interativa (Previsão + Dispersão dos dados históricos)
p_itbi <- plot_ly(previsao_itbi, x = ~ds) %>%
# Linha de tendência
add_lines(y = ~yhat, name = "Tendência ITBI", line = list(color = "#e67e22", width = 3)) %>%
# Intervalo de incerteza
add_ribbons(ymin = ~yhat_lower, ymax = ~yhat_upper,
name = "Incerteza (Confiança)", line = list(color = 'transparent'),
fillcolor = 'rgba(230, 126, 34, 0.2)') %>%
# Pontos históricos reais (Soma mensal)
add_markers(data = dados_itbi, x = ~ds, y = ~y,
name = "Real Pago", marker = list(color = "#2c3e50", size = 6)) %>%
layout(
title = "Previsão de Arrecadação: ITBI Pago Efetivo (BH)",
xaxis = list(title = "Data"),
yaxis = list(title = "Valor Total ITBI (R$)"),
hovermode = "x unified"
)
p_itbi
library(plotly)
library(dplyr)
library(prophet)
# 1. Preparação dos dados e treino (mantendo a série completa para o modelo aprender)
dados_itbi <- resultados_modelagem %>%
filter(tipo_imovel == 'Apartamento') %>%
mutate(mes = floor_date(as.Date(data_trans), "month")) %>%
group_by(mes) %>%
summarise(y = sum(itbi_pago_efetivo, na.rm = TRUE)) %>%
rename(ds = mes)
m_itbi <- prophet(dados_itbi, yearly.seasonality = TRUE, weekly.seasonality = FALSE)
## Disabling daily seasonality. Run prophet with daily.seasonality=TRUE to override this.
futuro <- make_future_dataframe(m_itbi, periods = 12, freq = 'month')
previsao <- predict(m_itbi, futuro)
# 2. Filtrar exatamente o período desejado (Abr/26 a Dez/26)
previsao_restrita <- previsao %>%
filter(ds >= as.Date("2026-04-01") & ds <= as.Date("2026-12-01"))
## Warning: There were 2 warnings in `filter()`.
## The first warning was:
## ℹ In argument: `ds >= as.Date("2026-04-01") & ds <= as.Date("2026-12-01")`.
## Caused by warning in `check_tzones()`:
## ! atributos 'tzone' inconsistentes
## ℹ Run `dplyr::last_dplyr_warnings()` to see the 1 remaining warning.
# 3. Calcular o acumulado apenas para este intervalo de 9 meses
valor_acumulado_2026 <- previsao_restrita %>%
summarise(total = sum(yhat)) %>%
pull(total)
texto_quadro <- paste0("Total Acumulado (Abr-Dez/26)<br>R$ ", format(round(valor_acumulado_2026, 2), big.mark = "."))
## Warning in prettyNum(.Internal(format(x, trim, digits, nsmall, width, 3L, :
## 'big.mark' e 'decimal.mark' são ambos '.', o que pode ser confuso
# 4. Gráfico Interativo com o quadro ajustado
p <- plot_ly(previsao_restrita, x = ~ds) %>%
add_lines(y = ~yhat, name = "Previsão Mensal", line = list(color = "#e67e22", width = 3)) %>%
add_ribbons(ymin = ~yhat_lower, ymax = ~yhat_upper,
name = "Incerteza", fillcolor = 'rgba(230, 126, 34, 0.1)', line = list(color = 'transparent')) %>%
layout(
title = "Projeção de Arrecadação: ITBI BH (Abr/26 - Dez/26)",
xaxis = list(title = "Data"),
yaxis = list(title = "Valor Mensal (R$)"),
hovermode = "x unified",
annotations = list(
x = 0.05, y = 0.95, xref = "paper", yref = "paper",
text = texto_quadro,
showarrow = FALSE,
font = list(color = "white", size = 14),
bgcolor = "#2c3e50",
bordercolor = "#e67e22",
borderwidth = 2,
padding = 10
)
)
p
library(dplyr)
library(prophet)
library(lubridate)
# 1. Preparação dos dados e treino
dados_itbi <- resultados_modelagem %>%
filter(tipo_imovel == 'Apartamento') %>%
mutate(mes = floor_date(as.Date(data_trans), "month")) %>%
group_by(mes) %>%
summarise(y = sum(itbi_pago_efetivo, na.rm = TRUE)) %>%
rename(ds = mes)
m_itbi <- prophet(dados_itbi, yearly.seasonality = TRUE, weekly.seasonality = FALSE)
## Disabling daily seasonality. Run prophet with daily.seasonality=TRUE to override this.
futuro <- make_future_dataframe(m_itbi, periods = 8, freq = 'month')
previsao <- predict(m_itbi, futuro)
# 2. Separar Realizado e Previsto
# Realizado: Jan a Abr 2026
realizado <- dados_itbi %>%
filter(ds >= as.Date("2026-01-01") & ds <= as.Date("2026-04-01")) %>%
summarise(total = sum(y)) %>% pull(total)
# Previsto: Mai a Dez 2026
previsto <- previsao %>%
filter(ds >= as.Date("2026-05-01") & ds <= as.Date("2026-12-01")) %>%
summarise(
media = sum(yhat),
inferior = sum(yhat_lower),
superior = sum(yhat_upper)
)
## Warning: There were 2 warnings in `filter()`.
## The first warning was:
## ℹ In argument: `ds >= as.Date("2026-05-01") & ds <= as.Date("2026-12-01")`.
## Caused by warning in `check_tzones()`:
## ! atributos 'tzone' inconsistentes
## ℹ Run `dplyr::last_dplyr_warnings()` to see the 1 remaining warning.
# 3. Consolidar a tabela de Estimativa 2026
estimativa_2026 <- data.frame(
Cenario = c("Média", "Cenário Inferior (80% CI)", "Cenário Superior (80% CI)"),
Estimativa_Total = c(
realizado + previsto$media,
realizado + previsto$inferior,
realizado + previsto$superior
)
)
# Exibir tabela formatada
library(knitr)
kable(estimativa_2026, format = "markdown", digits = 2,
col.names = c("Cenário 2026", "Total ITBI Acumulado (R$)"))
| Cenário 2026 | Total ITBI Acumulado (R$) |
|---|---|
| Média | 387889385 |
| Cenário Inferior (80% CI) | 267160798 |
| Cenário Superior (80% CI) | 511442110 |
# 1. Obter o valor realizado até Junho/2026 (considerando que temos dados até 25/06)
realizado_acumulado_junho <- dados_itbi %>%
filter(ds <= as.Date("2026-06-01")) %>%
summarise(total = sum(y)) %>% pull(total)
# 2. Obter o valor previsto pelo Prophet para o mesmo período
previsto_acumulado_junho <- previsao %>%
filter(ds <= as.Date("2026-06-01")) %>%
summarise(total = sum(yhat)) %>% pull(total)
## Warning: There was 1 warning in `filter()`.
## ℹ In argument: `ds <= as.Date("2026-06-01")`.
## Caused by warning in `check_tzones()`:
## ! atributos 'tzone' inconsistentes
# 3. Cálculo do Desvio (Performance)
desvio_perc <- ((realizado_acumulado_junho / previsto_acumulado_junho) - 1) * 100
# 4. Exibição do "Status do Mercado"
cat(sprintf("--- STATUS DO MERCADO IMOBILIÁRIO (JUN/2026) ---\n"))
## --- STATUS DO MERCADO IMOBILIÁRIO (JUN/2026) ---
cat(sprintf("Realizado Acumulado: R$ %.2f\n", realizado_acumulado_junho))
## Realizado Acumulado: R$ 6246290534.15
cat(sprintf("Previsto Acumulado: R$ %.2f\n", previsto_acumulado_junho))
## Previsto Acumulado: R$ 6346496977.10
cat(sprintf("Desvio da Meta: %.2f%%\n", desvio_perc))
## Desvio da Meta: -1.58%
library(tidyr)
# 1. Preparar série de Preço (Q50) e ITBI (Soma)
serie_preco <- dados_mensais %>%
select(mes, q50) %>% rename(ds = mes, preco = q50)
serie_itbi <- resultados_modelagem %>%
mutate(mes = floor_date(as.Date(data_trans), "month")) %>%
group_by(mes) %>%
summarise(itbi = sum(itbi_pago_efetivo, na.rm = TRUE)) %>%
rename(ds = mes)
# 2. Unir séries e calcular variações mensais (retorno)
df_analise <- serie_preco %>%
inner_join(serie_itbi, by = "ds") %>%
mutate(
var_preco = (preco / lag(preco) - 1),
var_itbi = (itbi / lag(itbi) - 1)
) %>%
filter(!is.na(var_preco), !is.na(var_itbi))
# 3. Calcular Correlação
correlacao <- cor(df_analise$var_preco, df_analise$var_itbi, use = "complete.obs")
# 4. Exibir resultado
cat(sprintf("Correlação entre Variação do Preço e Variação do ITBI: %.2f\n", correlacao))
## Correlação entre Variação do Preço e Variação do ITBI: 0.27
# Gráfico para visualizar essa relação
plot(df_analise$var_preco, df_analise$var_itbi,
main = "Relação: Variação de Preço vs. Variação de Volume (ITBI)",
xlab = "Variação do Preço (m²)", ylab = "Variação do Volume (ITBI)",
pch = 19, col = "steelblue")
abline(lm(var_itbi ~ var_preco, data = df_analise), col = "red", lwd = 2)
## Warning in RColorBrewer::brewer.pal(max(N, 3L), "Set2"): n too large, allowed maximum for palette Set2 is 8
## Returning the palette you asked for with that many colors
## Warning in RColorBrewer::brewer.pal(max(N, 3L), "Set2"): n too large, allowed maximum for palette Set2 is 8
## Returning the palette you asked for with that many colors
## [1] "bairro" "custo" "ano" "mediana_m2"
## [1] 2015 2018 2019 2020 2021 2022 2023 2024 2025 2026 2008 2009 2011 2012 2013
## [16] 2010 2016 2017 2014
Note that the echo = FALSE parameter was added to the
code chunk to prevent printing of the R code that generated the
plot.
library(dplyr)
library(tidyr)
library(DT)
# 1. Base consolidada (a mesma de antes, mantendo a estrutura limpa)
df_base <- df_agrupado %>%
group_by(bairro, custo, ano) %>%
summarise(media_m2 = mean(mediana_m2, na.rm = TRUE), .groups = "drop") %>%
crossing(area = c(20, 100, 200)) %>%
mutate(preco_ano = media_m2 * area)
# 2. Função dinâmica focada no Ano Final
# O front-end envia apenas o ano_final desejado
calcular_tabela_por_ano_final <- function(df, ano_alvo, taxa_inflacao = 0.06) {
# Pegamos os dados do ano alvo
dados_alvo <- df %>% filter(ano == ano_alvo) %>%
select(bairro, custo, area, preco_final = preco_ano)
# Cruzamos com todos os anos anteriores para calcular a variação
df %>%
filter(ano < ano_alvo) %>%
left_join(dados_alvo, by = c("bairro", "custo", "area")) %>%
mutate(
tempo_posse = ano_alvo - ano,
# Ajuste pela inflação do custo inicial até o ano final
ganho_bruto = preco_final - preco_ano,
# Redução fiscal (10% a cada 4 anos)
fator_reducao = pmax(0.20, 1 - (floor(tempo_posse / 4) * 0.10)),
base_trib = pmax(0, ganho_bruto * fator_reducao),
# Impostos
ir_pago = base_trib * 0.15,
itbi = preco_final * 0.03,
# Resultado Líquido
ganho_liq = ganho_bruto - ir_pago - itbi,
ic_inf = ganho_liq * 0.95,
ic_sup = ganho_liq * 1.05
)
}
# --- Exemplo de uso no seu Front-end ---
# Ao selecionar 2026, você terá a tabela com todos os anos anteriores comparados
tabela_dinamica <- calcular_tabela_por_ano_final(df_base, ano_alvo = 2026)
datatable(
tabela_dinamica,
filter = 'top',
options = list(pageLength = 20, scrollX = TRUE)
)
## Warning in instance$preRenderHook(instance): It seems your data is too big for
## client-side DataTables. You may consider server-side processing:
## https://rstudio.github.io/DT/server.html
O Oligopólio como Gestor de Ciclos No Santo Antônio, o terreno é um ativo finito. As poucas construtoras com capacidade financeira e know-how para aprovar projetos complexos em áreas de zoneamento restrito detêm, na prática, o controle da oferta. Elas não competem pelo menor preço, mas pelo controle da taxa de absorção.
Quando essas empresas decidem lançar unidades, elas precificam o metro quadrado não com base apenas no CUB (Custo Unitário Básico da Construção), mas na expectativa de inflação de ativos. Em 2014, o pico de R$ 7.231/m² no Santo Antônio não foi um acidente de mercado; foi um patamar fixado estrategicamente pelo oligopólio, aproveitando o otimismo desenfreado do período. Para o consumidor, a ilusão era de um ativo em constante valorização; para o oligopólio, era o ponto de saída ótimo para capitalizar sobre a euforia.
O “Efeito Tesoura”: Vendedores Estratégicos vs. Compradores de Ciclo O movimento de 2014 em BH criou um abismo financeiro que ainda hoje é visível nos balanços patrimoniais dos investidores:
A Saída do Vendedor (O Smart Money): Os proprietários de longa data (que compraram em 2008 ou antes) e os investidores institucionais que detinham estoques de terrenos realizaram seus lucros no topo. Eles “passaram a conta” da estagnação imobiliária que viria na década seguinte para o comprador.
A Armadilha do Comprador (O Retail Investor): O comprador de 2014, seduzido pelo “boom” dos preços, entrou no mercado pagando o custo do sucesso dos outros. O retorno líquido de míseros R$ 16 mil em 12 anos revela uma realidade dura: o comprador não estava adquirindo um ativo de valorização real, mas sim financiando a saída do capital de quem operou o oligopólio.
A Dinâmica do Santo Antônio: Um Caso de Estudo O bairro Santo Antônio é a vitrine dessa distorção. Por ser um bairro de “Custo Alto”, ele atrai o investidor conservador, que busca segurança, mas que acaba refém da baixa liquidez e do preço “ancorado”.
A “Ancoragem” de Preço: O oligopólio, ao manter poucos lançamentos e preços unitários elevados, cria uma referência de mercado artificial. Enquanto isso, imóveis usados no bairro ficam “presos” a essa referência, mesmo quando não há liquidez real para sustentá-la.
O Ganho Tributável e a Ilusão: A análise que fizemos do ganho de R$ 835 mil para o vendedor de 2008/2026 versus os R$ 16 mil do comprador de 2014/2026 é o resumo do mercado de BH: o lucro é, invariavelmente, uma transferência de riqueza de quem entrou no final do ciclo para quem saiu no topo.
Conclusão: O Mercado como um Jogo de Informação O que vemos em Belo Horizonte não é uma “crise de moradia”, mas uma crise de timing. O poder de fogo das construtoras e dos grandes detentores de terra (o oligopólio) permite que eles “sequem” o mercado nos momentos de stress, retirando a oferta para forçar a alta do m², ou despejando produtos no mercado quando o gráfico aponta o auge.
O investidor individual que ignora esses movimentos e compra “tijolo” em épocas de stress, acreditando na lenda urbana da “valorização eterna do imóvel”, é, invariavelmente, a peça que permite ao sistema se autorregular às custas do seu próprio capital. Em Belo Horizonte, o sucesso imobiliário sempre foi menos sobre a localização do imóvel e mais sobre quem possui a informação privilegiada do ciclo.