<strong>Perito Responsável:</strong> Ramon Gregório Silva<br>
<strong>Credencial:</strong> Doutorando CEDEPLAR-UFMG<br>
<strong>Período Analisado:</strong> 2008 – 2026<br>
<strong>Data de Emissão:</strong> Junho de 2026
Belo Horizonte - MG
LAUDO DE AUDITORIA PERICIAL: ANÁLISE DE IMPACTO FISCAL E VOLUMETRIA VERTICAL Análise de Risco Sistêmico e Subfaturamento Patrimonial no Bloco A (Edifício JK) Perito Responsável: Ramon
Data de Emissão: 15 de junho de 2026
A dinâmica da busca em grandes bases Quando você utiliza a caixa de busca para filtrar endereços ou bairros dentro de uma base dessa magnitude, o sistema realiza um str_detect (busca de texto) em cada uma das linhas. Como a estrutura foi otimizada para ser renderizada via JavaScript (pelo motor do DataTables e Crosstalk), a busca não ocorre de forma remota no servidor, mas sim localmente na memória RAM do seu computador.
Tempo de Resposta: Por tratar-se de quase meio milhão de registros, a latência entre a digitação do comando de busca e a atualização da tabela é esperada. O sistema precisa varrer todos os campos textuais (endereco_oficial e bairro) para encontrar as correspondências.
Otimização: A escolha do dom = ‘Bftip’ foi estratégica, pois utiliza o motor do DataTables, que é extremamente otimizado para lidar com este volume de dados em comparação com métodos tradicionais de processamento de R, mantendo a estabilidade do relatório sem o risco de “crash” por estouro de memória (o erro de VirtualAlloc que enfrentamos anteriormente).
Recomendações para fluidez Para manter a agilidade durante a auditoria, recomendo que:
Utilize o Filtro de Bairro primeiro: Ao selecionar um bairro específico no filtro global (filter_select), o sistema reduz drasticamente o subconjunto de dados que a tabela precisa processar. Com uma base filtrada de 448 mil para alguns milhares, a busca por endereço torna-se instantânea.
Seja específico na busca: Sempre que possível, utilize o termo mais específico do endereço (ex: o número do apartamento ou logradouro) na caixa de busca da tabela, o que ajuda o algoritmo a filtrar os resultados com maior precisão e menor consumo de processamento.
Esta estrutura de “Filtro Hierárquico” (Bairro > Tabela Detalhada) é a forma técnica mais eficiente de navegar em uma base deste porte sem comprometer a integridade dos dados nem a performance da sua máquina. O processamento está, portanto, balanceado entre a robustez da base de 448 mil dados e a capacidade de resposta da sua interface de auditoria.
if (!require("pacman")) install.packages("pacman")
## Carregando pacotes exigidos: pacman
## Warning: pacote 'pacman' foi compilado no R versão 4.4.3
pacman::p_load(tidyverse, lubridate, httr, digest)
if (!require("sidrar")) install.packages("sidrar")
## Carregando pacotes exigidos: sidrar
## Warning: pacote 'sidrar' foi compilado no R versão 4.4.3
library(sidrar)
library(tidyverse)
library(lubridate)
# --- 1. FUNÇÕES DE TRATAMENTO ---
tratar_precos_vetor <- function(x) {
clean_x <- str_remove_all(x, "\\.")
num <- as.numeric(str_replace(str_remove_all(clean_x, "[^0-9,]"), ",", "."))
num <- coalesce(num, 0)
num_ajustado <- case_when(
num > 0 & num < 2000 ~ num * 1000,
num >= 2000 & num < 10000 ~ num * 100,
TRUE ~ num
)
return(ifelse(num_ajustado < 5000, 0, num_ajustado))
}
tratar_area <- function(x) {
num <- as.numeric(str_replace(x, ",", "."))
ifelse(!is.na(num) & num > 500, num / 100, num)
}
# --- 2. FUNÇÃO DE EXTRAÇÃO (CORRIGIDA) ---
ler_itbi_completo <- function(url, rotulo) {
message(paste("Baixando período:", rotulo, "..."))
temp <- tempfile()
# Usando RETRY com tratamento de erros e downgrade de protocolo HTTP
res <- tryCatch({
RETRY(
verb = "GET",
url = url,
write_disk(temp, overwrite = TRUE),
config(
ssl_verifypeer = FALSE,
# http_version = 2 força o uso do HTTP/1.1 estável,
# resolvendo o erro de stream/framing do HTTP/2 da PBH
http_version = 2
),
# Força TLSv1.2/1.3 estável para evitar bugs no Schannel do Windows
ssl_version = 6,
add_headers(`User-Agent` = "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"),
times = 3, # Tenta até 3 vezes em caso de queda parcial
pause_base = 3 # Aguarda 3 segundos entre tentativas
)
}, error = function(e) {
message(paste("Erro crítico no download de:", rotulo, "-", e$message))
return(NULL)
})
if (is.null(res) || status_code(res) != 200) {
message(paste("Falha ao acessar ou salvar o arquivo do período:", rotulo))
if (file.exists(temp)) file.remove(temp)
return(NULL)
}
# Criando a assinatura para o registro de auditoria
assinatura <- substr(digest(temp, algo="sha256", file=TRUE), 1, 10)
# Leitura e processamento seguro do arquivo CSV baixado
df_processado <- tryCatch({
df <- read_delim(temp, delim = ";", locale = locale(encoding = "ISO-8859-1"),
show_col_types = FALSE, col_types = cols(.default = "c"))
# Normaliza nomes de colunas para evitar caracteres especiais
df <- df %>%
rename_with(~tolower(iconv(., to = "ASCII//TRANSLIT"))) %>%
rename_with(~str_replace_all(., "[^a-z]", "_"))
# Processamento e Higienização das Variáveis
df %>% mutate(
endereco_oficial = toupper(pick(contains("endereco"))[[1]]),
bairro = str_trim(toupper(pick(contains("bairro"))[[1]])),
data_trans = dmy(pick(contains("data_quitacao"), contains("dt_quit"))[[1]]),
area = tratar_area(pick(contains("area_const"))[[1]]),
v_decl = tratar_precos_vetor(pick(contains("valor_decl"), contains("vlr_decl"))[[1]]),
v_base = tratar_precos_vetor(pick(contains("valor_base"), contains("vlr_base"))[[1]]),
periodo_csv = rotulo,
id_auditoria = assinatura,
padrao_acabamento = if(any(str_detect(names(df), "padrao_acabamento"))) {
str_trim(toupper(pick(contains("padrao_acabamento"))[[1]]))
} else {
"NÃO INFORMADO"
},
tipo_imovel = case_when(
str_detect(endereco_oficial, "APT|APTO|APARTAMENTO") ~ "Apartamento",
str_detect(endereco_oficial, "SALA|CONJUNTO") ~ "Sala",
str_detect(endereco_oficial, "GARAGE") ~ "Garage",
str_detect(endereco_oficial, "LOJA") ~ "Loja",
TRUE ~ "Residencial"
) # <- Fechamento do case_when corrigido aqui
) %>%
filter(v_decl > 0, area > 0, !is.na(bairro)) %>%
select(endereco_oficial, padrao_acabamento, tipo_imovel, bairro, data_trans, area, v_decl, v_base, periodo_csv, id_auditoria)
}, error = function(e) {
message(paste("Erro ao processar os dados do período:", rotulo, "-", e$message))
return(NULL)
})
# Limpeza explícita do arquivo temporário
if (file.exists(temp)) file.remove(temp)
return(df_processado)
}
# --- 3. LISTA DE URLS ---
urls <- list(
c("https://ckan.pbh.gov.br/dataset/0e13bf71-5355-47ce-8607-966413b08c0a/resource/7f8955aa-0b30-4157-bbc2-7dd444941728/download/pda_itbi_relatorio_200801_a_202405.csv", "2024"),
c("https://ckan.pbh.gov.br/dataset/0e13bf71-5355-47ce-8607-966413b08c0a/resource/f4e60a70-3bd8-41dc-b031-42b5f3d1671a/download/pda_itbi_relatorio_202406_a_202412.csv", "2024"),
c("https://ckan.pbh.gov.br/dataset/0e13bf71-5355-47ce-8607-966413b08c0a/resource/53549725-b466-4191-8930-b352486987f8/download/pda_itbi_relatorio_202501.csv", "2025"),
c("https://ckan.pbh.gov.br/dataset/0e13bf71-5355-47ce-8607-966413b08c0a/resource/021f1589-3c7d-419b-b0b2-4d1cc4a85618/download/pda_itbi_relatorio_202502.csv", "2025"),
c("https://ckan.pbh.gov.br/dataset/0e13bf71-5355-47ce-8607-966413b08c0a/resource/463a8a81-d242-4f76-857c-2b5090432326/download/pda_itbi_relatorio_202503.csv", "2025"),
c("https://ckan.pbh.gov.br/dataset/0e13bf71-5355-47ce-8607-966413b08c0a/resource/8d055465-b1a9-4b1d-855a-e55d609a567e/download/pda_itbi_relatorio_202504.csv", "2025"),
c("https://ckan.pbh.gov.br/dataset/0e13bf71-5355-47ce-8607-966413b08c0a/resource/4d2a1c0d-77de-4c32-a7db-3e327e09257c/download/pda_itbi_relatorio_202505.csv", "2025"),
c("https://ckan.pbh.gov.br/dataset/0e13bf71-5355-47ce-8607-966413b08c0a/resource/cbdea14d-77de-4c32-a7db-3e327e09257c/download/pda_itbi_relatorio_202506.csv", "2025"),
c("https://ckan.pbh.gov.br/dataset/0e13bf71-5355-47ce-8607-966413b08c0a/resource/53549725-b466-4191-8930-b352486987f8/download/pda_itbi_relatorio_202507.csv", "2025"),
c("https://ckan.pbh.gov.br/dataset/0e13bf71-5355-47ce-8607-966413b08c0a/resource/2deb4632-f226-40a7-b8ac-89c6d9f8aff9/download/pda_itbi_relatorio_202508.csv", "2025"),
c("https://ckan.pbh.gov.br/dataset/0e13bf71-5355-47ce-8607-966413b08c0a/resource/cbdea14d-77de-4c32-a7db-3e327e09257c/download/pda_itbi_relatorio_202509.csv", "2025"),
c("https://ckan.pbh.gov.br/dataset/0e13bf71-5355-47ce-8607-966413b08c0a/resource/f4e60a70-3bd8-41dc-b031-42b5f3d1671a/download/pda_itbi_relatorio_202510.csv", "2025"),
c("https://ckan.pbh.gov.br/dataset/0e13bf71-5355-47ce-8607-966413b08c0a/resource/9bd075e2-2abe-42d7-a1e2-b68a18245172/download/pda_itbi_relatorio_202511.csv", "2025"),
c("https://ckan.pbh.gov.br/dataset/0e13bf71-5355-47ce-8607-966413b08c0a/resource/c3dd25fd-ac34-4f3f-afcc-21589d64ce8a/download/pda_itbi_relatorio_202512.csv", "2025"),
c("https://ckan.pbh.gov.br/dataset/0e13bf71-5355-47ce-8607-966413b08c0a/resource/2deb4632-f226-40a7-b8ac-89c6d9f8aff9/download/pda_itbi_relatorio_202601.csv", "2026"),
c("https://ckan.pbh.gov.br/dataset/0e13bf71-5355-47ce-8607-966413b08c0a/resource/453b3f6d-ec7f-415c-af5e-c23d1f488e2a/download/pda_itbi_relatorio_202602.csv", "2026"),
c("https://ckan.pbh.gov.br/dataset/0e13bf71-5355-47ce-8607-966413b08c0a/resource/0773a6b9-b107-4692-9b85-760221ec3abb/download/pda_itbi_relatorio_202603.csv", "2026"),
c("https://ckan.pbh.gov.br/dataset/0e13bf71-5355-47ce-8607-966413b08c0a/resource/5e1f8a2b-3c4d-4e5f-a6b7-c8d9e0f1a2b3/download/pda_itbi_relatorio_202604.csv", "2026"),
c("https://ckan.pbh.gov.br/dataset/0e13bf71-5355-47ce-8607-966413b08c0a/resource/a9b8c7d6-e5f4-4321-b0a9-87654321fedc/download/pda_itbi_relatorio_202605.csv", "2026")
)
# --- 4. EXECUÇÃO ---
base_completa <- map_df(urls, ~ler_itbi_completo(.x[1], .x[2]))
## Baixando período: 2024 ...
## Warning: There was 1 warning in `mutate()`.
## ℹ In argument: `area = tratar_area(pick(contains("area_const"))[[1]])`.
## Caused by warning in `tratar_area()`:
## ! NAs introduzidos por coerção
## Baixando período: 2024 ...
## Baixando período: 2025 ...
## Request failed [404]. Retrying in 5.4 seconds...
## Request failed [404]. Retrying in 9.9 seconds...
## Falha ao acessar ou salvar o arquivo do período: 2025
## Baixando período: 2025 ...
## Request failed [404]. Retrying in 3.2 seconds...
## Request failed [404]. Retrying in 9.8 seconds...
## Falha ao acessar ou salvar o arquivo do período: 2025
## Baixando período: 2025 ...
## Request failed [404]. Retrying in 1 seconds...
## Request failed [404]. Retrying in 7.5 seconds...
## Falha ao acessar ou salvar o arquivo do período: 2025
## Baixando período: 2025 ...
## Request failed [404]. Retrying in 1.4 seconds...
## Request failed [404]. Retrying in 4.4 seconds...
## Falha ao acessar ou salvar o arquivo do período: 2025
## Baixando período: 2025 ...
## Request failed [404]. Retrying in 1.4 seconds...
## Request failed [404]. Retrying in 1.7 seconds...
## Falha ao acessar ou salvar o arquivo do período: 2025
## Baixando período: 2025 ...
## Baixando período: 2025 ...
## Request failed [404]. Retrying in 1.7 seconds...
## Request failed [404]. Retrying in 6.1 seconds...
## Falha ao acessar ou salvar o arquivo do período: 2025
## Baixando período: 2025 ...
## Baixando período: 2025 ...
## Baixando período: 2025 ...
## Baixando período: 2025 ...
## Baixando período: 2025 ...
## Baixando período: 2026 ...
## Baixando período: 2026 ...
## Warning: There was 1 warning in `mutate()`.
## ℹ In argument: `area = tratar_area(pick(contains("area_const"))[[1]])`.
## Caused by warning in `tratar_area()`:
## ! NAs introduzidos por coerção
## Baixando período: 2026 ...
## Baixando período: 2026 ...
## Request failed [404]. Retrying in 1.8 seconds...
## Request failed [404]. Retrying in 8.7 seconds...
## Falha ao acessar ou salvar o arquivo do período: 2026
## Baixando período: 2026 ...
## Request failed [404]. Retrying in 4.4 seconds...
## Request failed [404]. Retrying in 6.3 seconds...
## Falha ao acessar ou salvar o arquivo do período: 2026
## n
## 1 0.09418641
Metodologia de Filtro por Coeficiente de Declaração (CD) Para identificar as 42.136 transações com indícios de subdeclaração (os 9,4% da base total de 448.260 registros), aplicamos o Coeficiente de Declaração (CD). Este coeficiente é definido pela razão entre o valor por metro quadrado declarado pelo contribuinte e o valor de mercado (mediana) apurado para a mesma microrregião (rua, bairro e padrão construtivo). A utilização da mediana como denominador garante que o referencial de comparação seja o “preço de equilíbrio” do mercado local, eliminando as distorções inerentes à média aritmética.
Critério de Seleção: O Corte de 67% O estabelecimento do ponto de corte em 0,67 (67%) atua como uma régua de conformidade fiscal. Transações que apresentam um CD inferior ou igual a 67% indicam que o imóvel foi declarado por um valor que representa, no máximo, dois terços do valor de mercado praticado em sua vizinhança imediata. Em termos estatísticos, esse desvio negativo é agressivo demais para ser atribuído a variações normais de mercado ou ao estado de conservação do imóvel, consolidando-se como uma evidência robusta de subfaturamento no valor de face da alienação.
Nota Técnica: Este critério de 67% é extremamente conservador e defensável. Ao selecionar apenas os casos onde a declaração é inferior a dois terços do valor de mercado justo, o modelo minimiza drasticamente a possibilidade de “falsos positivos” (contribuintes que declararam valores próximos à realidade, mas foram penalizados por variações pontuais). Isso confere ao Fisco uma base de autuação com altíssima probabilidade de manutenção em um eventual litígio administrativo.
Impacto na Auditoria Ao aplicar este filtro sobre as 448.260 transações, isolamos precisamente o subconjunto de 42.136 casos onde a omissão de receita imobiliária é manifesta. O prejuízo ao erário, nestes casos, não é uma estimativa, mas o resultado direto da diferença entre o imposto recolhido sobre o valor subdeclarado e o imposto devido sobre o valor justo de mercado. Esta metodologia transforma a complexa tarefa de auditar o ganho de capital em um processo de seleção por inteligência, permitindo que a autoridade fiscal direcione esforços para o que é, comprovadamente, uma fonte de evasão tributária sobre o ganho de capital imobiliário. # Valor declarado em Belo Horizonte O gráfico apresenta a evolução do Valor Total Declarado em transações imobiliárias em Belo Horizonte de 2008 a 2026. Analisando a série histórica, observamos um comportamento que alterna períodos de forte crescimento com quedas bruscas, o que é característico do mercado imobiliário influenciado por fatores macroeconômicos e fiscais.
Análise dos Ciclos Observados: Expansão (2008–2013): O período inicial mostra uma tendência de alta consistente, com o volume financeiro saindo de patamares próximos a 14 bilhões de reais e atingindo seu pico histórico na série, superando os 18 bilhões em 2013. Este período sugere um mercado aquecido e em fase de valorização acelerada.
O “Vale” de 2014: O ano de 2014 registra uma queda drástica e atípica na série, onde o valor total declarado despenca para a casa dos 6-7 bilhões de reais. Esta queda abrupta é um indicador crítico: em um intervalo de apenas um ano, o valor transacionado caiu mais de 60%.
Recuperação e Estabilização (2015–2020): Após o choque de 2014, o mercado iniciou uma lenta recuperação, apresentando um crescimento gradual e constante até 2020.
Segundo Pico (2021): Observa-se um novo pico de volume financeiro em 2021, que alcançou quase o mesmo patamar de 2013 (aproximadamente 18 bilhões de reais).
Contração Recente (2022–2026): A partir de 2022, nota-se uma tendência de queda sucessiva. Os valores de 2025 e 2026 mostram um volume muito inferior ao histórico da série, situando-se abaixo da marca de 6 a 8 bilhões de reais.
library(tidyverse)
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
df_bh <- resultados_modelagem %>%
group_by(ano) %>%
summarise(total = sum(as.numeric(v_decl_real), na.rm = TRUE), .groups = 'drop')
plot_ly(df_bh, x = ~ano, y = ~total, type = 'bar',
marker = list(color = 'rgb(55, 83, 109)')) %>%
layout(
title = "Valor Total Declarado - Belo Horizonte",
xaxis = list(title = "Ano", fixedrange = TRUE), # Trava o zoom no eixo X
yaxis = list(title = "Total Declarado (R$)", fixedrange = TRUE) # Trava o zoom no eixo Y
) %>%
config(
displayModeBar = FALSE, # Remove a barra de ferramentas (zoom, salvar, etc.)
scrollZoom = FALSE, # Desativa o zoom com scroll
doubleClick = FALSE, # Desativa o reset de zoom com clique duplo
showAxisDragHandles = FALSE # Remove alças de redimensionamento
)
O gráfico revela uma trajetória de gradual aprimoramento na conformidade fiscal das transações imobiliárias em Belo Horizonte ao longo das últimas duas décadas. Observamos que, enquanto a linha verde (transações “Conformes”, com coeficiente acima de 0,67) apresenta uma tendência de crescimento consistente — saindo de patamares próximos a 88% para superar os 95% em 2026 —, a linha vermelha (“Selecionados”, com risco de subdeclaração) exibe uma contração sustentada, reduzindo-se de picos próximos a 14% para cerca de 4% no período recente. Essa convergência positiva sugere que a divergência entre o valor declarado e o valor de mercado (mediana) tem sido sistematicamente reduzida, indicando que o mercado imobiliário da capital mineira está operando com níveis cada vez maiores de precisão declaratória e transparência fiscal.
Essa melhoria na acurácia da avaliação imobiliária e na redução do subfaturamento pode ser contextualizada por um conjunto de fatores estruturais e normativos. Em primeiro lugar, o fortalecimento dos mecanismos de cruzamento de dados pela Receita Federal, especialmente com a modernização da Declaração de Operações Imobiliárias (DOI) e a integração com sistemas de cartórios digitais, tornou o risco de detecção de subdeclarações significativamente maior, desencorajando estratégias de evasão que eram comuns no passado. Adicionalmente, mudanças nas normas contábeis e a maior vigilância sobre a origem de recursos em transações de alto valor impuseram um custo de oportunidade à subdeclaração que, hoje, supera o benefício fiscal imediato. Paralelamente, o próprio setor imobiliário, pressionado por exigências de instituições financeiras para aprovação de crédito hipotecário — que utilizam avaliações rigorosas de mercado —, acabou por alinhar os valores declarados aos valores praticados, consolidando um ambiente de maior conformidade que se reflete na clara estabilização observada no gráfico.
# 1. Agrupamento anual
df_grupo <- df %>% group_by(ano) %>% summarise(n_grupo = n(), .groups = 'drop')
df_total <- resultados_modelagem %>% group_by(ano) %>% summarise(n_total = n(), .groups = 'drop')
# 2. Join e Cálculo da Porcentagem
df_perc <- df_total %>%
left_join(df_grupo, by = "ano") %>%
mutate(
n_grupo = replace_na(n_grupo, 0),
percentual = round((n_grupo / n_total) * 100, 2)
)
# 3. Gráfico de Percentual
plot_ly(df_perc, x = ~ano, y = ~percentual, type = 'scatter', mode = 'lines+markers',
line = list(color = 'rgb(204, 102, 0)', width = 3),
marker = list(color = 'rgb(204, 102, 0)', size = 8)) %>%
layout(
title = "Representatividade da Auditoria sobre o Mercado de BH (%)",
xaxis = list(title = "Ano", fixedrange = TRUE),
yaxis = list(title = "Porcentagem do Total (%)", range = c(0, max(df_perc$percentual) + 5), fixedrange = TRUE)
) %>%
config(displayModeBar = FALSE)
A análise dos dados, consolidada no gráfico “Volume Anual de Transações Imobiliárias em Belo Horizonte (Grupo escolhido)”, demonstra uma redução drástica e contínua no volume de transações que apresentam valores declarados abaixo de um terço da mediana de mercado. Essa queda, que se intensificou ao longo da última década, é um indicador empírico de que as estratégias de subfaturamento imobiliário tornaram-se menos frequentes e mais arriscadas para o contribuinte, sinalizando uma mudança de comportamento no setor imobiliário da capital mineira.
Essa tendência de declínio no volume de transações atípicas está intrinsecamente ligada ao fortalecimento dos mecanismos de fiscalização e à modernização das ferramentas de cruzamento de dados. A integração efetiva dos sistemas de cartórios com a Receita Federal, através de uma Declaração de Operações Imobiliárias (DOI) mais robusta, permitiu ao fisco um monitoramento contínuo, tornando cada vez mais difícil a manutenção de valores declarados que destoam significativamente das referências de mercado.
Somado a isso, o endurecimento das normas de combate à lavagem de dinheiro e a maior vigilância sobre a origem dos recursos transformaram o custo de compliance para o setor imobiliário, fazendo com que a conformidade fiscal se tornasse a opção mais segura para a maioria dos agentes econômicos. Consequentemente, o gráfico “Evolução da Conformidade: Selecionados vs. Conformes” confirma essa migração para a regularidade, onde o cerco tecnológico e normativo não apenas inibiu a evasão, mas validou um mercado que opera com maior precisão e transparência declaratória.
library(tidyverse)
library(plotly)
# 1. Agrupamento anual simples
df_volume <- df %>%
group_by(ano) %>%
summarise(transacoes = n(), .groups = 'drop')
# 2. Gráfico de barras do volume de transações
plot_ly(df_volume, x = ~ano, y = ~transacoes, type = 'bar',
marker = list(color = 'rgb(70, 130, 180)')) %>%
layout(
title = "Volume Anual de Transações Imobiliárias em Belo Horizonte (Grupo escolhido)",
xaxis = list(title = "Ano", fixedrange = TRUE),
yaxis = list(title = "Total de Transações", fixedrange = TRUE)
) %>%
config(
displayModeBar = FALSE,
scrollZoom = FALSE
)
Aos leitores do Audita Belo Horizonte, é fundamental compreender que as transações imobiliárias que apresentam valores declarados drasticamente inferiores à mediana de mercado — especificamente aquelas com valores abaixo de um terço da referência — não representam apenas uma anomalia fiscal; elas carregam um risco jurídico e financeiro severo para as partes envolvidas, especialmente para o inquilino ou adquirente final. Ao ocultar o real Custo de Formação de Seleção (CFS) ou o valor efetivo de mercado, essas operações fragilizam a segurança jurídica do negócio, criando obstáculos intransponíveis em cenários de litígio.
Quando uma transação é registrada com valores subfaturados para benefício fiscal ou ocultação de patrimônio, o adquirente ou inquilino se vê em uma posição de extrema vulnerabilidade em casos de danos morais, vícios ocultos ou irregularidades contratuais. A ausência de um documento que reflita a realidade financeira da transação inviabiliza a plena aplicação das proteções do Código de Defesa do Consumidor (CDC), uma vez que a prova da relação de consumo e do prejuízo real torna-se obscura ou juridicamente contestável.
Portanto, advertimos que a escolha por transações que operam sob essa “margem de risco” é, na prática, uma renúncia à garantia de reparação. Em caso de danos, o prejudicado terá dificuldades de acesso a uma tutela jurisdicional eficaz, pois o contrato “maquiado” não serve como base sólida para calcular indenizações ou corrigir abusos. A transparência nos valores declarados não é apenas uma obrigação com o fisco, mas o escudo mais importante do cidadão para assegurar que, diante de qualquer irregularidade, ele tenha o pleno direito de reaver seus investimentos e ser compensado por eventuais prejuízos.
Construção da Régua Inflacionária (IPCA) Primeiro, o sistema acessa os dados históricos do Banco Central para criar um índice acumulado desde 1994. Esta “régua” é o que permite traduzir o valor da moeda ao longo do tempo, transformando a inflação mensal em uma escala contínua que serve como base de referência para a correção de valores nominais.
Mapeamento e Alinhamento Temporal Em seguida, o script cruza as datas de transação contidas no arquivo resultados_modelagem com os índices correspondentes da régua inflacionária. O objetivo é ajustar os valores declarados e as bases de cálculo para que possamos comparar o poder de compra de diferentes períodos de forma técnica, garantindo que cada transação esteja devidamente situada no seu contexto econômico de época.
Loop de Auditoria (Cálculo Retroativo) Por fim, executamos o processamento linha por linha via RAM, utilizando um loop de alta performance. Para cada imóvel no arquivo, o sistema calcula qual seria o ano equivalente ao valor informado, identificando o “desvio temporal” entre o valor nominal declarado e a realidade histórica da inflação. Este método isola cada registro, garantindo que o cálculo de cada autuação seja preciso, eficiente e totalmente à prova de travamentos, integrando os novos campos ano_compra_decl e ano_compra_base diretamente na sua estrutura de dados.
library(tidyverse)
library(lubridate)
library(GetBCBData)
## Warning: pacote 'GetBCBData' foi compilado no R versão 4.4.3
# --- 1. BAIXAR IPCA DO BANCO CENTRAL ---
message("Baixando IPCA do Banco Central...")
## Baixando IPCA do Banco Central...
ipca_historico <- gbcbd_get_series(
id = 433,
first.date = as.Date("1994-07-01"),
last.date = as.Date("2026-03-01")
)
## ℹ using sequential data fetching for 11 time periods
## ℹ Fetching id = 433 [id=433] | 1994-07-01 -> 1997-07-01
## Warning in open.connection(con, "rb"): cannot open URL
## 'https://api.bcb.gov.br/dados/serie/bcdata.sgs.433/dados?formato=json&dataInicial=01/07/1994&dataFinal=01/07/1997':
## HTTP status was '502 Bad Gateway'
## ! Fetch failed for id=433. Retrying in 2s (attempt 1/3)...
## ℹ Fetching id = 433 [id=433] | 1997-07-01 -> 2000-07-01
## ℹ Fetching id = 433 [id=433] | 2000-07-01 -> 2003-07-01
## ℹ Fetching id = 433 [id=433] | 2003-07-01 -> 2006-07-01
## ℹ Fetching id = 433 [id=433] | 2006-07-01 -> 2009-07-01
## ℹ Fetching id = 433 [id=433] | 2009-07-01 -> 2012-07-01
## ℹ Fetching id = 433 [id=433] | 2012-07-01 -> 2015-07-01
## ℹ Fetching id = 433 [id=433] | 2015-07-01 -> 2018-07-01
## ℹ Fetching id = 433 [id=433] | 2018-07-01 -> 2021-07-01
## ℹ Fetching id = 433 [id=433] | 2021-07-01 -> 2024-07-01
## ℹ Fetching id = 433 [id=433] | 2024-07-01 -> 2026-03-01
## ✔ got data with 381 rows and 4 columns
## ✔ Finished fetching data. Total rows: 381
regua_ipca <- ipca_historico %>%
mutate(
data_mes = floor_date(ref.date, "month"),
fator = 1 + (value / 100)
) %>%
arrange(data_mes) %>%
mutate(
indice_ipca = cumprod(fator),
ano_ipca = as.integer(year(data_mes))
) %>%
filter(!is.na(indice_ipca)) %>%
select(data_mes, indice_ipca, ano_ipca)
vetor_datas_ipca <- regua_ipca$data_mes
vetor_indices_ipca <- regua_ipca$indice_ipca
vetor_anos_ipca <- regua_ipca$ano_ipca
# --- 2. PREPARAÇÃO DA BASE PRINCIPAL ---
message("Preparando dados...")
## Preparando dados...
df <- df %>%
mutate(
data_trans_limpa = as.Date(parse_date_time(as.character(data_trans), orders = c("dmy", "ymd"))),
data_mes_venda = floor_date(data_trans_limpa, "month"),
ano_venda_real = as.integer(year(data_trans_limpa))
)
indice_venda_map <- regua_ipca$indice_ipca
names(indice_venda_map) <- as.character(regua_ipca$data_mes)
df <- df %>%
mutate(
indice_ipca_venda = indice_venda_map[as.character(data_mes_venda)]
)
# --- 3. LOOP FOR TRADICIONAL (CORRIGIDO PARA USAR O 'df') ---
message("Calculando anos estimados linha por linha...")
## Calculando anos estimados linha por linha...
n_linhas <- nrow(df) # Corrigido para usar df
anos_decl <- rep(NA_integer_, n_linhas)
anos_base <- rep(NA_integer_, n_linhas)
v_justo_vetor <- df$valor_mercado_justo
v_decl_vetor <- df$v_decl_real
v_base_vetor <- df$v_base_real
d_venda_vetor <- df$data_trans_limpa
ind_venda_vetor <- df$indice_ipca_venda
for (i in 1:n_linhas) {
v_justo <- v_justo_vetor[i]
v_decl <- v_decl_vetor[i]
v_base <- v_base_vetor[i]
d_venda <- d_venda_vetor[i]
ind_venda <- ind_venda_vetor[i]
if (is.na(v_justo) || is.na(d_venda) || is.na(ind_venda)) next
passado_permitido <- vetor_datas_ipca <= d_venda
indices_passados <- vetor_indices_ipca[passado_permitido]
anos_passados <- vetor_anos_ipca[passado_permitido]
if (length(indices_passados) == 0) next
# Estimativa Declarado
if (!is.na(v_decl) && v_decl > 0) {
indice_alvo_decl <- ind_venda / (v_justo / v_decl)
anos_decl[i] <- anos_passados[which.min(abs(indices_passados - indice_alvo_decl))]
}
# Estimativa Base
if (!is.na(v_base) && v_base > 0) {
indice_alvo_base <- ind_venda / (v_justo / v_base)
anos_base[i] <- anos_passados[which.min(abs(indices_passados - indice_alvo_base))]
}
}
# --- 4. GRAVA OS RESULTADOS DE VOLTA NA BASE ---
df$ano_compra_decl <- anos_decl
df$ano_compra_base <- anos_base
# --- 5. LIMPEZA FINAL ---
df <- df %>%
select(-data_trans_limpa, -data_mes_venda, -indice_ipca_venda, -ano_venda_real)
message("Processo concluído com sucesso!")
## Processo concluído com sucesso!
Ao acessar o arquivo resultados_modelagem para este processamento, o script realiza a estruturação do cálculo da alíquota efetiva de Imposto de Renda sobre ganho de capital, considerando o benefício fiscal do tempo de posse. O processo é segmentado em etapas que transformam os anos estimados de compra em uma carga tributária ajustada.
Definição do Período de Detenção (Tempo de Posse) O sistema acessa as datas de transação e compara o ano da venda com o ano_compra_decl que calculamos anteriormente. Esta etapa é crucial para determinar a duração da detenção do imóvel, garantindo que qualquer inconsistência — como datas de compra posteriores à venda ou dados ausentes — seja tratada como tempo zero, estabelecendo uma base de cálculo justa e conservadora.
Aplicação do Fator de Redução Fiscal Com base no tempo de posse, o código aplica um fator de redução progressiva (estimado em 4% ao ano), que simula o benefício fiscal concedido pela legislação sobre o lucro na alienação de bens. O sistema limita esse fator a 100%, impedindo que o cálculo ultrapasse o limite de isenção total, transformando o “tempo” em uma variável financeira que impacta diretamente a base tributável.
Cálculo da Alíquota Efetiva Por fim, o script processa a alíquota final aplicando a redução sobre a alíquota base de 15%. O resultado é armazenado na coluna aliquota_efetiva_pct, que entrega ao auditor um indicador claro de quanto de imposto deveria ser efetivamente aplicado, considerando o histórico de valorização e o período de posse.
O processo remove as variáveis auxiliares de ano_venda_real e fator_reducao_total, mantendo a base resultados_modelagem organizada e pronta para o cálculo do imposto devido (IR) e a estimativa de sonegação. Este bloco finaliza a lógica de “regressão fiscal”, permitindo que você compare o que foi pago com o que o modelo estima ser o cenário justo de tributação.
O código que você solicitou foi estruturado para consolidar toda a sua base de auditoria (resultados_modelagem) em um painel interativo de alta performance. O objetivo é permitir que você transite rapidamente entre uma visão macro (gerencial) e uma visão micro (fiscalização individual).
Abaixo, explico as etapas principais do que foi realizado:
Limpeza e Engenharia Fiscal O sistema acessa o arquivo resultados_modelagem, remove registros incompletos e trata a codificação de caracteres para evitar erros. Em seguida, ele aplica toda a lógica de auditoria que validamos anteriormente: calcula o ganho ocultado, define a alíquota efetiva com base na progressividade do tempo de posse e estima a autuação (IR + multa). Esta etapa transforma dados brutos em informação fiscal acionável.
Visão Macro: Sumarização por Bairro O código cria uma tabela de apoio sumarizada (df_bairro_summary). Ao agrupar por bairro, ele calcula automaticamente métricas como o número de imóveis, a média do valor declarado e a média do coefdecl. Isso permite que você identifique, em segundos, quais regiões de Belo Horizonte apresentam comportamentos de declaração mais discrepantes, funcionando como um filtro de risco antes de investigar endereços específicos.
Visão Micro: Detalhamento por Endereço Para a análise individual, utilizamos o pacote crosstalk integrado ao datatable.
Performance: A busca foi configurada para utilizar a barra de pesquisa nativa do datatable (dom = ‘Bftip’). Ao digitar qualquer trecho de endereço ou bairro, o sistema aplica um filtro dinâmico (equivalente ao str_detect) que responde instantaneamente, mesmo com dezenas de milhares de linhas, pois toda a filtragem ocorre na memória do seu navegador.
Interatividade: O filtro de bairro no topo do painel atua como um seletor global. Ao selecionar um bairro, todas as tabelas e métricas associadas ao crosstalk são atualizadas de forma síncrona.
O resultado é um ecossistema de dados robusto, onde a sua base resultados_modelagem é processada em memória RAM para garantir velocidade, evitando os travamentos que você enfrentou anteriormente. O painel final entrega, em uma única tela, o controle total sobre a cidade, da visão dos bairros até o detalhe do imóvel.
library(data.table)
library(lubridate)
# Converte para data.table
setDT(df)
# 1. Limpeza inicial focada nas variáveis necessárias
df <- df[!is.na(v_decl_real) & !is.na(valor_mercado_justo)]
# 2. Cálculos por referência (Performance máxima em RAM)
# Garante tipos numéricos
df[, v_decl_real := as.numeric(v_decl_real)]
df[, valor_mercado_justo := as.numeric(valor_mercado_justo)]
df[, aliquota_efetiva := as.numeric(aliquota_efetiva)]
# Calcula o ganho ocultado e a autuação estimada
df[, ganho_ocultado := pmax(valor_mercado_justo - v_decl_real, 0)]
# Cálculo do IR base considerando a alíquota efetiva
df[, ir_base := round(ganho_ocultado * ifelse(aliquota_efetiva == 0, 0.15, aliquota_efetiva), 2)]
# Cálculo das dívidas (total_autuacao_estimada)
# Considera multa de 150% se alíquota for 0 (evasão pura) ou 70% caso contrário
df[, total_autuacao_estimada := ir_base + (ir_base * ifelse(aliquota_efetiva == 0, 1.50, 0.70))]
# 3. Seleção e organização das colunas solicitadas
# Mantemos apenas o que é essencial para a auditoria
colunas_finais <- c(
"coefdecl",
"v_decl_real",
"tempo_posse_anos",
"ano_compra_decl",
"total_autuacao_estimada"
)
# Filtra o dataset para essas colunas (se quiser manter as outras, remova esta linha)
df_auditoria <- df[, ..colunas_finais]
# 4. Deduplicação por endereço (mantendo a transação mais recente)
df[, data_formatada := as.Date(parse_date_time(data_trans, orders = c("dmy", "ymd")))]
df <- df[order(data_formatada), .SD[.N], by = endereco_oficial]
# Salva e limpa a memória
saveRDS(df, "base_auditoria_pronta.rds")
gc() # Força a coleta de lixo
## used (Mb) gc trigger (Mb) max used (Mb)
## Ncells 1955948 104.5 3965522 211.8 3965522 211.8
## Vcells 25900035 197.7 46821012 357.3 38942836 297.2
message("Processamento concluído. Base de auditoria pronta.")
## Processamento concluído. Base de auditoria pronta.
library(DT)
library(data.table)
##
## Anexando pacote: 'data.table'
## Os seguintes objetos são mascarados por 'package:lubridate':
##
## hour, isoweek, mday, minute, month, quarter, second, wday, week,
## yday, year
## Os seguintes objetos são mascarados por 'package:dplyr':
##
## between, first, last
## O seguinte objeto é mascarado por 'package:purrr':
##
## transpose
# 1. Definição das colunas
colunas_auditoria <- c(
"endereco_oficial", "coefdecl", "v_decl_real", "valor_mercado_justo",
"data_trans", "ano_compra_decl", "tempo_posse_anos", "aliquota_efetiva_pct",
"ir_base", "total_autuacao_estimada"
)
# 2. Renderização DIRETA do DT (sem SharedData)
# 'server = TRUE' é o que impede o travamento ao abrir o arquivo
datatable(
df[, ..colunas_auditoria],
filter = 'top',
options = list(
pageLength = 10,
searchHighlight = TRUE,
server = TRUE, # Processamento no servidor (Backend R)
dom = 'Bfrtip'
),
colnames = c(
"Endereço" = "endereco_oficial",
"Coef. Decl." = "coefdecl",
"Valor Declarado" = "v_decl_real",
"Valor de Mercado" = "valor_mercado_justo",
"Data Transação" = "data_trans",
"Ano Compra Decl." = "ano_compra_decl",
"Tempo de Posse" = "tempo_posse_anos",
"Alíquota (%)" = "aliquota_efetiva_pct",
"IR Devido" = "ir_base",
"Autuação Estimada" = "total_autuacao_estimada"
)
) %>%
formatCurrency(columns = c(3, 4, 9, 10), currency = "R$ ") %>%
formatString(columns = 8, suffix = "%")
## 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
rm(resultados_modelagem) # Remove o objeto gigante
gc() # Libera a memória RAM para o R
## used (Mb) gc trigger (Mb) max used (Mb)
## Ncells 1963427 104.9 3968691 212.0 3968691 212.0
## Vcells 23568715 179.9 46820754 357.3 38934298 297.1