Nothing
#' Fetch Bank of Japan (BoJ) data
#'
#' Retrieve time series data from the Bank of Japan Statistics API.
#'
#' @param db (`character(1)`)\cr
#' The database code to query (e.g., `"FM08"` for foreign exchange rates). See the
#' [API manual](https://www.stat-search.boj.or.jp/info/api_manual_en.pdf) for available databases.
#' @param code (`character()`)\cr
#' One or more series codes to query (e.g., `"FXERD01"` for USD/JPY spot rate). Maximum 250
#' codes per request. All codes must have the same frequency. Use [boj_metadata()] to find
#' available codes.
#' @param start_date (`NULL` | `character(1)` | `integer(1)`)\cr
#' Start date of the data. Format depends on frequency: `"YYYYMM"` for daily, weekly, and
#' monthly data, `"YYYYQQ"` for quarterly (where QQ is 01-04), and `"YYYY"` for annual.
#' If `NULL`, all available data is returned. Default `NULL`.
#' @param end_date (`NULL` | `character(1)` | `integer(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`.
#' @param lang (`character(1)`)\cr
#' Language for series names, either `"en"` or `"jp"`. Default `"en"`.
#' @returns A [data.table::data.table()] with the requested data.
#' @source <https://www.stat-search.boj.or.jp/index_en.html>
#' @family data
#' @export
#' @examplesIf curl::has_internet()
#' \donttest{
#' # fetch USD/JPY exchange rate
#' boj_data("FM08", "FXERD01", start_date = "202401")
#'
#' # fetch multiple exchange rates
#' boj_data("FM08", c("FXERD01", "FXERD02"), start_date = "202401")
#' }
boj_data = function(db, code, start_date = NULL, end_date = NULL, lang = "en") {
assert_string(db, min.chars = 1L)
assert_character(code, min.chars = 1L, min.len = 1L, max.len = 250L)
assert_boj_date(start_date)
assert_boj_date(end_date)
assert_choice(lang, c("en", "jp"))
req = boj_request(
"getDataCode",
db = toupper(db),
code = code,
startDate = start_date,
endDate = end_date,
lang = lang
)
resps = req_perform_iterative(
req,
next_req = iterate_with_cursor("startPosition", boj_next_position),
max_reqs = Inf
)
series = resps_data(resps, \(resp) resp_body_json(resp)$RESULTSET)
parse_boj_data(list(RESULTSET = series))
}
boj_next_position = function(resp) {
pos = resp_body_json(resp)$NEXTPOSITION
if (length(pos) == 0L) NULL else pos
}
#' Fetch Bank of Japan (BoJ) metadata
#'
#' Retrieve series metadata from the Bank of Japan Statistics API.
#'
#' @param db (`character(1)`)\cr
#' The database code to query (e.g., `"FM08"` for foreign exchange rates).
#' @param lang (`character(1)`)\cr
#' Language for names, either `"en"` or `"jp"`. Default `"en"`.
#' @returns A [data.table::data.table()] with the requested metadata.
#' @source <https://www.stat-search.boj.or.jp/index_en.html>
#' @family metadata
#' @export
#' @examplesIf curl::has_internet()
#' \donttest{
#' boj_metadata("FM08")
#' }
boj_metadata = function(db, lang = "en") {
assert_string(db, min.chars = 1L)
assert_choice(lang, c("en", "jp"))
json = boj("getMetadata", db = toupper(db), lang = lang)
parse_boj_metadata(json)
}
parse_boj_data = function(json) {
series = json$RESULTSET
if (length(series) == 0L) {
dt = data.table(
date = as.Date(character()),
id = character(),
value = numeric(),
freq = character(),
name = character(),
unit = character()
)
setnames(dt, "id", "key")
return(dt)
}
dt = rbindlist(map(series, function(x) {
dates = unlist(x$VALUES$SURVEY_DATES)
values = as.numeric(map(x$VALUES$VALUES, \(x) x %||% NA_real_))
freq = tolower(x$FREQUENCY)
dt = data.table(
date = parse_boj_date(dates, freq),
id = x$SERIES_CODE,
value = values,
freq = boj_freq(freq),
name = x$NAME_OF_TIME_SERIES %||% x$NAME_OF_TIME_SERIES_J %||% NA_character_,
unit = x$UNIT %||% x$UNIT_J %||% NA_character_
)
na.omit(dt, cols = "value")
}))
setnames(dt, "id", "key")
dt[]
}
parse_boj_metadata = function(json) {
series = json$RESULTSET
if (length(series) == 0L) {
return(data.table(
code = character(),
name = character(),
unit = character(),
frequency = character(),
category = character()
))
}
dt = rbindlist(map(series, function(x) {
code = x$SERIES_CODE
if (is.null(code) || !nzchar(code)) {
return()
}
data.table(
code = code,
name = x$NAME_OF_TIME_SERIES %||% x$NAME_OF_TIME_SERIES_J %||% NA_character_,
unit = x$UNIT %||% x$UNIT_J %||% NA_character_,
frequency = x$FREQUENCY %||% NA_character_,
category = x$CATEGORY %||% x$CATEGORY_J %||% NA_character_
)
}))
dt[]
}
parse_boj_date = function(dates, freq) {
switch(
freq,
daily = ,
`weekly(monday)` = ,
`weekly(tuesday)` = ,
`weekly(wednesday)` = ,
`weekly(thursday)` = ,
`weekly(friday)` = ,
`weekly(saturday)` = ,
`weekly(sunday)` = {
as.Date(as.character(dates), format = "%Y%m%d")
},
monthly = as.Date(paste0(dates, "01"), format = "%Y%m%d"),
quarterly = {
dates_chr = as.character(dates)
year = substr(dates_chr, 1L, 4L)
quarter = as.integer(substr(dates_chr, 5L, 6L))
month = sprintf("%02d", (quarter - 1L) * 3L + 1L)
as.Date(paste0(year, month, "01"), format = "%Y%m%d")
},
semiannual = {
dates_chr = as.character(dates)
year = substr(dates_chr, 1L, 4L)
half = as.integer(substr(dates_chr, 5L, 6L))
month = sprintf("%02d", (half - 1L) * 6L + 1L)
as.Date(paste0(year, month, "01"), format = "%Y%m%d")
},
# the fiscal half-year starts in April: 01 = April-September, 02 = October-March
`semiannual(sep)` = {
dates_chr = as.character(dates)
year = substr(dates_chr, 1L, 4L)
half = as.integer(substr(dates_chr, 5L, 6L))
month = sprintf("%02d", (half - 1L) * 6L + 4L)
as.Date(paste0(year, month, "01"), format = "%Y%m%d")
},
annual = as.Date(sprintf("%s-01-01", dates), format = "%Y-%m-%d"),
# the fiscal year starts in April
`annual(mar)` = as.Date(sprintf("%s-04-01", dates), format = "%Y-%m-%d"),
as.character(dates)
)
}
boj_freq = function(freq) {
switch(
freq,
daily = "daily",
monthly = "monthly",
quarterly = "quarterly",
semiannual = ,
`semiannual(sep)` = "semi-annual",
annual = ,
`annual(mar)` = "annual",
`weekly(monday)` = ,
`weekly(tuesday)` = ,
`weekly(wednesday)` = ,
`weekly(thursday)` = ,
`weekly(friday)` = ,
`weekly(saturday)` = ,
`weekly(sunday)` = "weekly",
freq
)
}
assert_boj_date = function(x, .var.name = vname(x)) {
assert(
check_null(x),
check_string(x, min.chars = 4L, pattern = "^\\d+$"),
check_count(x, positive = TRUE),
.var.name = .var.name
)
invisible(x)
}
boj_error_body = function(resp) {
json = resp_body_json(resp)
json$MESSAGE
}
boj_request = function(resource, ...) {
base_request("https://www.stat-search.boj.or.jp/api/v1") |>
req_url_path_append(resource) |>
req_url_query(..., .multi = "comma") |>
req_error(body = boj_error_body)
}
boj = function(resource, ...) {
boj_request(resource, ...) |>
req_perform() |>
resp_body_json()
}
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.