Nothing
#' Fetch Banco Central do Brasil (BCB) data
#'
#' Retrieve time series data from the Banco Central do Brasil SGS (Sistema Gerenciador de Séries
#' Temporais) API.
#'
#' Daily series require a date range and the API limits the query window to at most 10 years; supply
#' `start_date` (and optionally `end_date`) when querying such series.
#'
#' @param series (`integer(1)`)\cr
#' The SGS series code to query (e.g., `1` for the USD/BRL exchange rate). Series codes can be
#' found on the SGS website.
#' @param start_date (`NULL` | `Date(1)` | `character(1)`)\cr
#' Start date of the data (e.g., `"2024-01-01"`). If `NULL`, all available data is returned.
#' Default `NULL`.
#' @param end_date (`NULL` | `Date(1)` | `character(1)`)\cr
#' End date of the data, in the same format as start_date. If `NULL`, data up to the latest
#' available date is returned. Default `NULL`.
#' @returns A [data.table::data.table()] with the requested data.
#' @source <https://dadosabertos.bcb.gov.br/>
#' @family data
#' @export
#' @examplesIf curl::has_internet()
#' \donttest{
#' # fetch USD/BRL exchange rate
#' bcb_data(1, start_date = "2024-01-01", end_date = "2024-01-31")
#'
#' # fetch the Selic target rate
#' bcb_data(432, start_date = "2024-01-01", end_date = "2024-01-31")
#' }
bcb_data = function(series, start_date = NULL, end_date = NULL) {
series = assert_count(series, positive = TRUE, coerce = TRUE)
start_date = assert_dateish(start_date, null.ok = TRUE)
end_date = assert_dateish(end_date, null.ok = TRUE)
json = bcb(
series,
dataInicial = start_date %&&% format(start_date, "%d/%m/%Y"),
dataFinal = end_date %&&% format(end_date, "%d/%m/%Y")
)
parse_bcb_data(json, series)
}
parse_bcb_data = function(json, series) {
if (length(json) == 0L) {
dt = data.table(date = as.Date(character()), id = character(), value = numeric())
setnames(dt, "id", "key")
return(dt[])
}
data = valor = NULL
dt = rbindlist(json, fill = TRUE)
dt[, let(
date = as.Date(data, "%d/%m/%Y"),
key = as.character(series),
value = as.numeric(valor)
)]
dt[, c("data", "valor") := NULL]
setcolorder(dt, c("date", "key", "value"))
dt[]
}
#' Fetch Banco Central do Brasil (BCB) exchange rates
#'
#' Retrieve PTAX foreign exchange reference rates from the Banco Central do Brasil Olinda API. The
#' rates are the closing (Fechamento) bid and ask quotations expressed in Brazilian real (BRL) per
#' unit of the foreign currency.
#'
#' Rates are published only on business days, so weekends and holidays return no rows.
#'
#' @param currency (`character()`)\cr
#' One or more ISO 4217 currency codes (e.g. `"USD"`, `c("USD", "EUR")`). See [bcb_currencies()]
#' for the available currencies.
#' @param start_date (`Date(1)` | `character(1)`)\cr
#' Start date of the data (e.g., `"2024-01-01"`).
#' @param end_date (`NULL` | `Date(1)` | `character(1)`)\cr
#' End date of the data, in the same format as start_date. If `NULL`, the `start_date` is used.
#' Default `NULL`.
#' @returns A [data.table::data.table()] with columns `date`, `currency`, `bid`, and `ask`.
#' @source <https://dadosabertos.bcb.gov.br/>
#' @family data
#' @export
#' @examplesIf curl::has_internet()
#' \donttest{
#' # fetch USD/BRL closing rates
#' bcb_fx_rates("USD", start_date = "2024-01-01", end_date = "2024-01-31")
#'
#' # fetch multiple currencies
#' bcb_fx_rates(c("USD", "EUR"), start_date = "2024-01-01", end_date = "2024-01-31")
#' }
bcb_fx_rates = function(currency, start_date, end_date = NULL) {
assert_character(currency, min.len = 1L, n.chars = 3L, any.missing = FALSE)
start_date = assert_dateish(start_date)
end_date = assert_dateish(end_date, null.ok = TRUE)
end_date = end_date %||% start_date
currency = toupper(currency)
rbindlist(map(currency, function(cur) {
json = bcb_ptax(cur, start_date, end_date)
parse_bcb_fx_rates(json, cur)
}))
}
#' Fetch Banco Central do Brasil (BCB) currencies
#'
#' Retrieve the list of currencies available from the Banco Central do Brasil PTAX API.
#'
#' @returns A [data.table::data.table()] with columns `code`, `name`, and `type`.
#' @source <https://dadosabertos.bcb.gov.br/>
#' @family metadata
#' @export
#' @examplesIf curl::has_internet()
#' \donttest{
#' bcb_currencies()
#' }
bcb_currencies = function() {
json = bcb_olinda("PTAX", "Moedas")
parse_bcb_currencies(json)
}
parse_bcb_currencies = function(json) {
value = json$value
data.table(
code = map_chr(value, "simbolo"),
name = map_chr(value, "nomeFormatado"),
type = map_chr(value, "tipoMoeda")
)
}
#' Fetch Banco Central do Brasil (BCB) market expectations
#'
#' Retrieve market expectations from the Banco Central do Brasil Focus survey (Relatório Focus) via
#' the Olinda API. Each row is the summary of survey responses collected on a given date for a future
#' reference period.
#'
#' Querying without a filter returns the full history, which is large; supplying `indicator` and/or a
#' date range is recommended.
#'
#' @param type (`character(1)`)\cr
#' The forecast horizon. One of `"annual"`, `"monthly"`, or `"quarterly"`. Default `"annual"`.
#' @param indicator (`NULL` | `character(1)`)\cr
#' The economic indicator to filter on (e.g. `"IPCA"`, `"Selic"`, `"Câmbio"`). If `NULL`, all
#' indicators are returned. Default `NULL`.
#' @param start_date (`NULL` | `Date(1)` | `character(1)`)\cr
#' Start of the survey date range (e.g., `"2024-01-01"`). If `NULL`, no lower bound is applied.
#' Default `NULL`.
#' @param end_date (`NULL` | `Date(1)` | `character(1)`)\cr
#' End of the survey date range, in the same format as start_date. If `NULL`, no upper bound is
#' applied. Default `NULL`.
#' @returns A [data.table::data.table()] with columns `date`, `indicator`, `detail`, `reference`,
#' `mean`, `median`, `sd`, `min`, `max`, `respondents`, and `base`.
#' @source <https://dadosabertos.bcb.gov.br/>
#' @family data
#' @export
#' @examplesIf curl::has_internet()
#' \donttest{
#' # annual IPCA inflation expectations
#' bcb_expectations("annual", "IPCA", start_date = "2024-01-01", end_date = "2024-01-31")
#' }
bcb_expectations = function(type = "annual", indicator = NULL, start_date = NULL, end_date = NULL) {
assert_choice(type, c("annual", "monthly", "quarterly"))
assert_string(indicator, min.chars = 1L, null.ok = TRUE)
start_date = assert_dateish(start_date, null.ok = TRUE)
end_date = assert_dateish(end_date, null.ok = TRUE)
resource = switch(
type,
annual = "ExpectativasMercadoAnuais",
monthly = "ExpectativaMercadoMensais",
quarterly = "ExpectativasMercadoTrimestrais"
)
filter = bcb_odata_filter(
indicator %&&% sprintf("Indicador eq '%s'", indicator),
start_date %&&% sprintf("Data ge '%s'", format(start_date)),
end_date %&&% sprintf("Data le '%s'", format(end_date))
)
json = bcb_olinda("Expectativas", resource, `$filter` = filter, `$orderby` = "Data")
parse_bcb_expectations(json)
}
parse_bcb_expectations = function(json) {
value = json$value
if (length(value) == 0L) {
return(data.table(
date = as.Date(character()),
indicator = character(),
detail = character(),
reference = character(),
mean = numeric(),
median = numeric(),
sd = numeric(),
min = numeric(),
max = numeric(),
respondents = integer(),
base = integer()
))
}
data.table(
date = as.Date(map_chr(value, "Data")),
indicator = map_chr(value, "Indicador"),
detail = map_chr(value, \(x) x$IndicadorDetalhe %||% NA_character_),
reference = map_chr(value, "DataReferencia"),
mean = map_dbl(value, \(x) as.numeric(x$Media)),
median = map_dbl(value, \(x) as.numeric(x$Mediana)),
sd = map_dbl(value, \(x) as.numeric(x$DesvioPadrao %||% NA_real_)),
min = map_dbl(value, \(x) as.numeric(x$Minimo %||% NA_real_)),
max = map_dbl(value, \(x) as.numeric(x$Maximo %||% NA_real_)),
respondents = map_int(value, \(x) as.integer(x$numeroRespondentes %||% NA_integer_)),
base = map_int(value, \(x) as.integer(x$baseCalculo %||% NA_integer_))
)
}
#' Fetch Banco Central do Brasil (BCB) Top-5 market expectations
#'
#' Retrieve the Top-5 market expectations from the Banco Central do Brasil Focus survey (Relatório
#' Focus) via the Olinda API. The Top-5 ranking summarises the forecasts of the institutions with
#' the most accurate projections for a given indicator.
#'
#' @param type (`character(1)`)\cr
#' The forecast horizon. One of `"annual"`, `"monthly"`, `"quarterly"`, or `"selic"`. Default
#' `"annual"`.
#' @inheritParams bcb_expectations
#' @returns A [data.table::data.table()] with columns `date`, `indicator`, `type_calc`, `reference`,
#' `mean`, `median`, `sd`, `min`, and `max`. The `type_calc` column holds the ranking horizon
#' (`"C"` short, `"M"` medium, or `"L"` long term). For `type = "selic"` the `reference` column
#' holds the COPOM meeting (e.g. `"R4/2026"`).
#' @source <https://dadosabertos.bcb.gov.br/>
#' @family data
#' @export
#' @examplesIf curl::has_internet()
#' \donttest{
#' # annual Top-5 IPCA inflation expectations
#' bcb_top5("annual", "IPCA", start_date = "2024-01-01", end_date = "2024-01-31")
#'
#' # Top-5 Selic target rate expectations
#' bcb_top5("selic", start_date = "2024-01-01", end_date = "2024-01-31")
#' }
bcb_top5 = function(type = "annual", indicator = NULL, start_date = NULL, end_date = NULL) {
assert_choice(type, c("annual", "monthly", "quarterly", "selic"))
assert_string(indicator, min.chars = 1L, null.ok = TRUE)
start_date = assert_dateish(start_date, null.ok = TRUE)
end_date = assert_dateish(end_date, null.ok = TRUE)
resource = switch(
type,
annual = "ExpectativasMercadoTop5Anuais",
monthly = "ExpectativasMercadoTop5Mensais",
quarterly = "ExpectativaMercadoTop5Trimestral",
selic = "ExpectativasMercadoTop5Selic"
)
# the Selic Top-5 resource names its indicator property in lower case
indic_field = if (type == "selic") "indicador" else "Indicador"
filter = bcb_odata_filter(
indicator %&&% sprintf("%s eq '%s'", indic_field, indicator),
start_date %&&% sprintf("Data ge '%s'", format(start_date)),
end_date %&&% sprintf("Data le '%s'", format(end_date))
)
json = bcb_olinda("Expectativas", resource, `$filter` = filter, `$orderby` = "Data")
parse_bcb_top5(json)
}
parse_bcb_top5 = function(json) {
value = json$value
if (length(value) == 0L) {
return(data.table(
date = as.Date(character()),
indicator = character(),
type_calc = character(),
reference = character(),
mean = numeric(),
median = numeric(),
sd = numeric(),
min = numeric(),
max = numeric()
))
}
# the Selic Top-5 resource uses lower-case field names and `reuniao` for the reference
data.table(
date = as.Date(map_chr(value, "Data")),
indicator = map_chr(value, \(x) x$Indicador %||% x$indicador),
type_calc = map_chr(value, \(x) x$tipoCalculo %||% NA_character_),
reference = map_chr(value, \(x) x$DataReferencia %||% x$reuniao %||% NA_character_),
mean = map_dbl(value, \(x) as.numeric(x$Media %||% x$media)),
median = map_dbl(value, \(x) as.numeric(x$Mediana %||% x$mediana)),
sd = map_dbl(value, \(x) as.numeric(x$DesvioPadrao %||% x$desvioPadrao %||% NA_real_)),
min = map_dbl(value, \(x) as.numeric(x$Minimo %||% x$minimo %||% NA_real_)),
max = map_dbl(value, \(x) as.numeric(x$Maximo %||% x$maximo %||% NA_real_))
)
}
#' Fetch Banco Central do Brasil (BCB) inflation expectations
#'
#' Retrieve the rolling 12- or 24-month inflation expectations from the Banco Central do Brasil
#' Focus survey (Relatório Focus) via the Olinda API.
#'
#' @param horizon (`character(1)`)\cr
#' The forecast horizon, either `"12m"` (next 12 months) or `"24m"` (next 24 months). Default
#' `"12m"`.
#' @inheritParams bcb_expectations
#' @returns A [data.table::data.table()] with columns `date`, `indicator`, `smoothed`, `mean`,
#' `median`, `sd`, `min`, `max`, `respondents`, and `base`. The `smoothed` column indicates
#' whether the forecast is the smoothed (suavizada) series.
#' @source <https://dadosabertos.bcb.gov.br/>
#' @family data
#' @export
#' @examplesIf curl::has_internet()
#' \donttest{
#' # next-12-months IPCA inflation expectations
#' bcb_inflation("12m", "IPCA", start_date = "2024-01-01", end_date = "2024-01-31")
#' }
bcb_inflation = function(horizon = "12m", indicator = NULL, start_date = NULL, end_date = NULL) {
assert_choice(horizon, c("12m", "24m"))
assert_string(indicator, min.chars = 1L, null.ok = TRUE)
start_date = assert_dateish(start_date, null.ok = TRUE)
end_date = assert_dateish(end_date, null.ok = TRUE)
resource = switch(
horizon,
`12m` = "ExpectativasMercadoInflacao12Meses",
`24m` = "ExpectativasMercadoInflacao24Meses"
)
filter = bcb_odata_filter(
indicator %&&% sprintf("Indicador eq '%s'", indicator),
start_date %&&% sprintf("Data ge '%s'", format(start_date)),
end_date %&&% sprintf("Data le '%s'", format(end_date))
)
json = bcb_olinda("Expectativas", resource, `$filter` = filter, `$orderby` = "Data")
parse_bcb_inflation(json)
}
parse_bcb_inflation = function(json) {
value = json$value
if (length(value) == 0L) {
return(data.table(
date = as.Date(character()),
indicator = character(),
smoothed = logical(),
mean = numeric(),
median = numeric(),
sd = numeric(),
min = numeric(),
max = numeric(),
respondents = integer(),
base = integer()
))
}
data.table(
date = as.Date(map_chr(value, "Data")),
indicator = map_chr(value, "Indicador"),
smoothed = map_chr(value, \(x) x$Suavizada %||% NA_character_) == "S",
mean = map_dbl(value, \(x) as.numeric(x$Media)),
median = map_dbl(value, \(x) as.numeric(x$Mediana)),
sd = map_dbl(value, \(x) as.numeric(x$DesvioPadrao %||% NA_real_)),
min = map_dbl(value, \(x) as.numeric(x$Minimo %||% NA_real_)),
max = map_dbl(value, \(x) as.numeric(x$Maximo %||% NA_real_)),
respondents = map_int(value, \(x) as.integer(x$numeroRespondentes %||% NA_integer_)),
base = map_int(value, \(x) as.integer(x$baseCalculo %||% NA_integer_))
)
}
#' Fetch Banco Central do Brasil (BCB) Selic expectations
#'
#' Retrieve market expectations for the Selic target rate from the Banco Central do Brasil Focus
#' survey (Relatório Focus) via the Olinda API. Each row summarises the forecasts collected on a
#' given survey date for a future COPOM meeting.
#'
#' @param start_date (`NULL` | `Date(1)` | `character(1)`)\cr
#' Start of the survey date range (e.g., `"2024-01-01"`). If `NULL`, no lower bound is applied.
#' Default `NULL`.
#' @param end_date (`NULL` | `Date(1)` | `character(1)`)\cr
#' End of the survey date range, in the same format as start_date. If `NULL`, no upper bound is
#' applied. Default `NULL`.
#' @returns A [data.table::data.table()] with columns `date`, `meeting`, `mean`, `median`, `sd`,
#' `min`, `max`, `respondents`, and `base`. The `meeting` column identifies the COPOM meeting the
#' forecast refers to (e.g. `"R3/2028"`).
#' @source <https://dadosabertos.bcb.gov.br/>
#' @family data
#' @export
#' @examplesIf curl::has_internet()
#' \donttest{
#' bcb_selic(start_date = "2024-01-01", end_date = "2024-01-31")
#' }
bcb_selic = function(start_date = NULL, end_date = NULL) {
start_date = assert_dateish(start_date, null.ok = TRUE)
end_date = assert_dateish(end_date, null.ok = TRUE)
filter = bcb_odata_filter(
start_date %&&% sprintf("Data ge '%s'", format(start_date)),
end_date %&&% sprintf("Data le '%s'", format(end_date))
)
json = bcb_olinda(
"Expectativas",
"ExpectativasMercadoSelic",
`$filter` = filter,
`$orderby` = "Data"
)
parse_bcb_selic(json)
}
parse_bcb_selic = function(json) {
value = json$value
if (length(value) == 0L) {
return(data.table(
date = as.Date(character()),
meeting = character(),
mean = numeric(),
median = numeric(),
sd = numeric(),
min = numeric(),
max = numeric(),
respondents = integer(),
base = integer()
))
}
data.table(
date = as.Date(map_chr(value, "Data")),
meeting = map_chr(value, \(x) x$Reuniao %||% NA_character_),
mean = map_dbl(value, \(x) as.numeric(x$Media)),
median = map_dbl(value, \(x) as.numeric(x$Mediana)),
sd = map_dbl(value, \(x) as.numeric(x$DesvioPadrao %||% NA_real_)),
min = map_dbl(value, \(x) as.numeric(x$Minimo %||% NA_real_)),
max = map_dbl(value, \(x) as.numeric(x$Maximo %||% NA_real_)),
respondents = map_int(value, \(x) as.integer(x$numeroRespondentes %||% NA_integer_)),
base = map_int(value, \(x) as.integer(x$baseCalculo %||% NA_integer_))
)
}
bcb_odata_filter = function(...) {
clauses = c(...)
if (length(clauses) > 0L) paste(clauses, collapse = " and ") else NULL
}
parse_bcb_fx_rates = function(json, currency) {
value = json$value
if (length(value) == 0L) {
return(data.table(
date = as.Date(character()),
currency = character(),
bid = numeric(),
ask = numeric()
))
}
data.table(
date = as.Date(map_chr(value, "dataHoraCotacao")),
currency = currency,
bid = map_dbl(value, "cotacaoCompra"),
ask = map_dbl(value, "cotacaoVenda")
)
}
bcb = function(series, ...) {
base_request("https://api.bcb.gov.br/dados/serie") |>
req_url_path_append(sprintf("bcdata.sgs.%s", series), "dados") |>
req_url_query(formato = "json", ...) |>
req_error(body = bcb_error_body) |>
req_perform() |>
resp_body_json()
}
bcb_ptax = function(currency, start_date, end_date) {
resource = paste0(
"CotacaoMoedaPeriodo(moeda=@moeda,dataInicial=@dataInicial,",
"dataFinalCotacao=@dataFinalCotacao)"
)
bcb_olinda(
"PTAX",
resource,
`@moeda` = sprintf("'%s'", currency),
`@dataInicial` = sprintf("'%s'", format(start_date, "%m-%d-%Y")),
`@dataFinalCotacao` = sprintf("'%s'", format(end_date, "%m-%d-%Y")),
`$filter` = "tipoBoletim eq 'Fechamento'",
`$select` = "cotacaoCompra,cotacaoVenda,dataHoraCotacao"
)
}
bcb_olinda = function(service, resource, ...) {
url = sprintf("https://olinda.bcb.gov.br/olinda/servico/%s/versao/v1/odata", service)
base_request(url) |>
req_url_path_append(resource) |>
req_url_query(..., `$format` = "json") |>
req_error(body = bcb_error_body) |>
req_perform() |>
resp_body_json()
}
bcb_error_body = function(resp) {
resp_body_string(resp, "UTF-8")
}
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.