diff --git a/DESCRIPTION b/DESCRIPTION index bab63f2..9ee633a 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -19,7 +19,8 @@ Imports: stringr, usethis, tidyr, - yaml + yaml, + readr Suggests: urca, tsibble, diff --git a/NAMESPACE b/NAMESPACE index 534ad48..66f2f71 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -22,6 +22,7 @@ export(get_pib_gasto) export(get_pib_sectores) export(get_prestamos_osd) export(get_remesas) +export(get_tasa_interbancaria) export(get_tasas_activas) export(get_tasas_pasivas) export(get_tc) diff --git a/R/operaciones_interbancarias.R b/R/operaciones_interbancarias.R new file mode 100644 index 0000000..f8af406 --- /dev/null +++ b/R/operaciones_interbancarias.R @@ -0,0 +1,101 @@ +#' Get average interbank interest rates by maturity +#' +#' Downloads and processes the official Average Interbank Rates by Maturity +#' file published by the Central Bank of the Dominican Republic. +#' +#' The function: +#' \itemize{ +#' \item Downloads the .xlsm file from the institutional CDN. +#' \item Cleans headers and aggregated rows (cumulative values). +#' \item Converts amounts and interest rates to numeric format. +#' \item Builds time variables (`year`, `mes`, `date`). +#' } +#' +#' @return A monthly-frequency `tibble` including: +#' \describe{ +#' \item{date}{Date object (first day of the month).} +#' \item{year}{Numeric year.} +#' \item{mes}{Numeric month (1–12).} +#' \item{monto_operaciones_interbancarias}{Total interbank transaction amount.} +#' \item{tasa_promedio_ponderado}{Weighted average interbank interest rate.} +#' \item{monto_depositos_vista}{Demand deposits amount.} +#' \item{tasa_depositos_vista}{Demand deposits interest rate.} +#' \item{monto_d_*}{Amounts by maturity bucket (days).} +#' \item{tasa_d_*}{Interest rates by maturity bucket (days).} +#' } +#' +#' @details +#' Requires the source file structure to remain unchanged (11 initial rows skipped +#' and fixed column order). If the format published by the Central Bank changes, +#' the function may fail. +#' +#' @source Central Bank of the Dominican Republic. +#' +#' @examples +#' \dontrun{ +#' rates <- get_tasa_interbancaria() +#' dplyr::glimpse(rates) +#' } +#' +#' @export +get_tasa_interbancaria <- function() { + url <- paste0( + "https://cdn.bancentral.gov.do/documents/", + "estadisticas/sector-monetario-y-financiero/", + "documents/Tasas_Interbancarias_Promedio_por_Plazos.xlsm" + ) + + file <- tempfile(fileext = ".xlsm") + + tryCatch( + download.file(url, file, mode = "wb", quiet = TRUE), + error = function(e) { + stop("No se pudo descargar el archivo de tasas interbancarias.") + } + ) + + headers_oi <- c( + "year_mes", + "monto_operaciones_interbancarias", + "tasa_promedio_ponderado", + "monto_depositos_vista", + "tasa_depositos_vista", + "monto_d_1_7", "tasa_d_1_7", + "monto_d_8_30", "tasa_d_8_30", + "monto_d_31_60", "tasa_d_31_60", + "monto_d_61_90", "tasa_d_61_90", + "monto_d_91_120", "tasa_d_91_120", + "monto_d_121_180", "tasa_d_121_180", + "monto_d_181_365", "tasa_d_181_365", + "monto_mas_365_dias", "tasa_mas_365_dias" + ) + + readxl::read_excel( + file, + skip = 11, + col_names = FALSE + ) |> + stats::setNames(headers_oi) |> + dplyr::filter(!is.na(year_mes)) |> + dplyr::mutate( + year = as.integer(stringr::str_extract(year_mes, "20\\d{2}")), + year_mes = stringr::str_remove(year_mes, "\\*") + ) |> + tidyr::fill(year) |> + dplyr::filter(!stringr::str_detect(year_mes, "Acumulad")) |> + dplyr::mutate( + dplyr::across( + -c(year_mes, year), + readr::parse_number + ) + ) |> + dplyr::filter(!is.na(monto_operaciones_interbancarias)) |> + dplyr::distinct(year, year_mes, .keep_all = TRUE) |> + dplyr::mutate( + mes = databcrd::crear_mes(year_mes), + date = lubridate::make_date(year, mes, 1) + ) |> + dplyr::relocate(date, year, mes) |> + suppressMessages() |> + suppressWarnings() +} diff --git a/inst/WORDLIST b/inst/WORDLIST index 321bd1f..ebec528 100644 --- a/inst/WORDLIST +++ b/inst/WORDLIST @@ -52,3 +52,8 @@ clase grupo subclase subgrupo +interbank +Interbank +xlsm +CDN + diff --git a/man/get_tasa_interbancaria.Rd b/man/get_tasa_interbancaria.Rd new file mode 100644 index 0000000..f5bdc9e --- /dev/null +++ b/man/get_tasa_interbancaria.Rd @@ -0,0 +1,49 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/operaciones_interbancarias.R +\name{get_tasa_interbancaria} +\alias{get_tasa_interbancaria} +\title{Get average interbank interest rates by maturity} +\source{ +Central Bank of the Dominican Republic. +} +\usage{ +get_tasa_interbancaria() +} +\value{ +A monthly-frequency \code{tibble} including: +\describe{ +\item{date}{Date object (first day of the month).} +\item{year}{Numeric year.} +\item{mes}{Numeric month (1–12).} +\item{monto_operaciones_interbancarias}{Total interbank transaction amount.} +\item{tasa_promedio_ponderado}{Weighted average interbank interest rate.} +\item{monto_depositos_vista}{Demand deposits amount.} +\item{tasa_depositos_vista}{Demand deposits interest rate.} +\item{monto_d_\emph{}{Amounts by maturity bucket (days).} +\item{tasa_d_}}{Interest rates by maturity bucket (days).} +} +} +\description{ +Downloads and processes the official Average Interbank Rates by Maturity +file published by the Central Bank of the Dominican Republic. +} +\details{ +The function: +\itemize{ +\item Downloads the .xlsm file from the institutional CDN. +\item Cleans headers and aggregated rows (cumulative values). +\item Converts amounts and interest rates to numeric format. +\item Builds time variables (\code{year}, \code{mes}, \code{date}). +} + +Requires the source file structure to remain unchanged (11 initial rows skipped +and fixed column order). If the format published by the Central Bank changes, +the function may fail. +} +\examples{ +\dontrun{ +rates <- get_tasa_interbancaria() +dplyr::glimpse(rates) +} + +} diff --git a/tests/testthat/test-operaciones_interbancarias.R b/tests/testthat/test-operaciones_interbancarias.R new file mode 100644 index 0000000..dec4d25 --- /dev/null +++ b/tests/testthat/test-operaciones_interbancarias.R @@ -0,0 +1,83 @@ +data <- get_tasa_interbancaria() |> + dplyr::mutate(fecha = date) + +test_that("There aren't dates in the future", { + testthat::expect_true(max(data$fecha) <= lubridate::today()) +}) + +test_that("There aren't haps between dates", { + test_nmonths <- data |> + dplyr::arrange(fecha) |> + dplyr::mutate(lag_fecha = dplyr::lag(fecha)) |> + dplyr::filter(!is.na(lag_fecha)) |> + dplyr::mutate(one_month_diff = fecha == lag_fecha + months(1)) + + testthat::expect_true(all(test_nmonths$one_month_diff)) +}) + +test_that("No missing columns", { + any_missing_columns <- data |> + sapply(function(x) all(is.na(x))) |> + all() + + testthat::expect_false(any_missing_columns) +}) + +test_that("No missing rows", { + empty_rows <- data |> + apply(MARGIN = 1, FUN = function(x) all(is.na(x))) |> + all() + + testthat::expect_false(empty_rows) +}) + +test_that("Dates are unique", { + testthat::expect_equal(nrow(data), dplyr::n_distinct(data$fecha)) +}) + +test_that("Dates are ordered after arrange", { + ordered_data <- data |> dplyr::arrange(fecha) + testthat::expect_true(all(diff(ordered_data$fecha) > 0)) +}) + +test_that("Year and month are consistent with date", { + testthat::expect_true( + all(lubridate::year(data$fecha) == data$year) + ) + testthat::expect_true( + all(lubridate::month(data$fecha) == data$mes) + ) +}) + +test_that("Montos are non-negative", { + monto_cols <- names(data)[stringr::str_detect(names(data), "^monto")] + + non_negative <- data |> + dplyr::select(dplyr::all_of(monto_cols)) |> + sapply(function(x) all(is.na(x) | x >= 0)) |> + all() + + testthat::expect_true(non_negative) +}) + +test_that("Tasas are within reasonable bounds", { + tasa_cols <- names(data)[stringr::str_detect(names(data), "^tasa")] + + reasonable_range <- data |> + dplyr::select(dplyr::all_of(tasa_cols)) |> + sapply(function(x) all(is.na(x) | (x >= 0 & x <= 100))) |> + all() + + testthat::expect_true(reasonable_range) +}) + +test_that("No duplicated year-month combinations", { + testthat::expect_equal( + nrow(data), + dplyr::n_distinct(data$year, data$mes) + ) +}) + +test_that("Dataset has at least 12 observations", { + testthat::expect_gte(nrow(data), 12) +})