Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@ export(
getSymbols.SQLite,
getSymbols.mysql,
getSymbols.FRED,
getSymbols.FXMacroData,
getSymbols.yahoo,
getSymbols.yahooj,
getSymbols.oanda,
Expand Down
85 changes: 85 additions & 0 deletions R/getSymbols.R
Original file line number Diff line number Diff line change
Expand Up @@ -836,6 +836,91 @@ function(Symbols,env,return.class='xts',
return(fr)
} #}}}

# getSymbols.FXMacroData {{{
`getSymbols.FXMacroData` <- function(Symbols, env,
return.class="xts", currency="USD", value.field="val",
api.key=Sys.getenv("FXMACRODATA_API_KEY"), limit=1000, ...) {
importDefaults("getSymbols.FXMacroData")
this.env <- environment()
for(var in names(list(...))) {
assign(var, list(...)[[var]], this.env)
}
if(!hasArg("verbose")) verbose <- FALSE
if(!hasArg("auto.assign")) auto.assign <- TRUE
if(!hasArg("warnings")) warnings <- TRUE
if(!hasArg("from")) from <- ""
if(!hasArg("to")) to <- ""

API.URL <- "https://fxmacrodata.com/api/v1/announcements"
returnSym <- Symbols
noDataSym <- NULL

parse_symbol <- function(Symbol) {
parts <- unlist(strsplit(Symbol, "[./:]"))
if(length(parts) >= 2L) {
list(currency=tolower(parts[[1L]]), indicator=tolower(parts[[2L]]))
} else {
list(currency=tolower(currency), indicator=tolower(Symbol))
}
}

fetch_json <- function(URL) {
res <- curl::curl_fetch_memory(URL)
txt <- rawToChar(res$content)
if(res$status_code != 200L) {
msg <- tryCatch(jsonlite::fromJSON(txt)$detail, error=function(e) NULL)
stop(if(is.null(msg)) paste0("FXMacroData returned HTTP ", res$status_code) else msg,
call.=FALSE)
}
jsonlite::fromJSON(txt)
}

for(i in seq_along(Symbols)) {
if(verbose) cat("downloading ", Symbols[[i]], " from FXMacroData.....\n\n")
test <- try({
parsed <- parse_symbol(Symbols[[i]])
params <- paste0("?limit=", as.integer(limit))
if(nzchar(api.key)) params <- paste0(params, "&api_key=", URLencode(api.key, reserved=TRUE))
if(nzchar(as.character(from))) params <- paste0(params, "&start_date=", URLencode(as.character(from), reserved=TRUE))
if(nzchar(as.character(to))) params <- paste0(params, "&end_date=", URLencode(as.character(to), reserved=TRUE))
URL <- paste(API.URL, parsed$currency, parsed$indicator, sep="/")
fxmd <- fetch_json(paste0(URL, params))
rows <- fxmd$data
if(is.null(rows) || NROW(rows) < 1L)
stop("FXMacroData returned no rows", call.=FALSE)
rows <- utils::head(rows, as.integer(limit))
if(!value.field %in% colnames(rows))
stop(paste0("FXMacroData response does not contain value field ", dQuote(value.field)),
call.=FALSE)
date.field <- if("date" %in% colnames(rows)) "date" else "announcement_datetime_local"
fr <- xts(as.numeric(rows[[value.field]]), as.Date(rows[[date.field]]),
src="FXMacroData", updated=Sys.time())
fr <- fr[order(index(fr))]
colnames(fr) <- as.character(toupper(Symbols[[i]]))
fr <- fr[paste(from, to, sep="/")]
fr <- convert.time.series(fr=fr, return.class=return.class)
Symbols[[i]] <- toupper(gsub("[^[:alnum:]_]", "_", Symbols[[i]]))
if(auto.assign)
assign(Symbols[[i]], fr, env)
if(verbose) cat("done.\n")
}, silent=TRUE)
if(inherits(test, "try-error")) {
msg <- paste0("Unable to import ", dQuote(returnSym[[i]]),
" from FXMacroData.\n", attr(test, "condition")$message)
if(hasArg(".has1sym.") && match.call(expand.dots=TRUE)$.has1sym.) {
stop(msg)
}
if(isTRUE(warnings)) {
warning(msg, call.=FALSE, immediate.=TRUE)
}
noDataSym <- c(noDataSym, returnSym[[i]])
}
}
if(auto.assign)
return(setdiff(returnSym, noDataSym))
return(fr)
} #}}}

"getSymbols.cache" <- function() {}

# getFX {{{
Expand Down
50 changes: 50 additions & 0 deletions man/getSymbols.FXMacroData.Rd
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
\name{getSymbols.FXMacroData}
\alias{getSymbols.FXMacroData}
\title{Download Macroeconomic Data from FXMacroData}
\description{
Download macroeconomic announcement time series from FXMacroData into an
\code{xts} object.
}
\usage{
getSymbols.FXMacroData(Symbols, env, return.class = "xts",
currency = "USD", value.field = "val",
api.key = Sys.getenv("FXMACRODATA_API_KEY"), limit = 1000, ...)
}
\arguments{
\item{Symbols}{character vector of FXMacroData series. Use
\code{"USD.INFLATION"}, \code{"USD.UNEMPLOYMENT"}, or pass an indicator such
as \code{"inflation"} with \code{currency = "USD"}.}
\item{env}{where to create objects when \code{auto.assign = TRUE}.}
\item{return.class}{class of returned object, passed to quantmod conversion.}
\item{currency}{default currency used when a symbol omits the currency
prefix.}
\item{value.field}{response field to use as the numeric observation, usually
\code{"val"} or \code{"val_mom"}.}
\item{api.key}{optional FXMacroData API key. Defaults to the
\code{FXMACRODATA_API_KEY} environment variable.}
\item{limit}{maximum rows requested from FXMacroData.}
\item{\dots}{additional arguments, including \code{from}, \code{to},
\code{auto.assign}, \code{verbose}, and \code{warnings}.}
}
\details{
The source calls
\url{https://fxmacrodata.com/api/v1/announcements/\{currency\}/\{indicator\}}
and converts the \code{data} array to a dated \code{xts} series. Authentication
is optional for public USD macro series; when provided, the API key is passed as
the \code{api_key} query parameter.
}
\value{
If \code{auto.assign = TRUE}, the loaded symbol names are returned invisibly and
objects are created in \code{env}. Otherwise, an \code{xts} object is returned.
}
\examples{
\dontrun{
getSymbols("USD.INFLATION", src = "FXMacroData")
getSymbols("unemployment", src = "FXMacroData", currency = "USD",
auto.assign = FALSE)
}
}
\seealso{
\code{\link{getSymbols}}, \code{\link{setSymbolLookup}},
\code{\link{getSymbols.FRED}}
}