Skip to content

Commit

Permalink
Merge pull request #53 from matthiasgomolka/api_v3
Browse files Browse the repository at this point in the history
API v3
  • Loading branch information
matthiasgomolka authored Feb 27, 2024
2 parents 39c0de7 + 8b3c49e commit 30f09f0
Show file tree
Hide file tree
Showing 67 changed files with 1,134 additions and 2,468 deletions.
8 changes: 3 additions & 5 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Type: Package
Package: simfinapi
Title: Accessing 'SimFin' Data
Version: 0.2.4
Version: 1.0.0
Authors@R:
person("Matthias", "Gomolka", , "matthias.gomolka@posteo.de", role = c("aut", "cre"))
Description: Through simfinapi, you can intuitively access the 'SimFin'
Expand All @@ -14,13 +14,11 @@ BugReports: https://github.com/matthiasgomolka/simfinapi/issues
Depends:
R (>= 3.5)
Imports:
bit64,
checkmate (>= 2.0.0),
data.table (>= 1.12.8),
future.apply (>= 1.4.0),
httr,
httr2,
lifecycle,
memoise (>= 1.1.0),
progressr,
RcppSimdJson (>= 0.1.1),
utils
Suggests:
Expand Down
35 changes: 16 additions & 19 deletions NAMESPACE
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
# Generated by roxygen2: do not edit by hand

export(sfa_get_entities)
export(sfa_get_info)
export(sfa_get_prices)
export(sfa_get_ref)
export(sfa_get_shares)
export(sfa_get_statement)
export(sfa_load_common_shares_outstanding)
export(sfa_load_companies)
export(sfa_load_shareprices)
export(sfa_load_statements)
export(sfa_load_weighted_shares_outstanding)
export(sfa_set_api_key)
export(sfa_set_cache_dir)
export(sfa_set_sfplus)
importFrom(RcppSimdJson,fparse)
importFrom(bit64,as.integer64)
importFrom(checkmate,assert_character)
importFrom(checkmate,assert_choice)
importFrom(checkmate,assert_date)
Expand All @@ -20,24 +17,24 @@ importFrom(checkmate,assert_logical)
importFrom(checkmate,assert_string)
importFrom(data.table,CJ)
importFrom(data.table,as.data.table)
importFrom(data.table,fread)
importFrom(data.table,data.table)
importFrom(data.table,is.data.table)
importFrom(data.table,rbindlist)
importFrom(data.table,set)
importFrom(data.table,setattr)
importFrom(data.table,setcolorder)
importFrom(data.table,setkeyv)
importFrom(data.table,setnames)
importFrom(data.table,transpose)
importFrom(data.table,year)
importFrom(future.apply,future_lapply)
importFrom(future.apply,future_mapply)
importFrom(httr,GET)
importFrom(httr,content)
importFrom(httr2,last_response)
importFrom(httr2,req_headers)
importFrom(httr2,req_perform)
importFrom(httr2,req_url_path_append)
importFrom(httr2,req_url_query)
importFrom(httr2,req_user_agent)
importFrom(httr2,request)
importFrom(httr2,resp_body_string)
importFrom(httr2,resp_is_error)
importFrom(lifecycle,deprecated)
importFrom(memoise,cache_filesystem)
importFrom(memoise,memoise)
importFrom(progressr,progressor)
importFrom(progressr,with_progress)
importFrom(utils,download.file)
importFrom(utils,hasName)
importFrom(utils,unzip)
4 changes: 4 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# simfinapi 1.0.0
## Features
* The package is now compatible with the new SimFin API V3.

# simfinapi 0.2.4
## Bug Fixes
* Some new API keys were not accepted by `check_api_key()`. This is fixed now.
Expand Down
61 changes: 24 additions & 37 deletions R/call_api.R
Original file line number Diff line number Diff line change
@@ -1,47 +1,34 @@
#' @importFrom httr content
#' @importFrom RcppSimdJson fparse
#' @importFrom utils hasName
#' @importFrom memoise memoise cache_filesystem
#' @importFrom httr GET
call_api <- function(..., cache_dir) {
# check for cache setup
#' @importFrom httr2 request req_url_path_append req_headers req_user_agent req_url_query
#' req_perform last_response resp_is_error resp_body_string
#' @noRd
call_api <- function(url, api_key, cache_dir, ...) {
# check for cache setup

if (is.null(cache_dir)) {
warning(
"'cache_dir' not set. Defaulting to 'tempdir()'. Thus, API results will ",
"only be cached during this session. To learn why and how to cache ",
"results over the end of this session, see `?sfa_set_cache_dir`.\n\n",
"[This warning appears only once per session.]",
call. = FALSE
)
sfa_set_cache_dir(tempdir(), create = TRUE)
cache_dir <- getOption("sfa_cache_dir")
}
if (is.null(cache_dir)) {
warning("'cache_dir' not set. Defaulting to 'tempdir()'. Thus, API results will ", "only be cached during this session. To learn why and how to cache ",
"results over the end of this session, see `?sfa_set_cache_dir`.\n\n", "[This warning appears only once per session.]",
call. = FALSE)
sfa_set_cache_dir(tempdir(), create = TRUE)
cache_dir <- getOption("sfa_cache_dir")
}

checkmate::assert_directory(cache_dir, access = "rw")
checkmate::assert_directory(cache_dir, access = "rw")

mem_GET <- memoise::memoise(
httr::GET,
cache = memoise::cache_filesystem(cache_dir)
)
req <- httr2::request("https://prod.simfin.com/api/v3") |>
httr2::req_url_path_append(url) |>
httr2::req_headers(Authorization = api_key, accept = "application/json") |>
httr2::req_user_agent("simfinapi (https://github.com/matthiasgomolka/simfinapi)") |>
httr2::req_url_query(...)

# call API and transform result to list
response <- mem_GET(
url = "https://legacy.simfin.com",
...
)
request <- response[["request"]][["url"]]
content <- RcppSimdJson::fparse(
response[["content"]],
max_simplify_lvl = "vector",
int64_policy = "integer64"
)
mem_req_perform <- memoise::memoise(httr2::req_perform, cache = memoise::cache_filesystem(cache_dir))

if (utils::hasName(content, "error")) {
warning("From 'SimFin' API: '", content[["error"]], "'", call. = FALSE)
return(NULL)
}
resp <- tryCatch(mem_req_perform(req), error = \(error) httr2::last_response())

if (httr2::resp_is_error(resp)) {
handle_api_error(resp)
}

return(list(request = request, content = content))
return(resp)
}
46 changes: 33 additions & 13 deletions R/check_inputs.R
Original file line number Diff line number Diff line change
@@ -1,25 +1,30 @@
#' @noRd
msg_sfplus_required <- function(var, verb = "Omitting") {
stop(verb, " '", var, "' is reserved for SimFin+ users.", call. = FALSE)
}

#' @importFrom checkmate assert_string
#' @noRd
check_api_key <- function(api_key) {
checkmate::assert_string(api_key, pattern = "^[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*$")
checkmate::assert_string(api_key)#, pattern = "^[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*$")
}

#' @importFrom checkmate assert_directory
#' @noRd
check_cache_dir <- function(cache_dir) {
if (!is.null(cache_dir)) {
checkmate::assert_directory(cache_dir, access = "rw")
}
}

#' @importFrom checkmate assert_logical
#' @noRd
check_sfplus <- function(sfplus) {
checkmate::assert_logical(sfplus, any.missing = FALSE, len = 1L)
}

#' @importFrom checkmate assert_character
#' @noRd
check_ticker <- function(ticker) {
checkmate::assert_character(
ticker,
Expand All @@ -30,28 +35,32 @@ check_ticker <- function(ticker) {
}

#' @importFrom checkmate assert_integerish
check_simfin_id <- function(simfin_id) {
#' @noRd
check_id <- function(id) {
checkmate::assert_integerish(
simfin_id,
id,
lower = 1L,
any.missing = FALSE,
null.ok = TRUE
)
}

#' @importFrom checkmate assert_choice
#' @noRd
check_statement <- function(statement, sfplus) {
checkmate::assert_choice(
checkmate::assert_subset(
statement,
c("pl", "bs", "cf", "derived", "all"),
c("pl", "bs", "cf", "derived"),
empty.ok = FALSE,
fmatch = TRUE
)
if (statement == "all" & isFALSE(sfplus)) {
stop('statement = "all" is reserved for SimFin+ users.', call. = FALSE)
}
# if (statement == "all" & isFALSE(sfplus)) {
# stop('statement = "all" is reserved for SimFin+ users.', call. = FALSE)
# }
}

#' @importFrom checkmate assert_choice
#' @noRd
check_period <- function(period, sfplus, called_from_get_shares = FALSE) {
checkmate::assert_choice(
period,
Expand All @@ -71,14 +80,16 @@ check_period <- function(period, sfplus, called_from_get_shares = FALSE) {
}
}

#' @noRd
check_period_get_shares <- function(...) {
check_period(...)
}


#' @importFrom checkmate assert_integerish
#' @noRd
check_fyear <- function(fyear, sfplus) {
if (is.null(fyear) & isFALSE(sfplus)) {
if (is.null(fyear) && isFALSE(sfplus)) {
msg_sfplus_required("fyear")
}
checkmate::assert_integerish(
Expand All @@ -88,15 +99,18 @@ check_fyear <- function(fyear, sfplus) {
null.ok = TRUE
)
}

#' @noRd
check_fyear_get_shares <- function(..., type) {
if (type %in% c("wa-basic", "wa-diluted")) {
check_fyear(...)
}
}

#' @importFrom checkmate assert_date
#' @noRd
check_start <- function(start, sfplus) {
if (!is.null(start) & isFALSE(sfplus)) {
if (!is.null(start) && isFALSE(sfplus)) {
msg_sfplus_required("start", "Specifying")
}
checkmate::assert_date(
Expand All @@ -108,8 +122,9 @@ check_start <- function(start, sfplus) {
}

#' @importFrom checkmate assert_date
#' @noRd
check_end <- function(end, sfplus) {
if (!is.null(end) & isFALSE(sfplus)) {
if (!is.null(end) && isFALSE(sfplus)) {
msg_sfplus_required("end", "Specifying")
}
checkmate::assert_date(
Expand All @@ -121,15 +136,17 @@ check_end <- function(end, sfplus) {
}

#' @importFrom checkmate assert_logical
#' @noRd
check_ttm <- function(ttm) {
checkmate::assert_logical(ttm, any.missing = FALSE, len = 1L)
}

#' @importFrom checkmate assert_logical
#' @noRd
check_shares <- function(shares, sfplus) {
checkmate::assert_logical(shares, any.missing = FALSE, len = 1L)

if (isTRUE(shares) & isFALSE(sfplus)) {
if (isTRUE(shares) && isFALSE(sfplus)) {
stop(
"'shares = TRUE' is reserved to SimFin+ users. As a normal user, please ",
"use 'sfa_get_shares()' with 'type = \"wa-basic\"' or 'type = ",
Expand All @@ -140,8 +157,9 @@ check_shares <- function(shares, sfplus) {
}

#' @importFrom checkmate assert_logical
#' @noRd
check_ratios <- function(ratios, sfplus) {
if (!is.null(ratios) & isFALSE(sfplus)) {
if (!is.null(ratios) && isFALSE(sfplus)) {
msg_sfplus_required("ratios", "Specifying")
}
checkmate::assert_logical(
Expand All @@ -153,6 +171,7 @@ check_ratios <- function(ratios, sfplus) {
}

#' @importFrom checkmate assert_choice
#' @noRd
check_type <- function(type) {
checkmate::assert_choice(
type,
Expand All @@ -162,6 +181,7 @@ check_type <- function(type) {
}

#' @importFrom checkmate assert_choice
#' @noRd
check_ref_data <- function(ref_data) {
checkmate::assert_choice(
ref_data,
Expand Down
33 changes: 17 additions & 16 deletions R/param_doc.R
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,16 @@
#' @param cache_dir [character] Your cache directory. It's recommended to set
#' the cache directory globally using [sfa_set_cache_dir].
#'
#' @param sfplus [logical] Set`TRUE` if you have a SimFin+ account. It's
#' recommended to set `sfplus` globally using [sfa_set_sfplus].
#'
#' @param ticker [integer] Ticker of the companies of interest.
#'
#' @param simfin_id [integer] 'SimFin' IDs of the companies of interest. Any
#' `simfin_id` will be internally translated to the respective `ticker`. This
#' @param id [integer] 'SimFin' IDs of the companies of interest. Any
#' `id` will be internally translated to the respective `ticker`. This
#' reduces the number of queries in case you query the same company via
#' `ticker` *and* `simfin_id`.
#' `ticker` *and* `id`.
#'
#' @param asreported [logical] If `TRUE`, retrieves the as-reported (not restated) data.
#'
#' @param ttm [logical] If `TRUE`, retrieves trailing twelve month periods.
#'
#' @param start [Date] Filter for the report dates (reserved for SimFin+ users).
#' With this filter you can filter the statements by the date on which the
Expand All @@ -33,17 +34,17 @@
#' to provide exactly one period. As SimFin+ user, this filter can be omitted
#' to retrieve all statements available for the company.
#'
#' - `"q1"`: First fiscal quarter.
#' - `"q2"`: Second fiscal quarter.
#' - `"q3"`: Third fiscal quarter.
#' - `"q4"`: Fourth fiscal quarter.
#' - `"fy"`: Full fiscal year.
#' - `"h1"`: First 6 months of fiscal year.
#' - `"h2"`: Last 6 months of fiscal year.
#' - `"9m"`: First nine months of fiscal year.
#' - `"6m"`: Any fiscal 6 month period (first + second half years; reserved
#' - `'q1'`: First fiscal quarter.
#' - `'q2'`: Second fiscal quarter.
#' - `'q3'`: Third fiscal quarter.
#' - `'q4'`: Fourth fiscal quarter.
#' - `'fy'`: Full fiscal year.
#' - `'h1'`: First 6 months of fiscal year.
#' - `'h2'`: Last 6 months of fiscal year.
#' - `'9m'`: First nine months of fiscal year.
#' - `'6m'`: Any fiscal 6 month period (first + second half years; reserved
#' for SimFin+ users).
#' - `"quarters"`: All quarters (q1 + q2 + q3 + q4; reserved for SimFin+
#' - `'quarters'`: All quarters (q1 + q2 + q3 + q4; reserved for SimFin+
#' users).
#'
#' @param fyear [integer] Filter for fiscal year. As a non-SimFin+ user, you
Expand Down
Loading

0 comments on commit 30f09f0

Please sign in to comment.