The problem
ESTBAN is assembled by the Central Bank from the monthly returns of each institution. Once in a while a bank’s return does not make it into the file. The bank is still listed, with every branch and every account equal to zero. A documented case is Banco Santander from January to March 2025: three consecutive months at zero in the whole country, then back to normal in April.
This is a non-report, not a zero balance sheet. Summing those rows into a municipal series makes credit and deposits fall by the bank’s share for three months and jump back, and any model fed with the series reads that as a real shock. Re-downloading does not help: the published files are what they are.
Detecting it
estban_flag_nonreport() marks every row of an
institution-month whose accounts sum to zero across the whole
table. The wider the table, the safer the test: one dormant
branch can legitimately have zero everywhere, but a bank with zeros in
every city of a state (or of the country) has not reported.
Let us build a three-month series from the extract shipped with the package and make one bank vanish in February:
library(estbanr)
f <- system.file("extdata", "202401_ESTBAN_AG_sample.CSV", package = "estbanr")
m1 <- estban_read(f, uf = "PE")
verb <- grep("^verbete_", names(m1))
m2 <- m1; m2$ref <- 202402L; m2[, verb] <- m2[, verb] * 1.05
m3 <- m1; m3$ref <- 202403L; m3[, verb] <- m3[, verb] * 1.10
m2[m2$cnpj == "60746948", verb] <- 0 # Bradesco: nothing in February
x <- rbind(m1, m2, m3)
flagged <- estban_flag_nonreport(x)
unique(flagged[flagged$nonreport, c("nome_instituicao", "ref")])
#> # A tibble: 1 × 2
#> nome_instituicao ref
#> <chr> <int>
#> 1 BCO BRADESCO S.A. 202402Fixing it
estban_impute_nonreport() treats the flagged
institution-months as missing, collapses branches to one row per
institution and municipality, and fills interior gaps
by linear interpolation along each (institution, municipality, account)
series. Gaps at the start or end of a series are left as
NA: a bank that stopped reporting last month stays missing
until the file is revised, instead of being invented.
imp <- estban_impute_nonreport(x)
#> ℹ 1 institution-month flagged as non-report; interpolating interior gaps.
v <- "verbete_160_operacoes_de_credito"
imp[imp$cnpj == "60746948" & imp$municipio == "CARUARU", c("ref", v, "imputed")]
#> # A tibble: 3 × 3
#> ref verbete_160_operacoes_de_credito imputed
#> <int> <dbl> <int>
#> 1 202401 23110341 0
#> 2 202402 24265858. 45
#> 3 202403 25421375. 0February is now the midpoint of January and March, and the
imputed column says how many accounts were filled in that
row (all 45).
Effect on the municipal series
estban_by_municipality() runs the imputation by default.
Compare the credit series of Caruaru with and without it:
raw <- estban_by_municipality(x, impute = FALSE)
fix <- estban_by_municipality(x)
#> ℹ 1 institution-month flagged as non-report; interpolating interior gaps.
data.frame(
ref = raw$ref[raw$municipio == "CARUARU"],
raw = raw[[v]][raw$municipio == "CARUARU"],
imputed = fix[[v]][fix$municipio == "CARUARU"]
)
#> ref raw imputed
#> 1 202401 3368237459 3368237459
#> 2 202402 3512383474 3536649332
#> 3 202403 3705061205 3705061205Without the treatment, February is short by the whole credit of the missing bank (here a small share of the city, so the total does not even fall, which is exactly what makes the error hard to spot); with it, February sits between January and March as it should.
When not to impute
- Short extracts. With a single month there are no neighbours; the flag still works, the interpolation cannot.
-
Genuine exits. A bank that closed all branches in a
municipality shows zero from then on. That is an edge gap and is left as
NAby design, but if the closure happened between two reported months in your window (it reopened later), the interpolation would bridge it. Look at theimputedcolumn and atnonreportbefore trusting a long bridge. -
Balance identities. Interpolated accounts no longer
add up exactly to the totals (
verbete_399_total_do_ativo,verbete_899_total_do_passivo), because each account is interpolated separately. Recompute totals from components if you need the identity.