Skip to content

Commit

Permalink
Fetch data from the Tiingo API with a getSymbols.tiingo() method.
Browse files Browse the repository at this point in the history
This enables package users to download historical OHLC, Adjusted OHLC,
Dividend, and Split information from the Tiingo API. Tiingo's free
offering allows access to 60K global securities for 30+ years. Tiingo
provides daily, weekly, monthly, and annual frequencies.

Details of the service can be found at the following links
https://www.tiingo.com/pricing
https://api.tiingo.com/docs/tiingo/daily

Tiingo also provides fundamentals and a 5 minute delay quote system,
though this implimentation includes no provision for downloading them.
  • Loading branch information
SteveBronder committed Apr 7, 2018
1 parent 5c98c96 commit f1865ce
Show file tree
Hide file tree
Showing 3 changed files with 220 additions and 0 deletions.
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ export(
getSymbols.yahoo,
getSymbols.yahooj,
getSymbols.oanda,
getSymbols.tiingo,
#getSymbols.Bloomberg,
#getSymbols.IBrokers,
getSymbols.csv,
Expand Down
127 changes: 127 additions & 0 deletions R/getSymbols.R
Original file line number Diff line number Diff line change
Expand Up @@ -1350,6 +1350,132 @@ getSymbols.av <- function(Symbols, env, api.key,
# Mnemonic alias, letting callers use getSymbols("IBM", src="alphavantage")
getSymbols.alphavantage <- getSymbols.av

#
# Download OHLC Data From Tiingo
#
# Meant to be called internally by getSymbols().
#
getSymbols.tiingo <- function(Symbols, env, api.key,
return.class="xts",
periodicity="daily",
adjusted = FALSE,
from='2007-01-01',
to=Sys.Date(),
data.type="json",
...) {

return.class = match.arg(return.class,
c("xts", "data.frame", "zoo",
"ts", "matrix", "timeSeries",
"quantmod.OHLC"))
periodicity = match.arg(periodicity,
c("daily", "weekly",
"monthly", "annually"))
adjusted = match.arg(as.character(adjusted),
c("TRUE", "FALSE", "both"))
# match.arg returns a char so we do conversion back to logical
if (adjusted == "TRUE" | adjusted == "FALSE") {
adjusted = as.logical(adjusted)
}
data.type = match.arg(data.type, c("json", "csv"))
importDefaults("getSymbols.tiingo")
this.env <- environment()
for (var in names(list(...))) {
assign(var, list(...)[[var]], this.env)
}

if (!hasArg("api.key")) {
stop("getSymbols.tiingo: An API key is required (api.key). Register",
" at https://api.tiingo.com.", call. = FALSE)
}
if (!hasArg("auto.assign")) auto.assign <- TRUE
if (!hasArg("verbose")) verbose <- FALSE
if (!hasArg("warnings")) warnings <- TRUE

valid.periodicity <- c("daily", "weekly", "monthly", "annually")
periodicity <- match.arg(periodicity, valid.periodicity)
default.return.class <- return.class
default.periodicity <- periodicity

if (!requireNamespace("jsonlite", quietly = TRUE)) {
stop("getSymbols.tiingo: Package", dQuote("jsonlite"), "is required but",
" cannot be loaded.", call. = FALSE)
}

tmp <- tempfile()
on.exit(file.remove(tmp))

downloadOne <- function(sym, default.return.class, default.periodicity) {

return.class <- getSymbolLookup()[[sym]]$return.class
return.class <- if (is.null(return.class)) default.return.class else return.class
periodicity <- getSymbolLookup()[[sym]]$periodicity
periodicity <- if (is.null(periodicity)) default.periodicity else periodicity
periodicity <- match.arg(periodicity, valid.periodicity)
sym.name <- getSymbolLookup()[[sym]]$name
sym.name <- if (is.null(sym.name)) sym else sym.name

if (verbose) cat("loading", sym.name, ".....")
from.strftime <- strftime(from, format = "%Y-%m-%d")
to.strftime <- strftime(to, format = "%Y-%m-%d")
if (adjusted == FALSE) {
return_columns = c("open", "high", "low", "close", "volume",
"divCash", "splitFactor")
} else if (adjusted == TRUE) {
return_columns = c("adjClose", "adjHigh", "adjLow", "adjOpen",
"adjVolume", "divCash", "splitFactor")
} else {
return_columns = c("open", "high", "low", "close", "volume",
"adjClose", "adjHigh", "adjLow", "adjOpen",
"adjVolume", "divCash", "splitFactor")
}
URL <- paste0("https://api.tiingo.com/tiingo/",
default.periodicity, "/",
sym.name, "/prices/",
"?startDate=", from.strftime,
"&endDate=", to.strftime,
"&format=", data.type,
"&token=", api.key,
"&columns=", paste0(return_columns, collapse = ","))
download.file(url = URL, destfile = tmp, quiet = !verbose)
tiingo.names <- c("open", "high", "low", "close", "volume",
"adjClose", "adjHigh", "adjLow", "adjOpen",
"adjVolume", "divCash", "splitFactor")
qm.names <- paste(sym, c("Open", "High", "Low", "Close", "Volume",
"AdjustedClose", "AdjustedHigh", "AdjustedLow",
"AdjustedOpen", "AdjustedVolume", "DivCash",
"SplitFactor"), sep = ".")
data.names <- data.frame(tiingo.names = tiingo.names, qm.names = qm.names,
stringsAsFactors = FALSE)

if (data.type == "json") {
stock.data <- jsonlite::fromJSON(tmp)
if (verbose) cat("done.\n")
} else {
stock.data <- as.data.frame(read.csv(tmp, header = TRUE))
}
tm.stamps = as.POSIXct(stock.data[, "date"], ...)
stock.data[, "date"] = NULL
colnames(stock.data) <- qm.names[match(colnames(stock.data), tiingo.names)]
# convert data to xts
xts.data <- xts(stock.data, tm.stamps, src = "tiingo", updated = Sys.time())
xts.data <- convert.time.series(xts.data, return.class = return.class)
if (auto.assign)
assign(sym, xts.data, env)
return(xts.data)
}

matrices <- lapply(Symbols, FUN=downloadOne,
default.return.class=default.return.class,
default.periodicity=default.periodicity)

if (auto.assign) {
return(Symbols)
} else {
return(matrices[[1]])
}
}

# convert.time.series {{{
`convert.time.series` <- function(fr,return.class) {
if('quantmod.OHLC' %in% return.class) {
Expand Down Expand Up @@ -1448,3 +1574,4 @@ function(Symbols=NULL,file.path=stop("must specify 'file.path'"),env=parent.fram
fr <- convert.time.series(fr=fr,return.class=return.class)
}
#}}}

92 changes: 92 additions & 0 deletions man/getSymbols.tiingo.Rd
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
\name{getSymbols.tiingo}
\alias{getSymbols.tiingo}
\title{ Download OHLC Data from Tiingo }
\description{
Downloads historical or realtime equity price data
from \url{https://api.tiingo.com/}.
Registration is required.
}
\usage{
getSymbols.tiingo(Symbols, env, api.key,
return.class="xts",
periodicity="daily",
adjusted = FALSE,
from='2007-01-01',
to=Sys.Date(),
data.type="json",
...)
}
\arguments{
\item{Symbols}{ a character vector specifying the names
of the symbols to be loaded}
\item{env}{ where to create objects (environment) }
\item{api.key}{ the API key issued by Tiingo when you registered (character)}
\item{return.class}{ class of returned object, see Value (character) }
\item{periodicity}{ one of \code{"daily"}, \code{"weekly"}, \code{"monthly"}, or \code{"Annually"} }
\item{adjusted}{if \code{TRUE}, returns OHLC adjusted for dividends and splits, a value of \code{"both"}
returns both unadjusted and adjusted}
\item{from}{ Retrieve data no earlier than this date. (2007-01-01)}
\item{to}{ Retrieve data through this date (Sys.Date())}
\item{data.type}{ either \code{"json"} or \code{"csv"} }
\item{\dots}{ additional parameters as per \code{\link{getSymbols}} }
}
\details{
Meant to be called internally by \code{getSymbols} only.
This method is not meant to be called directly, instead
a call to \code{getSymbols("x", src="tiingo")} will
in turn call this method. It is documented for the
sole purpose of highlighting the arguments accepted.

You must register with Tiingo in order to download their data.
Register at their web site, \url{https://api.tiingo.com},
and you will receive an \emph{API key}:
a short string of alphanumeric characters (e.g., "FU4U").
Provide the API key every time you call \code{getSymbols};
or set it globally using \code{setDefaults(getSymbols.tiingo, api.key="yourKey")}.

Tiingo provides daily, weekly, monthly, and annual data.
Use \code{periodicity} to select one.
This API accessor will return adjusted or unadjusted OHLC as well as split and dividend information.

For daily, weekly, and monthly data, Tiingo says the available data is up to 30 years;

Tiingo provides access to data via two APIs. You can choose the API via
the \code{data.type} argument. \code{data.type="json"}, the default, will
import data using the JSON API. This API includes additional metadata (e.g.
last updated time, timezone, etc) that is not provided via the CSV API.
}
\value{
A call to \code{getSymbols(Symbols, src="tiingo")} will create objects
in the specified environment,
one object for each \code{Symbol} specified.
The object class of the object(s) is determined by \code{return.class}.
Presently this may be \code{"ts"}, \code{"zoo"}, \code{"xts"}, or \code{"timeSeries"}.
}
% \note{
% [TBD]
% }
\references{ Tiingo documentation available at \url{https://www.tiingo.com} }
\author{ Steve Bronder }
\seealso{
\code{\link{getSymbols}},
\code{\link{getSymbols.yahoo}},
\code{\link{getSymbols.google}}
}
\examples{
\dontrun{
# You'll need the API key given when you registered
getSymbols("IBM", src="tiingo", api.key="yourKey")

# Return adjusted OHLCV for IBM
getSymbols("IBM", src="tiingo", api.key="yourKey", adjusted = TRUE)


getSymbols("IBM", src="tiingo", api.key="yourKey", from="1992-01-10",
periodicity="weekly")

# Repeating your API key every time is tedious.
# Fortunately, you can set a global default.
setDefaults(getSymbols.tiingo, api.key="yourKey")
getSymbols("IBM", src = "tiingo", adjusted = "both")
}
}

0 comments on commit f1865ce

Please sign in to comment.