diff --git a/.github/workflows/reverse-dependency-check.yaml b/.github/workflows/reverse-dependency-check.yaml new file mode 100644 index 0000000..8d1be44 --- /dev/null +++ b/.github/workflows/reverse-dependency-check.yaml @@ -0,0 +1,208 @@ +name: Reverse dependency checks + +on: + workflow_dispatch: + push: + pull_request: + +permissions: + contents: read + +concurrency: + group: reverse-deps-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + reverse-dependencies: + name: blotter and quantstrat + runs-on: ubuntu-latest + + env: + _R_CHECK_FORCE_SUGGESTS_: false + + steps: + - name: Check out FinancialInstrument + uses: actions/checkout@v4 + + - name: Set up R + uses: r-lib/actions/setup-r@v2 + with: + r-version: release + use-public-rspm: true + + - name: Install FinancialInstrument dependencies + uses: r-lib/actions/setup-r-dependencies@v2 + with: + extra-packages: | + any::rcmdcheck + any::tinytest + any::testthat + any::PerformanceAnalytics + any::foreach + any::iterators + + - name: Install current FinancialInstrument + run: R CMD INSTALL . + + - name: Clone downstream packages + run: | + mkdir -p revdeps + + git clone --depth 1 \ + https://github.com/braverock/blotter.git \ + revdeps/blotter + + git clone --depth 1 \ + https://github.com/braverock/quantstrat.git \ + revdeps/quantstrat + + - name: Install remaining CRAN dependencies + shell: Rscript {0} + run: | + required <- c( + "quantmod", + "xts", + "zoo", + "TTR", + "PerformanceAnalytics", + "foreach", + "iterators", + "boot", + "tinytest", + "testthat" + ) + + missing <- required[ + !vapply( + required, + requireNamespace, + logical(1), + quietly = TRUE + ) + ] + + if (length(missing)) { + install.packages(missing) + } + + - name: Reinstall current FinancialInstrument + run: R CMD INSTALL . + + - name: Install blotter + run: R CMD INSTALL revdeps/blotter + + - name: Verify FinancialInstrument and blotter + shell: Rscript {0} + run: | + expected_fi <- package_version( + read.dcf( + "DESCRIPTION", + fields = "Version" + )[1, 1] + ) + + expected_blotter <- package_version( + read.dcf( + "revdeps/blotter/DESCRIPTION", + fields = "Version" + )[1, 1] + ) + + installed_fi <- packageVersion( + "FinancialInstrument" + ) + + installed_blotter <- packageVersion( + "blotter" + ) + + cat( + "FinancialInstrument:", + as.character(installed_fi), + "\n" + ) + + cat( + "blotter:", + as.character(installed_blotter), + "\n" + ) + + stopifnot( + installed_fi == expected_fi, + installed_blotter == expected_blotter + ) + + - name: Check blotter + shell: Rscript {0} + run: | + result <- rcmdcheck::rcmdcheck( + "revdeps/blotter", + args = c( + "--no-manual", + "--no-vignettes" + ), + build_args = "--no-build-vignettes", + error_on = "error" + ) + + print(result) + + - name: Reinstall tested dependency chain + run: | + R CMD INSTALL . + R CMD INSTALL revdeps/blotter + + - name: Install quantstrat + run: R CMD INSTALL revdeps/quantstrat + + - name: Verify quantstrat dependency chain + shell: Rscript {0} + run: | + library(FinancialInstrument) + library(blotter) + library(quantstrat) + + cat( + "FinancialInstrument:", + as.character(packageVersion("FinancialInstrument")), + "\n" + ) + + cat( + "blotter:", + as.character(packageVersion("blotter")), + "\n" + ) + + cat( + "quantstrat:", + as.character(packageVersion("quantstrat")), + "\n" + ) + + - name: Check quantstrat + continue-on-error: true + shell: Rscript {0} + run: | + result <- rcmdcheck::rcmdcheck( + "revdeps/quantstrat", + args = c( + "--no-manual", + "--no-vignettes" + ), + build_args = "--no-build-vignettes", + check_dir = "quantstrat-check", + error_on = "never" + ) + + print(result) + + - name: Upload quantstrat check results + if: always() + uses: actions/upload-artifact@v4 + with: + name: quantstrat-check-results + path: quantstrat-check + if-no-files-found: warn + \ No newline at end of file diff --git a/DESCRIPTION b/DESCRIPTION index efed003..f8d9e41 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Package: FinancialInstrument Type: Package Title: Financial Instrument Modeling Infrastructure -Version: 1.4.0 +Version: 1.4.1 Authors@R: c(person(given = "Peter", family = "Carl", role = "aut"), diff --git a/NEWS b/NEWS index b332bab..70fce54 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,43 @@ +# FinancialInstrument 1.4.1 + +## CRAN Compliance Fixes + +* **Console Output:** Added `verbose = FALSE` parameter to `alltick2sec()` at + the end of its signature. The unconditional `cat()` progress call is now + suppressible via `verbose = FALSE` (the default) and uses `message()` for + optional progress reporting, preserving all existing return values. + +* **Return-Value Documentation:** Added `@return` tags to all exported functions + that were previously missing `\value` sections, describing return class, + structure, and side-effects where applicable. + +* **Examples — Temporary Directories:** Replaced `dir.create("tmpdata")` with + `tempdir()` / `tempfile()` in the `saveInstruments`, `loadInstruments`, + `CompareInstrumentFiles`, `saveSymbols.common`, `saveSymbols.days`, and + `getSymbols.FI` examples. Cleanup is handled via `tryCatch(finally = ...)`. + +* **Examples — Internal Registry Access:** Removed `FinancialInstrument:::.instrument` + from all documentation examples and replaced backup/restore patterns with + exported `saveInstruments()` / `reloadInstruments()` calls. + +* **Examples — Unmatched Parenthesis:** Fixed the unmatched parenthesis in the + `ls_by_currency()` example. + +* **Examples — Commented-Out Code:** Removed commented-out executable + alternatives from examples. + +* **`\dontrun{}` vs `\donttest{}`:** Converted examples that can run in a + standard R session from `\dontrun{}` to either runnable or `\donttest{}` + blocks; retained `\dontrun{}` only for genuinely unrunnable code (e.g., + examples requiring live network access or specific external files). + +* **Namespace:** Confirmed `expires()` is exported. Removed examples for + genuinely unexported helpers. + +* **`loadInstruments` compatibility:** Updated the header check in + `loadInstruments()` to recognise both the legacy `"#auto"` prefix and the + new `"# Auto"` prefix written by the refactored `saveInstruments()`. + # FinancialInstrument 1.4.0 ## New Maintainer diff --git a/R/CompareInstrumentFiles.R b/R/CompareInstrumentFiles.R index 951374a..3540744 100644 --- a/R/CompareInstrumentFiles.R +++ b/R/CompareInstrumentFiles.R @@ -2,16 +2,16 @@ #' #' Compare the .instrument environments of two files #' -#' This will load two instrument files (created by -#' \code{\link{saveInstruments}}) and find the differences between them. In -#' addition to returning a list of difference that are found, it will produce -#' messages indicating the number of instruments that were added, the number of -#' instruments that were removed, and the number of instruments that are +#' This will load two instrument files (created by +#' \code{\link{saveInstruments}}) and find the differences between them. In +#' addition to returning a list of difference that are found, it will produce +#' messages indicating the number of instruments that were added, the number of +#' instruments that were removed, and the number of instruments that are #' different. #' #' @param file1 A file containing an instrument environment -#' @param file2 Another file containing an instrument environment. If not -#' provided, \code{file1} will be compared against the currently loaded +#' @param file2 Another file containing an instrument environment. If not +#' provided, \code{file1} will be compared against the currently loaded #' instrument environment. #' @param ... Arguments to pass to \code{\link{all.equal.instrument}} #' @return A list that contains the names of all instruments that were added, @@ -19,27 +19,53 @@ #' instruments that were updated (per \code{\link{all.equal.instrument}}). #' @author Garrett See #' @seealso \code{\link{saveInstruments}}, \code{\link{all.equal.instrument}} -#' @examples -#' \dontrun{ -#' #backup current .instrument environment -#' bak <- as.list(FinancialInstrument:::.instrument, all.names=TRUE) -#' old.wd <- getwd() -#' tmpdir <- tempdir() -#' setwd(tmpdir) -#' rm_instruments(keep=FALSE) -#' # create some instruments and save -#' stock(c("SPY", "DIA", "GLD"), currency("USD")) -#' saveInstruments("MyInstruments1") -#' # make some changes -#' rm_stocks("GLD") -#' stock("QQQ", "USD") -#' instrument_attr("SPY", "description", "S&P ETF") -#' saveInstruments("MyInstruments2") -#' CompareInstrumentFiles("MyInstruments1", "MyInstruments2") -#' #Clean up -#' setwd(old.wd) -#' reloadInstruments(bak) -#' } +#' @examples +#' example_dir <- tempfile("fi-compare-") +#' dir.create(example_dir) +#' +#' backup_name <- "backup.RData" +#' file1_name <- "instruments1.RData" +#' file2_name <- "instruments2.RData" +#' +#' backup_path <- file.path(example_dir, backup_name) +#' file1_path <- file.path(example_dir, file1_name) +#' file2_path <- file.path(example_dir, file2_name) +#' +#' saveInstruments(backup_name, dir = example_dir) +#' +#' tryCatch( +#' { +#' stopifnot(file.exists(backup_path)) +#' +#' rm_instruments(keep.currencies = FALSE) +#' currency("USD") +#' stock(c("SPY", "DIA", "GLD"), currency = "USD") +#' saveInstruments(file1_name, dir = example_dir) +#' +#' stopifnot(file.exists(file1_path)) +#' +#' rm_stocks("GLD") +#' stock("QQQ", currency = "USD") +#' instrument_attr( +#' "SPY", +#' "description", +#' "S&P 500 ETF" +#' ) +#' saveInstruments(file2_name, dir = example_dir) +#' +#' stopifnot(file.exists(file2_path)) +#' +#' CompareInstrumentFiles(file1_path, file2_path) +#' }, +#' finally = { +#' if (file.exists(backup_path)) { +#' reloadInstruments(backup_name, dir = example_dir) +#' } +#' +#' unlink(example_dir, recursive = TRUE) +#' } +#' ) +#' #' @export CompareInstrumentFiles <- function(file1, file2, ...) { force(file1) @@ -83,7 +109,7 @@ CompareInstrumentFiles <- function(file1, file2, ...) { } else { message(paste(liu, "instruments updated.")) } - out <- c(list(new.instruments=new.instruments, + out <- c(list(new.instruments=new.instruments, removed.instruments=removed.instruments), diffs) out <- Filter(function(x) length(x) > 0L, out) diff --git a/R/FinancialInstrument-package.R b/R/FinancialInstrument-package.R index b103102..95a8aa5 100644 --- a/R/FinancialInstrument-package.R +++ b/R/FinancialInstrument-package.R @@ -91,11 +91,11 @@ NULL #' where and how your market data are stored so that #' \code{\link[quantmod]{getSymbols}} will work for you. #' -#' FinancialInstrument's functions build and manipulate objects that are stored -#' in an environment named ".instrument" at the top level of the package -#' (i.e. "FinancialInstrument:::.instrument") rather than the global -#' environment, \code{.GlobalEnv}. Objects may be listed using -#' \code{ls_instruments()} (or many other ls_* functions). +#' FinancialInstrument stores instrument definitions in an internal +#' package-level environment named `.instrument`, rather than in +#' `.GlobalEnv`. Users should access instrument definitions through exported +#' functions such as `getInstrument()`, `ls_instruments()`, +#' `saveInstruments()`, and `loadInstruments()`. #' #' We store instruments in their own environment for two reasons. First, it #' keeps the user's workspace less cluttered and lowers the probability of diff --git a/R/FindCommonInstrumentAttributes.R b/R/FindCommonInstrumentAttributes.R index 7be65a3..a8ebf3c 100644 --- a/R/FindCommonInstrumentAttributes.R +++ b/R/FindCommonInstrumentAttributes.R @@ -1,23 +1,12 @@ #' Find attributes that more than one instrument have in common #' @param Symbols character vector of primary_ids of instruments -#' @param \dots arguments to pass to +#' @param \dots arguments to pass to #' \code{\link[FinancialInstrument]{getInstrument}} -#' @return character vector of names of attributes that all \code{Symbols}' +#' @return character vector of names of attributes that all \code{Symbols}' #' instruments have in common #' @author gsee #' @note I really do not like the name of this function, so if it survives, its #' name may change -#' @examples -#' \dontrun{ -#' ibak <- as.list(FinancialInstrument:::.instrument, all.names=TRUE) -#' Symbols <- c("SPY", "AAPL") -#' define_stocks(Symbols, addIBslot=FALSE) -#' update_instruments.SPDR("SPY") -#' update_instruments.TTR("AAPL", exchange="NASDAQ") -#' FindCommonInstrumentAttributes(Symbols) -#' FindCommonInstrumentAttributes(c(Symbols, "USD")) -#' reloadInstruments(ibak) -#' } FindCommonInstrumentAttributes <- function(Symbols, ...) { i <- lapply(Symbols, getInstrument, ...) n <- lapply(i, names) diff --git a/R/Tick2Sec.R b/R/Tick2Sec.R index 31e4b0e..7865241 100644 --- a/R/Tick2Sec.R +++ b/R/Tick2Sec.R @@ -2,7 +2,7 @@ # R (http://r-project.org/) Instrument Class Model # # Copyright (c) 2009-2012 -# Peter Carl, Dirk Eddelbuettel, Jeffrey Ryan, +# Peter Carl, Dirk Eddelbuettel, Jeffrey Ryan, # Joshua Ulrich, Brian G. Peterson, and Garrett See # # This library is distributed under the terms of the GNU Public License (GPL) @@ -14,42 +14,46 @@ #' Convert tick data to one-second data #' -#' This is like taking a snapshot of the market at the end of every second, +#' This is like taking a snapshot of the market at the end of every second, #' except the volume over the second is summed. -#' -#' From tick data with columns: \dQuote{Price}, \dQuote{Volume}, -#' \dQuote{Bid.Price}, \dQuote{Bid.Size}, \dQuote{Ask.Price}, \dQuote{Ask.Size}, -#' to data of one second frequency with columns \dQuote{Bid.Price}, +#' +#' From tick data with columns: \dQuote{Price}, \dQuote{Volume}, +#' \dQuote{Bid.Price}, \dQuote{Bid.Size}, \dQuote{Ask.Price}, \dQuote{Ask.Size}, +#' to data of one second frequency with columns \dQuote{Bid.Price}, #' \dQuote{Bid.Size}, \dQuote{Ask.Price}, \dQuote{Ask.Size}, #' \dQuote{Trade.Price}, and \dQuote{Volume} #' -#' The primary purpose of these functions is to reduce the amount of data on +#' The primary purpose of these functions is to reduce the amount of data on #' disk so that it will take less time to load the data into memory. #' -#' If there are no trades or bid/ask price updates in a given second, we will -#' not make a row for that timestamp. If there were no trades, but the bid or -#' ask price changed, then we _will_ have a row but the Volume and Trade.Price -#' will be NA. +#' If there are no trades or bid/ask price updates in a given second, we will +#' not make a row for that timestamp. If there were no trades, but the bid or +#' ask price changed, then we _will_ have a row but the Volume and Trade.Price +#' will be NA. #' -#' If there are multiple trades in the same second, Volume will be the sum of -#' the volume, but only the last trade price in that second will be printed. -#' Similarly, if there is a trade, and then later in the same second, there is +#' If there are multiple trades in the same second, Volume will be the sum of +#' the volume, but only the last trade price in that second will be printed. +#' Similarly, if there is a trade, and then later in the same second, there is #' a bid/ask update, the last Bid/Ask Price/Size will be used. -#' -#' \code{alltick2sec} is used to convert the data of several files from tick to +#' +#' \code{alltick2sec} is used to convert the data of several files from tick to #' one second frequency data. #' #' @param x the xts series to convert to 1 minute BATV #' -#' @param getdir Directory that contains tick data -#' @param savedir Directory in which to save converted data -#' @param Symbols String names of instruments to convert -#' @param overwrite TRUE/FALSE. If file already exists in savedir, should it be -#' overwritten? +#' @param getdir Directory containing tick data. If omitted, the value of +#' \code{getOption("FinancialInstrument.tickdir")} is used. +#' @param savedir Directory in which converted data will be saved. If omitted, +#' the value of \code{getOption("FinancialInstrument.secdir")} is used. +#' @param Symbols Character vector naming instruments to convert. If +#' \code{NULL}, all entries in \code{getdir} are considered. +#' @param overwrite Logical. If a destination file already exists, should it +#' be overwritten? +#' @param verbose Logical. If \code{TRUE}, display progress messages. #' @return \code{to_secBATV} returns an xts object of one second frequency. #' \code{alltick2sec} returns a list of files that were converted. #' @author gsee -#' @note \code{to_secBATV} is used by the TRTH_BackFill.R script in the +#' @note \code{to_secBATV} is used by the TRTH_BackFill.R script in the #' inst/parser directory of the FinancialInstrument package. These functions #' are specific to to data created by that script and are not intended for #' more general use. @@ -57,7 +61,11 @@ #' \dontrun{ #' getSymbols("CLU1") #' system.time(xsec <- to_secBATV(CLU1)) -#' convert.log <- alltick2sec() +#' convert.log <- alltick2sec( +#' getdir = "path/to/tick-data", +#' savedir = "path/to/second-data", +#' verbose = TRUE +#' ) #' } #' @export #' @rdname Tick2Sec @@ -65,59 +73,135 @@ to_secBATV <- function(x) { #require(qmao) # Define Bi and As functions (copied from qmao package) Bi <- function(x) { - if (has.Bid(x)) + if (has.Bid(x)) return(x[, grep("Bid", colnames(x), ignore.case = TRUE)]) stop("subscript out of bounds: no column name containing \"Bid\"") } As <- function(x) { - if (has.Ask(x)) + if (has.Ask(x)) return(x[, grep("Ask", colnames(x), ignore.case = TRUE)]) stop("subscript out of bounds: no column name containing \"Ask\"") } ohlcv <- suppressWarnings(to.period(x[, 1:2], 'seconds', 1)) ba <- x[, -c(1:2)] Volm <- if (!has.Vo(ohlcv)) { - rep(NA, NROW(ohlcv)) + rep(NA, NROW(ohlcv)) } else Vo(ohlcv) ClVo <- if(length(ohlcv) != 0) { ohlcv[, 4:5] } else { tmp <- xts(cbind(rep(NA, NROW(x)), rep(NA, NROW(x))), index(x)) tmp[endpoints(tmp, 'seconds')] } - + ClVo <- align.time(ClVo, 1) - + ba.sec <- align.time(to.period(ba, 'seconds', 1, OHLC=FALSE), 1) ba.sec <- na.locf(ba.sec) - + xx <- cbind(ba.sec, ClVo, all=TRUE) colnames(xx) <- c("Bid.Price", "Bid.Size", "Ask.Price", "Ask.Size", "Trade.Price", "Volume") - + xx -} +} #' @rdname Tick2Sec -alltick2sec <- function(getdir = '~/TRTH/tick/', - savedir = '~/TRTH/sec/', - Symbols=list.files(getdir), - overwrite = FALSE) { - if (!file.exists(savedir)) stop(paste("Please create savedir (", savedir, ") first", sep="")) - if(!requireNamespace("foreach", quietly=TRUE)) - stop("package:",dQuote("foreach"),"cannot be loaded.") - Symbols <- Symbols[!Symbols %in% c("instruments.rda")] +alltick2sec <- function( + getdir = getOption( + "FinancialInstrument.tickdir", + NULL + ), + savedir = getOption( + "FinancialInstrument.secdir", + NULL + ), + Symbols = NULL, + overwrite = FALSE, + verbose = FALSE +) { + if ( + is.null(getdir) || + length(getdir) != 1L || + is.na(getdir) || + !nzchar(getdir) + ) { + stop( + paste0( + "Supply 'getdir' or set ", + "options(FinancialInstrument.tickdir = ...)." + ), + call. = FALSE + ) + } + + if ( + is.null(savedir) || + length(savedir) != 1L || + is.na(savedir) || + !nzchar(savedir) + ) { + stop( + paste0( + "Supply 'savedir' or set ", + "options(FinancialInstrument.secdir = ...)." + ), + call. = FALSE + ) + } + + if (!dir.exists(getdir)) { + stop( + "'getdir' does not exist: ", + getdir, + call. = FALSE + ) + } + + if (!dir.exists(savedir)) { + stop( + "'savedir' does not exist: ", + savedir, + call. = FALSE + ) + } + + if (!requireNamespace("foreach", quietly = TRUE)) { + stop( + "Package ", + dQuote("foreach"), + " cannot be loaded.", + call. = FALSE + ) + } + + if (is.null(Symbols)) { + Symbols <- list.files(getdir) + } + + Symbols <- Symbols[ + !Symbols %in% "instruments.rda" + ] + gsep <- if(substr(getdir, nchar(getdir), nchar(getdir)) == "/") { "" } else "/" ssep <- if(substr(savedir, nchar(savedir), nchar(savedir)) == "/") {""} else "/" - s=NULL - `%dopar%` <- foreach::`%dopar%` - foreach::foreach(s = Symbols) %dopar% { - cat("converting ", s, ' ...\n') + + s <- NULL + + `%fi_foreach%` <- if (foreach::getDoParRegistered()) { + foreach::`%dopar%` + } else { + foreach::`%do%` + } + + foreach::foreach(s = Symbols) %fi_foreach% { + + if (verbose) message("converting ", s) gdir <- paste(getdir, s, sep=gsep) if (file.exists(gdir)) { - sdir <- paste(savedir, s, sep=ssep) + sdir <- paste(savedir, s, sep=ssep) if (!file.exists(sdir)) dir.create(sdir) #create dir for symbol if it doesn't exist fls <- list.files(gdir) fls <- fls[!fls %in% c("Bid.Image", "Ask.Image", "Price.Image")] - tmpenv <- new.env() + tmpenv <- new.env() unname(sapply(fls, function(fl) { if (!file.exists(paste(sdir, fl, sep='/')) || overwrite) { xsym <- try(load(paste(gdir, fl, sep="/"))) @@ -134,7 +218,7 @@ alltick2sec <- function(getdir = '~/TRTH/tick/', fl } } - } else warning(paste(sdir, '/', fl, + } else warning(paste(sdir, '/', fl, " already exists and will not be overwritten. Use overwrite=TRUE to overwrite.", sep="")) })) } else warning(paste(gdir, 'does not exist')) diff --git a/R/all.equal.instrument.R b/R/all.equal.instrument.R index 574ef7a..cd1cfb7 100644 --- a/R/all.equal.instrument.R +++ b/R/all.equal.instrument.R @@ -2,7 +2,7 @@ # R (http://r-project.org/) Instrument Class Model # # Copyright (c) 2009-2012 -# Peter Carl, Dirk Eddelbuettel, Jeffrey Ryan, +# Peter Carl, Dirk Eddelbuettel, Jeffrey Ryan, # Joshua Ulrich, Brian G. Peterson, and Garrett See # # This library is distributed under the terms of the GNU Public License (GPL) @@ -15,34 +15,35 @@ #' instrument all.equal method #' -#' This is most useful for seeing the difference between two \code{instrument} +#' This is most useful for seeing the difference between two \code{instrument} #' objects. #' -#' @param char.n If length of a character vector is \code{char.n} or less it +#' @param char.n If length of a character vector is \code{char.n} or less it #' will be treated as a single element. A negative value for \code{char.n} will #' be treated as if it were positive \code{Inf}. -#' @param collapse Only used if a character vector is of length less than -#' \code{char.n}. Unless \code{collapse} is \code{NULL}, it will be used in a -#' call to \code{\link{paste}}. If \code{collapse} is \code{NULL}, each element +#' @param collapse Only used if a character vector is of length less than +#' \code{char.n}. Unless \code{collapse} is \code{NULL}, it will be used in a +#' call to \code{\link{paste}}. If \code{collapse} is \code{NULL}, each element #' of the character vector will be compared separately. #' @author Garrett See #' @seealso \code{\link{getInstrument}}, \code{\link{instrument.table}}, #' \code{\link{buildHierarchy}} #' @note ALPHA code. Subject to change #' @keywords internal utilities +#' @return `TRUE` when `target` and `current` are equal. Otherwise, a character +#' vector describing differences between the two objects. #' @examples -#' \dontrun{ #' currency("USD") #' stock("SPY", "USD", validExchanges=c("SMART", "ARCA", "BATS", "BEX")) -#' stock("DIA", "USD", validExchanges=c("SMART", "ARCA", "ISLAND"), +#' stock("DIA", "USD", validExchanges=c("SMART", "ARCA", "ISLAND"), #' ExtraField="something") -#' +#' #' all.equal(getInstrument("SPY"), getInstrument("DIA")) #' all.equal(getInstrument("SPY"), getInstrument("DIA"), char.n=5) #' all.equal(getInstrument("SPY"), getInstrument("DIA"), char.n=5, collapse=NULL) -#' +#' #' all.equal(getInstrument("DIA"), getInstrument("USD")) -#' } +#' #' @export all.equal.instrument <- function (target, current, char.n=2, collapse=";", ...) { stopifnot(is.instrument(target)) @@ -50,9 +51,9 @@ all.equal.instrument <- function (target, current, char.n=2, collapse=";", ...) msg <- NULL if (mode(target) != mode(current)) { msg <- paste("Modes: ", mode(target), ", ", mode(current), sep="") - } + } if (length(target) != length(current)) { - msg <- c(msg, paste("Lengths: ", length(target), ", ", length(current), + msg <- c(msg, paste("Lengths: ", length(target), ", ", length(current), sep="")) } nt <- names(target) @@ -61,7 +62,7 @@ all.equal.instrument <- function (target, current, char.n=2, collapse=";", ...) msg <- c(msg, "names for current but not for target") #shouldn't happen because instruments are named lists } else if (is.null(nc) && !is.null(nt)) { - msg <- c(msg, "names for target but not for current") + msg <- c(msg, "names for target but not for current") } else { if (!all(nt %in% nc)) { msg <- c(msg, paste("Names in target that are not in current: <", @@ -74,9 +75,9 @@ all.equal.instrument <- function (target, current, char.n=2, collapse=";", ...) } if (!is.instrument(current)) { msg <- c(msg, paste("target is ", class(target)[1L], - ", current is ", class(current)[1L], sep="")) + ", current is ", class(current)[1L], sep="")) return(msg) - #TODO: maybe more comparisons can be done depending on what + #TODO: maybe more comparisons can be done depending on what # class(current) is } # Same class? @@ -88,7 +89,7 @@ all.equal.instrument <- function (target, current, char.n=2, collapse=";", ...) if (!isTRUE(all.equal(tc, cc))) { if (is.null(collapse)) { out <- NULL - if (!all(tc %in% cc)) { + if (!all(tc %in% cc)) { out <- paste("Classes of target that are not classes of current: <", paste(tc[!tc %in% cc], collapse=", "), ">") } @@ -98,11 +99,11 @@ all.equal.instrument <- function (target, current, char.n=2, collapse=";", ...) } msg <- c(msg, out) } else { - msg <- c(msg, paste("Classes: ", paste(paste(tc, collapse=collapse), + msg <- c(msg, paste("Classes: ", paste(paste(tc, collapse=collapse), paste(cc, collapse=collapse), sep=", "), sep="")) - } + } } - uniqueNames <- function(target, current) { + uniqueNames <- function(target, current) { unique(c(names(target), names(current))) } do.compare <- function(target, current, i) { @@ -128,7 +129,7 @@ all.equal.instrument <- function (target, current, char.n=2, collapse=";", ...) } if (max(length(ti), length(ci)) > char.n && is.character(ti)) { out <- NULL - if (!all(ti %in% ci)) + if (!all(ti %in% ci)) out <- paste(i, "in target but not in current: <", paste(ti[!ti %in% ci], collapse=", "), ">") if (!all(ci %in% ti)) @@ -140,7 +141,7 @@ all.equal.instrument <- function (target, current, char.n=2, collapse=";", ...) out <- if (isTRUE(all.equal(ti, ci, check.attributes=FALSE))) { all.equal(ti, ci) } else { - paste(paste(ti, collapse=collapse), + paste(paste(ti, collapse=collapse), paste(ci, collapse=collapse), sep=", ") } return(paste(i, ": ", out, sep="")) @@ -148,10 +149,10 @@ all.equal.instrument <- function (target, current, char.n=2, collapse=";", ...) out <- paste(ti, ci, sep=", ") out <- paste(i, ": ", out, sep="") return(out) - } + } } ntc <- uniqueNames(target, current) - msg <- c(msg, + msg <- c(msg, do.call(c, lapply(ntc, function(x) do.compare(target, current, x)))) if (is.null(msg)) { TRUE diff --git a/R/build_symbols.R b/R/build_symbols.R index f5f8196..f5b9896 100644 --- a/R/build_symbols.R +++ b/R/build_symbols.R @@ -1,4 +1,4 @@ -#' construct a series of symbols based on root symbol and suffix letters +#' Construct a series of symbols based on root symbol and suffix letters #' #' The columns needed by this version of the function are \code{primary_id} #' and \code{month_cycle}. \code{primary_id} should match the \code{primary_id} @@ -14,6 +14,9 @@ #' @param roots data.frame containing at least columns \code{primary_id} and \code{month_cycle}, see Details #' @author Brian G. Peterson #' @seealso \code{\link{load.instruments}} +#' @return A character vector containing the constructed series identifiers. +#' Each identifier combines a root contract identifier, a contract-month +#' code, and a year suffix. #' @export build_series_symbols <- function(roots, yearlist=c(0,1)) { symbols<-'' @@ -27,7 +30,7 @@ build_series_symbols <- function(roots, yearlist=c(0,1)) { return(symbols[-1]) } -#' build symbols for exchange guaranteed (calendar) spreads +#' Build symbols for exchange guaranteed (calendar) spreads #' #' The columns needed by this version of the function are \code{primary_id}, #' \code{month_cycle}, and code \code{contracts_ahead}. @@ -44,16 +47,16 @@ build_series_symbols <- function(roots, yearlist=c(0,1)) { #' \code{contracts_ahead} should contain a comma-delimited string describing #' the cycle on which the guaranteed calendar spreads are to be consructed, #' e.g. '1' for one-month spreads, '1,3' for one and three month spreads, -#' '1,6,12' for 1, 6, and 12 month spreads, etc. -#' For quarterly symbols, the correct \code{contracts_ahead} may be -#' something like '1,2,3' for quarterly, bi-annual, and annual spreads. +#' '1,6,12' for 1, 6, and 12 month spreads, etc. +#' For quarterly symbols, the correct \code{contracts_ahead} may be +#' something like '1,2,3' for quarterly, bi-annual, and annual spreads. #' -#' \code{active_months} is a numeric field indicating how many months including -#' the month of the \code{start_date} the contract is available to trade. +#' \code{active_months} is a numeric field indicating how many months including +#' the month of the \code{start_date} the contract is available to trade. #' This number will be used as the upper limit for symbol generation. -#' -#' If \code{type} is also specified, it should be a specific instrument type, -#' e.g. 'future_series','option_series','guaranteed_spread' or 'calendar_spread' +#' +#' If \code{type} is also specified, it should be a specific instrument type, +#' e.g. 'future_series','option_series','guaranteed_spread' or 'calendar_spread' #' #' One of \code{data} or \code{file} must be populated for input data. #' @@ -65,7 +68,11 @@ build_series_symbols <- function(roots, yearlist=c(0,1)) { #' @seealso #' \code{\link{load.instruments}} #' \code{\link{build_series_symbols}} -# @examples +#' @return If `outputfile` is `NULL`, a data frame with columns `symbol` and +#' `type`, containing the constructed spread identifiers and their instrument +#' types. If `outputfile` is supplied, the data frame is written to that CSV +#' file and the function returns `NULL` invisibly. +# @examples # build_spread_symbols(data=data.frame(primary_id='CL', # month_sequence="F,G,H,J,K,M,N,Q,U,V,X,Z", # contracts_ahead="1,2,3", @@ -187,7 +194,7 @@ build_spread_symbols <- function(data=NULL,file=NULL,outputfile=NULL,start_date= colnames(contractFrame)<-c("symbol","type") if(!is.null(outputfile)){ - write.csv(contractFrame,outputfile) + write.csv(contractFrame,outputfile) } else { return(contractFrame) } @@ -197,7 +204,7 @@ build_spread_symbols <- function(data=NULL,file=NULL,outputfile=NULL,start_date= # R (http://r-project.org/) Instrument Class Model # # Copyright (c) 2009-2012 -# Peter Carl, Dirk Eddelbuettel, Jeffrey Ryan, +# Peter Carl, Dirk Eddelbuettel, Jeffrey Ryan, # Joshua Ulrich, Brian G. Peterson, and Garrett See # # This code is distributed under the terms of the GNU Public License (GPL) diff --git a/R/expires.R b/R/expires.R index c79f775..4db00a0 100644 --- a/R/expires.R +++ b/R/expires.R @@ -12,7 +12,7 @@ # ############################################################################### -#' extract the correct expires value from an \code{instrument} +#' Extract the correct expires value from an \code{instrument} #' #' Currently, there are methods for \code{instrument}, \code{spread}, #' \code{character}, and \code{xts} @@ -41,7 +41,6 @@ #' \code{\link{getInstrument}} and \code{\link{buildHierarchy}} to see actual #' values stored in \code{instrument} #' @examples -#' \dontrun{ #' instr <- instrument("FOO_U1", currency=currency("USD"), multiplier=1, #' expires=c("2001-09-01", "2011-09-01", "2021-09-01"), #' assign_i=FALSE) @@ -60,7 +59,7 @@ #' expires=c("2001-09-01", "2011-09-01", "2021-09-01"), #' assign_i=TRUE) #' expires("FOO_U1") -#' } +#' #' @export expires <- function(x, ...) { UseMethod("expires") @@ -82,6 +81,8 @@ expires <- function(x, ...) { #' if \code{expires} is a single value, \code{expired} will be ignored. #' @param silent silence warnings? #' @seealso \code{\link{expires}} +#' @return A `Date` value representing the selected expiration date, or `NULL` +#' if the instrument has no usable expiration information. #' @author Garrett See #' @keywords internal #' @export @@ -124,13 +125,13 @@ expires.instrument <- function(x, Date, expired=TRUE, silent=FALSE, ...) { } -#' character expires extraction method +#' Character expires extraction method #' -#' if no \code{instrument} can be found by the id of \code{x}, or if the +#' If no \code{instrument} can be found by the id of \code{x}, or if the #' \code{instrument} does not have an \code{expires} attribute, an attempt #' will be made to infer the year and month of expiration using \code{parse_id} -#' in which case the returned value will be a string of the format -#' \dQuote{YYYY-MM}. Presently, \code{Date} and \code{expired} will be ignored +#' in which case the returned value will be a string. +#' Presently, \code{Date} and \code{expired} will be ignored #' if \code{x} is not the name of an instrument #' @param Date Can be a Date or character string. When \code{expires} is a #' vector, the retuned value will be one of the two values of \code{expires} @@ -142,6 +143,10 @@ expires.instrument <- function(x, Date, expired=TRUE, silent=FALSE, ...) { #' \code{FALSE} the first one after \code{Date} will be returned. #' @param silent silence warnings? #' @seealso \code{\link{expires.instrument}} +#' @return A `Date` value representing the selected or inferred expiration +#' date. If `x` identifies a defined instrument, its stored expiration +#' information is used; otherwise, the date is inferred from the identifier. +#' May return `NULL` when a defined instrument has no usable expiration. #' @author Garrett See #' @keywords internal #' @export @@ -178,6 +183,10 @@ expires.character <- function(x, Date, expired=TRUE, silent=FALSE, ...) { #' returned will be the last one before \code{Date}. If \code{expired} is #' \code{FALSE} the first one after \code{Date} will be returned. #' @seealso \code{\link{expires.instrument}} +#' @return A `Date` value representing the spread expiration. The value is +#' taken from the spread itself when available; otherwise, it is determined +#' from the first-expiring member. May return `NULL` if no expiration can be +#' determined. #' @author Garrett See #' @keywords internal #' @export diff --git a/R/find.instrument.R b/R/find.instrument.R index 75ba61f..6830374 100644 --- a/R/find.instrument.R +++ b/R/find.instrument.R @@ -2,7 +2,7 @@ # R (http://r-project.org/) Instrument Class Model # # Copyright (c) 2009-2012 -# Peter Carl, Dirk Eddelbuettel, Jeffrey Ryan, +# Peter Carl, Dirk Eddelbuettel, Jeffrey Ryan, # Joshua Ulrich, Brian G. Peterson, and Garrett See # # This library is distributed under the terms of the GNU Public License (GPL) @@ -14,59 +14,65 @@ #' Find the primary_ids of instruments that contain certain strings -#' +#' #' Uses regular expression matching to find \code{\link{instrument}}s -#' -#' @param text character string containing a regular expression. This is used +#' +#' @param text character string containing a regular expression. This is used #' by \code{\link{grep}} (see also) as the \code{pattern} argument. #' @param where if \dQuote{anywhere} all levels/attributes of the instruments #' will be searched. Otherwise, \code{where} can be used to specify in which #' levels/attributes to look. (e.g. \code{c("name", "description")} would only #' look for \code{text} in those 2 places. -#' @param Symbols the character ids of instruments to be searched. All are +#' @param Symbols the character ids of instruments to be searched. All are #' are searched by default. #' @param ignore.case passed to \code{\link{grep}}; if \code{FALSE}, the pattern -#' matching is case sensitive and if \code{TRUE}, case is ignored during +#' matching is case sensitive and if \code{TRUE}, case is ignored during #' matching. #' @param exclude character vector of names of levels/attributes that should not #' be searched. #' @param ... other arguments to pass through to \code{\link{grep}} -#' @return character vector of primary_ids of instruments that contain the +#' @return character vector of primary_ids of instruments that contain the #' sought after \code{text}. #' @author Garrett See -#' @seealso \code{\link{buildHierarchy}}, \code{\link{instrument.table}}, +#' @seealso \code{\link{buildHierarchy}}, \code{\link{instrument.table}}, #' \code{\link{regex}} #' @examples -#' \dontrun{ -#' instruments.bak <- as.list(FinancialInstrument:::.instrument, all.names=TRUE) -#' rm_instruments(keep.currencies=FALSE) -#' currency("USD") -#' stock("SPY", "USD", description="S&P 500 ETF") -#' stock("DIA", "USD", description="DJIA ETF") -#' stock(c("AA", "AXP", "BA", "BAC", "CAT"), "USD", members.of='DJIA') -#' stock("BMW", currency("EUR")) -#' find.instrument("ETF") -#' find.instrument("DJIA") -#' find.instrument("DJIA", "members.of") -#' find.instrument("USD") -#' find.instrument("EUR") -#' find.instrument("EUR", Symbols=ls_stocks()) -#' find.instrument("USD", "type") +#' backup_file <- tempfile(fileext = ".RData") +#' saveInstruments(backup_file) #' -#' ## Can be combined with buildHierachy -#' buildHierarchy(find.instrument("ETF"), "type", "description") +#' tryCatch( +#' { +#' rm_instruments(keep.currencies = FALSE) #' -#' ## Cleanup. restore previous instrument environment -#' rm_instruments(); rm_currencies() -#' loadInstruments(instruments.bak) -#' } +#' currency(c("USD", "EUR")) +#' stock("SPY", "USD", description = "S&P 500 ETF") +#' stock("DIA", "USD", description = "DJIA ETF") +#' stock( +#' c("AA", "AXP", "BA", "BAC", "CAT"), +#' "USD", +#' members.of = "DJIA" +#' ) +#' stock("BMW", "EUR") +#' +#' find.instrument("ETF") +#' find.instrument("DJIA") +#' find.instrument("DJIA", where = "members.of") +#' find.instrument("USD") +#' find.instrument("EUR", Symbols = ls_stocks()) +#' }, +#' finally = { +#' reloadInstruments(backup_file) +#' unlink(backup_file) +#' } +#' ) + #' @export find.instrument <- function(text, where='anywhere', Symbols = ls_instruments(), ignore.case=TRUE, exclude=NULL, ...) { tbl <- if (length(where) == 1 && where == "anywhere") { instrument.table(Symbols, exclude=exclude) } else buildHierarchy(Symbols, where[!where %in% exclude]) - unique(tbl[unique(unname(unlist(apply(tbl, 2, function(x) - grep(pattern=text, x=x, ignore.case=ignore.case, + unique(tbl[unique(unname(unlist(apply(tbl, 2, function(x) + grep(pattern=text, x=x, ignore.case=ignore.case, useBytes=TRUE, ...))))), 1]) } diff --git a/R/instrument.R b/R/instrument.R index 277c290..2c989ae 100644 --- a/R/instrument.R +++ b/R/instrument.R @@ -22,6 +22,8 @@ #' class test for object supposedly of type 'instrument' #' @param x object to test for type +#' @return A single logical value indicating whether `x` inherits from class +#' `"instrument"`. #' @export is.instrument <- function( x ) { inherits( x, "instrument" ) @@ -41,6 +43,8 @@ is.instrument.name <- function(x) { #' class test for object supposedly of type 'currency' #' @param x object to test for type +#' @return A single logical value indicating whether `x` inherits from class +#' `"currency"`. #' @export is.currency <- function( x ) { # x<-getInstrument(x, silent=TRUE) # Please use is.currency.name if x is character @@ -51,6 +55,8 @@ is.currency <- function( x ) { #' check each element of a character vector to see if it is either the #' primary_id or an identifier of a \code{\link{currency}} #' @param x character vector +#' @return A logical vector indicating whether each element of `x` identifies +#' a defined currency instrument. Non-character input returns `FALSE`. #' @export is.currency.name <- function( x ) { if (!is.character(x)) return(FALSE) @@ -142,7 +148,10 @@ is.currency.name <- function( x ) { #' primary_id be overwritten? Default is TRUE. If FALSE, an error will be #' thrown and the instrument will not be created. #' @aliases stock bond future option currency instrument fund -#' +#' @return When `assign_i = TRUE`, a character vector containing the +#' `primary_id` values of the instruments assigned to the instrument +#' registry. When `assign_i = FALSE`, an instrument object is returned for +#' scalar input; vectorized constructors return a list of instrument objects. #' @seealso #' \code{\link{currency}}, #' \code{\link{exchange_rate}}, @@ -371,10 +380,15 @@ future <- function(primary_id , currency , multiplier , tick_size=NULL, #' @param overwrite TRUE/FALSE. If FALSE, only \code{first_traded} and #' \code{expires} will be updated. #' @param ... any other passthru parameters +#' @return For `future_series()` and `option_series()`, a character vector of +#' assigned `primary_id` values when `assign_i = TRUE`, or an instrument +#' object for scalar input and a list of instrument objects for vectorized +#' input when `assign_i = FALSE`. `bond_series()` returns the result of +#' creating or updating the bond-series instrument; this is normally the +#' assigned identifier for a newly created series. #' @aliases option_series future_series bond_series #' #' @examples -#' \dontrun{ #' currency("USD") #' future("ES","USD",multiplier=50, tick_size=0.25) #' future_series('ES_U1') @@ -389,7 +403,7 @@ future <- function(primary_id , currency , multiplier , tick_size=NULL, #' #multiple series instruments at once. #' future_series(c("ES_H12","ES_M12")) #' option_series(c("SPY_110917C115","SPY_110917P115")) -#' } +#' #' @export #' @rdname series_instrument future_series <- function(primary_id, root_id=NULL, suffix_id=NULL, @@ -677,7 +691,7 @@ option_series <- function(primary_id , root_id = NULL, suffix_id = NULL, } } -#' constructor for series of options using yahoo data +#' Constructor for series of options using yahoo data #' #' Defines a chain or several chains of options by looking up necessary info #' from yahoo. @@ -706,11 +720,11 @@ option_series <- function(primary_id , root_id = NULL, suffix_id = NULL, #' @seealso \code{\link{option_series}}, \code{\link{option}}, #' \code{\link{instrument}}, \code{\link[quantmod]{getOptionChain}} #' @examples -#' \dontrun{ +#' \donttest{ #' option_series.yahoo('SPY') #only nearby calls and puts #' option_series.yahoo('DIA', Exp=NULL) #all chains #' ls_instruments() -#' } +#'} #' @export option_series.yahoo <- function(symbol, Exp, currency="USD", multiplier=100, first_traded=NULL, tick_size=NULL, overwrite=TRUE) { @@ -850,7 +864,7 @@ currency <- function(primary_id, identifiers = NULL, assign_i=TRUE, ...){ } -#' constructor for spot exchange rate instruments +#' Constructor for spot exchange rate instruments #' #' Currency symbols (like any symbol) may be any combination of alphanumeric #' characters, but the FX market has a convention that says that the first @@ -878,7 +892,11 @@ currency <- function(primary_id, identifiers = NULL, assign_i=TRUE, ...){ #' be thrown if there is already an instrument defined with the same #' \code{primary_id}. #' @param ... any other passthru parameters -#' @references http://financial-dictionary.thefreedictionary.com/Base+Currency +#' @return When `assign_i = TRUE`, a character vector containing the +#' `primary_id` values of the exchange-rate instruments assigned to the +#' registry. When `assign_i = FALSE`, an exchange-rate instrument object is +#' returned for scalar input, or a list of such objects for vectorized input. +#' @references https://www.investopedia.com/terms/b/basecurrency.asp #' @export exchange_rate <- function (primary_id = NULL, currency = NULL, counter_currency = NULL, tick_size=0.01, @@ -1033,7 +1051,6 @@ bond_series <- function(primary_id , suffix_id, ..., first_traded=NULL, #' or \code{bond} although it may be updated in the future. #' @author Garrett See #' @examples -#' \dontrun{ #' instrument.auto("CL_H1.U1") #' getInstrument("CL_H1.U1") #guaranteed_spread #' @@ -1049,7 +1066,7 @@ bond_series <- function(primary_id , suffix_id, ..., first_traded=NULL, #' future("VX","USD",1000,underlying_id=synthetic("SPX","USD")) #make the root #' instrument.auto("VX_H11") #and try again #' getInstrument("VX_H11") #made a future_series -#' } +#' #' @export instrument.auto <- function(primary_id, currency=NULL, multiplier=1, silent=FALSE, default_type='unknown', root=NULL, assign_i=TRUE, @@ -1276,17 +1293,20 @@ instrument.auto <- function(primary_id, currency=NULL, multiplier=1, silent=FALS #' @param Dates date range to retrieve 'as of', may not currently be implemented #' @param silent if TRUE, will not warn on failure, default FALSE #' @param type class of object to look for. See Details +#' @return An object inheriting from the requested instrument class when a +#' matching instrument is found. Returns `FALSE` when no matching instrument +#' is found. #' @examples -#' \dontrun{ #' option('..VX', multiplier=100, -#' underlying_id=future('.VX',multiplier=1000, -#' underlying_id=synthetic('VIX', currency("USD")))) +#' underlying_id=future('.VX',multiplier=1000, +#' underlying_id=synthetic('VIX', currency("USD"))) +#' ) #' #' getInstrument("VIX") #' getInstrument('VX') #returns the future #' getInstrument("VX",type='option') #' getInstrument('..VX') #finds the option -#' } +#' #' @export #' @rdname getInstrument getInstrument <- function(x, Dates=NULL, silent=FALSE, type='instrument'){ @@ -1513,6 +1533,7 @@ add.defined.by <- function(primary_ids, ...) { #' instrument class print method #' #' @author Joshua Ulrich, Garrett See +#' @return The instrument object `x`, invisibly. #' @keywords internal #' @export print.instrument <- function(x, ...) { @@ -1524,6 +1545,8 @@ print.instrument <- function(x, ...) { #' instrument class sort method #' #' @author Garrett See +#' @return An instrument object of the same class as `x`. Core fields remain +#' first and additional named fields are sorted alphabetically. #' @keywords internal #' @export sort.instrument <- function(x, decreasing=FALSE, na.last=NA, ...) { diff --git a/R/load.instruments.R b/R/load.instruments.R index 4cd15ba..d703e36 100644 --- a/R/load.instruments.R +++ b/R/load.instruments.R @@ -2,7 +2,7 @@ # R (http://r-project.org/) Instrument Class Model # # Copyright (c) 2009-2012 -# Peter Carl, Dirk Eddelbuettel, Jeffrey Ryan, +# Peter Carl, Dirk Eddelbuettel, Jeffrey Ryan, # Joshua Ulrich, Brian G. Peterson, and Garrett See # # This library is distributed under the terms of the GNU Public License (GPL) @@ -13,28 +13,28 @@ ############################################################################### -#' load instrument metadata into the .instrument environment -#' +#' Load instrument metadata into the .instrument environment +#' #' This function will load instrument metadata (data about the data) #' either from a file specified by the \code{file} argument or #' from a \code{data.frame} specified by the \code{metadata} argument. -#' +#' #' The function will attempt to make reasonable assumptions about what you're trying to do, but this isn't magic. -#' +#' #' You will typically need to specify the \code{type} of instrument to be loaded, failure to do so will generate a Warning and \code{default_type} will be used. -#' +#' #' You will need to specify a \code{primary_id}, or define a \code{id_col} that contains the data to be used as the primary_id of the instrument. -#' +#' #' You will need to specify a \code{currency}, unless the instrument \code{type} is 'currency' -#' +#' #' Use the \code{identifier_cols} argument to specify which fields (if any) in the CSV are to be passed to \code{\link{instrument}} as the \code{identifiers} argument #' #' Typically, columns will exist for \code{multiplier} and \code{tick_size}. -#' -#' Any other columns necessary to define the specified instrument type will also be required to avoid fatal Errors. -#' +#' +#' Any other columns necessary to define the specified instrument type will also be required to avoid fatal Errors. +#' #' Additional columns will be processed, either as additional identifiers for recognized identifier names, or as custom fields. See \code{\link{instrument}} for more information on custom fields. -#' +#' #' @param file string identifying file to load, default NULL, see Details #' @param ... any other passthru parameters #' @param metadata optional, data.frame containing metadata, default NULL, see Details @@ -42,19 +42,53 @@ #' @param default_type character string to use as instrument type fallback, see Details #' @param identifier_cols character vector of field names to be passed as identifiers, see Details #' @param overwrite TRUE/FALSE. See \code{\link{instrument}}. -#' @seealso +#' @return `NULL` invisibly. Called for the side effect of creating instrument +#' definitions in the package instrument registry. +#' @seealso #' \code{\link{loadInstruments}}, -#' \code{\link{instrument}}, -#' \code{\link{setSymbolLookup.FI}}, -#' \code{\link[quantmod]{getSymbols}}, +#' \code{\link{instrument}}, +#' \code{\link{setSymbolLookup.FI}}, +#' \code{\link[quantmod]{getSymbols}}, #' \code{\link{getSymbols.FI}} #' @examples -#' \dontrun{ -#' load.instruments(system.file('data/currencies.csv.gz',package='FinancialInstrument')) -#' load.instruments(system.file('data/root_contracts.csv.gz',package='FinancialInstrument')) -#' load.instruments(system.file('data/future_series.csv.gz',package='FinancialInstrument')) +#' example_ids <- c( +#' "FI_EXAMPLE_A", +#' "FI_EXAMPLE_B" +#' ) +#' +#' had_usd <- is.currency.name("USD") +#' +#' tryCatch( +#' { +#' if (!had_usd) { +#' currency("USD") +#' } +#' +#' metadata <- data.frame( +#' primary_id = example_ids, +#' type = c("stock", "stock"), +#' currency = c("USD", "USD"), +#' description = c( +#' "Example instrument A", +#' "Example instrument B" +#' ), +#' stringsAsFactors = FALSE +#' ) +#' +#' load.instruments(metadata = metadata) +#' +#' getInstrument("FI_EXAMPLE_A") +#' getInstrument("FI_EXAMPLE_B") +#' }, +#' finally = { +#' rm_instruments(example_ids) +#' +#' if (!had_usd) { +#' rm_currencies("USD") +#' } +#' } +#' ) #' -#' } #' @export load.instruments <- function (file=NULL, ..., metadata=NULL, id_col=1, default_type='stock', identifier_cols=NULL, overwrite=TRUE) { @@ -64,12 +98,12 @@ load.instruments <- function (file=NULL, ..., metadata=NULL, id_col=1, default_t filedata<-read.csv(file,stringsAsFactors=FALSE, ...=...) } else { stop("The specified file ",file," does not seem to exist, maybe specify the full path?") - } + } } else { filedata<-metadata rm(metadata) } - + # check required column headers if(!any(grepl('primary_id',colnames(filedata)))) { #no primary_id column name, use id_col as the name @@ -84,11 +118,11 @@ load.instruments <- function (file=NULL, ..., metadata=NULL, id_col=1, default_t filedata$type<-rep(default_type,nrow(filedata)) } else { filedata$type <- rep(dotargs$type, nrow(filedata)) - dotargs$type <- NULL + dotargs$type <- NULL } } if (!is.null(dotargs$currency) && !is.currency.name(dotargs$currency)) currency(dotargs$currency) - + #now process the data for(rn in 1:nrow(filedata)){ type <- as.character(filedata[rn,'type']) @@ -110,28 +144,28 @@ load.instruments <- function (file=NULL, ..., metadata=NULL, id_col=1, default_t if (set_primary) { arg$primary_id<-filedata[rn,id_col] } - + #do some name cleanup to make up for Reuters silliness if(substr(arg$primary_id,1,1)==1) arg$primary_id <- substr(arg$primary_id,2,nchar(arg$primary_id)) arg$primary_id<-make.names(arg$primary_id) if(!is.null(arg$X.RIC)){ if(substr(arg$X.RIC,1,1)==1) arg$X.RIC <- substr(arg$X.RIC,2,nchar(arg$X.RIC)) - } + } if(!is.null(arg$RIC)){ if(substr(arg$RIC,1,1)==1) arg$RIC <- substr(arg$RIC,2,nchar(arg$RIC)) - } + } if(length(dotargs)) arg<-c(arg,dotargs) - + if(!is.null(identifier_cols) && any(identifier_cols %in% names(arg))){ arg$identifiers <- arg[names(arg) %in% identifier_cols] arg[identifier_cols] <- NULL } - + arg$overwrite <- overwrite if(is.function(try(match.fun(type),silent=TRUE))){ out <- try(do.call(type,arg)) - - + + #TODO recover gracefully? } else { # the call for a function named for type didn't work, so we'll try calling instrument as a generic @@ -143,21 +177,24 @@ load.instruments <- function (file=NULL, ..., metadata=NULL, id_col=1, default_t } # end loop on rows } -#' set quantmod-style SymbolLookup for instruments -#' -#' This function exists to tell \code{\link[quantmod]{getSymbols}} where to look for your repository of market data. -#' -#' The \code{base_dir} parameter \emph{must} be set or the function will fail. -#' This will vary by your local environment and operating system. For mixed-OS environments, -#' we recommend doing some OS-detection and setting the network share to your data to a common -#' location by operating system. For example, all Windows machines may use \dQuote{M:/} -#' and all *nix-style (linux, Mac) machines may use \dQuote{/mnt/mktdata/}. -#' -#' The \code{split_method} currently allows either \sQuote{days} or \sQuote{common}, and expects the -#' file or files to be in sub-directories named for the symbol. In high frequency data, it is standard practice to split +#' Set quantmod-style SymbolLookup for instruments +#' +#' This function exists to tell \code{\link[quantmod]{getSymbols}} where to look +#' for your repository of market data. +#' +#' The \code{base_dir} parameter \emph{must} be set or the function will fail. +#' This will vary by your local environment and operating system. For mixed-OS +#' environments, we recommend doing some OS-detection and setting the network +#' share to your data to a common location by operating system. For example, +#' all Windows machines may use \dQuote{M:/} and all *nix-style (linux, Mac) +#' machines may use \dQuote{/mnt/mktdata/}. +#' +#' The \code{split_method} currently allows either \sQuote{days} or +#' \sQuote{common}, and expects the file or files to be in sub-directories named +#' for the symbol. In high frequency data, it is standard practice to split #' the data by days, which is why that option is the default. -#' -#' @param base_dir string specifying the base directory where data is stored, see Details +#' +#' @param base_dir string specifying the base directory where data is stored, see Details #' @param Symbols character vector of names of instruments for which to \code{setSymbolLookup} #' @param \dots any other passthru parameters #' @param storage_method currently only \sQuote{rda}, but we will eventually support \sQuote{indexing} at least, and maybe others @@ -165,7 +202,9 @@ load.instruments <- function (file=NULL, ..., metadata=NULL, id_col=1, default_t #' @param use_identifier string identifying which column should be use to construct the \code{primary_id} of the instrument, default 'primary_id' #' @param extension file extension, default "rda" #' @param src which \code{\link[quantmod]{getSymbols}} sub-type to use, default \code{\link{getSymbols.FI}} by setting 'FI' -#' @seealso +#' @return No meaningful return value. Called for the side effect of updating +#' the `quantmod` symbol lookup table for the requested instruments. +#' @seealso #' \code{\link{getSymbols.FI}}, #' \code{\link{instrument_attr}}, #' \code{\link{load.instruments}}, \code{\link{loadInstruments}}, @@ -175,7 +214,7 @@ load.instruments <- function (file=NULL, ..., metadata=NULL, id_col=1, default_t setSymbolLookup.FI<-function(base_dir, Symbols, ..., split_method=c("days","common"), storage_method='rda', use_identifier='primary_id', extension='rda', src='FI'){ # check that base_dir exists if(!file.exists(base_dir)) stop('base_dir ',base_dir,' does not seem to specify a valid path' ) - + # take split split_method<-split_method[1] # only use the first value @@ -183,9 +222,9 @@ setSymbolLookup.FI<-function(base_dir, Symbols, ..., split_method=c("days","comm instr_names <- if(missing(Symbols)) { ls_non_currencies(ls(pos=.instrument)) #if roots begin with a dot, this will filter out roots and currencies } else Symbols - + #TODO add check to make sure that src is actually the name of a getSymbols function - + #initialize list params<-list() params$storage_method<-storage_method @@ -205,7 +244,7 @@ setSymbolLookup.FI<-function(base_dir, Symbols, ..., split_method=c("days","comm instr_str<-make.names(tmp_instr$identifiers[[use_identifier]]) } else { instr_str<-make.names(tmp_instr[[use_identifier]]) - } + } if(!is.null(instr_str)) instr<-instr_str symbol<-list() symbol[[1]]<-params @@ -219,79 +258,120 @@ setSymbolLookup.FI<-function(base_dir, Symbols, ..., split_method=c("days","comm #' getSymbols method for loading data from split files -#' -#' This function should probably get folded back into getSymbols.rda in +#' +#' This function should probably get folded back into getSymbols.rda in #' quantmod. -#' +#' #' Meant to be called internally by \code{\link[quantmod]{getSymbols}} . -#' -#' The symbol lookup table will most likely be loaded by +#' +#' The symbol lookup table will most likely be loaded by #' \code{\link{setSymbolLookup.FI}} -#' -#' If date_format is NULL (the Default), we will assume an ISO date as changed -#' by \code{\link{make.names}}, for example, 2010-12-01 would be assumed to be a +#' +#' If date_format is NULL (the Default), we will assume an ISO date as changed +#' by \code{\link{make.names}}, for example, 2010-12-01 would be assumed to be a #' file containing 2010.12.01 -#' +#' #' If \code{indexTZ} is provided, the data will be converted to that timezone #' -#' If auto.assign is FALSE, \code{Symbols} should be of length 1. Otherwise, -#' \code{\link[quantmod]{getSymbols}} will give you an error that says +#' If auto.assign is FALSE, \code{Symbols} should be of length 1. Otherwise, +#' \code{\link[quantmod]{getSymbols}} will give you an error that says #' \dQuote{must use auto.assign=TRUE for multiple Symbols requests} -#' However, if you were to call \code{getSymbols.FI} directly (which is -#' \emph{NOT} recommended) with \code{auto.assign=FALSE} and more than one +#' However, if you were to call \code{getSymbols.FI} directly (which is +#' \emph{NOT} recommended) with \code{auto.assign=FALSE} and more than one #' Symbol, a list would be returned. -#' -#' Argument matching for this function is as follows. If the user provides a +#' +#' Argument matching for this function is as follows. If the user provides a #' value for an argument, that value will be used. If the user did not provide -#' a value for an argument, but there is a value for that argument for the -#' given \code{Symbol} in the Symbol Lookup Table (see -#' \code{\link{setSymbolLookup.FI}}), that value will be used. Otherwise, +#' a value for an argument, but there is a value for that argument for the +#' given \code{Symbol} in the Symbol Lookup Table (see +#' \code{\link{setSymbolLookup.FI}}), that value will be used. Otherwise, #' the formal defaults will be used. #' -#' @param Symbols a character vector specifying the names of each symbol to be +#' @param Symbols a character vector specifying the names of each symbol to be #' loaded #' @param from Retrieve data no earlier than this date. Default '2010-01-01'. #' @param to Retrieve data through this date. Default Sys.Date(). #' @param ... any other passthru parameters -#' @param dir if not specified in getSymbolLookup, directory string to use. +#' @param dir if not specified in getSymbolLookup, directory string to use. #' default "" #' @param return.class only "xts" is currently supported #' @param extension file extension, default "rda" -#' @param split_method string specifying the method used to split the files, -#' currently \sQuote{days} or \sQuote{common}, see +#' @param split_method string specifying the method used to split the files, +#' currently \sQuote{days} or \sQuote{common}, see #' \code{\link{setSymbolLookup.FI}} -#' @param use_identifier optional. identifier used to construct the -#' \code{primary_id} of the instrument. If you use this, you must have -#' previously defined the \code{\link{instrument}} +#' @param use_identifier optional. identifier used to construct the +#' \code{primary_id} of the instrument. If you use this, you must have +#' previously defined the \code{\link{instrument}} #' @param date_format format as per the \code{\link{strptime}}, see Details #' @param verbose TRUE/FALSE -#' @param days_to_omit character vector of names of weekdays that should not be -#' loaded. Default is \code{c("Saturday", "Sunday")}. Use \code{NULL} to +#' @param days_to_omit character vector of names of weekdays that should not be +#' loaded. Default is \code{c("Saturday", "Sunday")}. Use \code{NULL} to #' attempt to load data for all days of the week. -#' @param indexTZ valid TZ string. (e.g. \dQuote{America/Chicago} or +#' @param indexTZ valid TZ string. (e.g. \dQuote{America/Chicago} or #' \dQuote{America/New_York}) See \code{\link[xts]{indexTZ}}. -#' @seealso +#' @return If `auto.assign = TRUE`, a character vector containing the names of +#' the objects assigned to `env`. If `auto.assign = FALSE`, an `xts` object +#' is returned for one requested symbol and a named list is returned for +#' multiple symbols. Returns `NULL` when no data are found. +#' @seealso #' \code{\link{saveSymbols.days}} #' \code{\link{instrument}} #' \code{\link{setSymbolLookup.FI}} #' \code{\link{loadInstruments}} #' \code{\link[quantmod]{getSymbols}} #' @examples -#' \dontrun{ -#' getSymbols("SPY", src='yahoo') -#' dir.create("tmpdata") -#' saveSymbols.common("SPY", base_dir="tmpdata") -#' rm("SPY") -#' getSymbols("SPY", src='FI', dir="tmpdata", split_method='common') -#' unlink("tmpdata/SPY", recursive=TRUE) -#' } +#' example_dir <- tempfile("fi-symbol-data-") +#' dir.create(example_dir) +#' +#' data_env <- new.env() +#' +#' data_env$FI_EXAMPLE <- xts::xts( +#' cbind( +#' Close = c(100, 101, 102, 101), +#' Volume = c(1000, 1200, 900, 1100) +#' ), +#' order.by = as.Date("2020-01-01") + 0:3 +#' ) +#' +#' tryCatch( +#' { +#' saveSymbols.common( +#' Symbols = "FI_EXAMPLE", +#' base_dir = example_dir, +#' env = data_env +#' ) +#' +#' loaded_data <- getSymbols.FI( +#' Symbols = "FI_EXAMPLE", +#' dir = example_dir, +#' split_method = "common", +#' auto.assign = FALSE, +#' verbose = FALSE +#' ) +#' +#' stopifnot( +#' xts::is.xts(loaded_data), +#' NROW(loaded_data) == 4L, +#' identical( +#' colnames(loaded_data), +#' c("Close", "Volume") +#' ) +#' ) +#' +#' loaded_data +#' }, +#' finally = { +#' unlink(example_dir, recursive = TRUE) +#' } +#' ) +#' #' @export getSymbols.FI <- function(Symbols, from=getOption("getSymbols.FI.from", "2010-01-01"), to=getOption("getSymbols.FI.to", Sys.Date()), - ..., + ..., dir=getOption("getSymbols.FI.dir", ""), - return.class=getOption("getSymbols.FI.return.class", + return.class=getOption("getSymbols.FI.return.class", "xts"), extension=getOption("getSymbols.FI.extension", "rda"), split_method=getOption("getSymbols.FI.split_method", @@ -311,42 +391,42 @@ getSymbols.FI <- function(Symbols, for(var in names(list(...))) { assign(var,list(...)[[var]], this.env) } - - #The body of the following function comes from Dominik's answer here: + + #The body of the following function comes from Dominik's answer here: #browseURL("http://stackoverflow.com/questions/7224938/can-i-rbind-be-parallelized-in-r") #it does what do.call(rbind, lst) would do, but faster and with less memory usage do.call.rbind <- function(lst) { while(length(lst) > 1) { idxlst <- seq(from=1, to=length(lst), by=2) - + lst <- lapply(idxlst, function(i) { if(i==length(lst)) { return(lst[[i]]) } - + return(rbind(lst[[i]], lst[[i+1]])) }) } lst[[1]] } - + # Find out if user provided a value for each formal if (hasArg.from <- hasArg(from)) .from <- from if (hasArg.to <- hasArg(to)) .to <- to if (hasArg.dir <- hasArg(dir)) .dir <- dir - if (hasArg.return.class <- hasArg(return.class)) + if (hasArg.return.class <- hasArg(return.class)) .return.class <- return.class if (hasArg.extension <- hasArg(extension)) .extension <- extension - if (hasArg.split_method <- hasArg(split_method)) + if (hasArg.split_method <- hasArg(split_method)) .split_method <- split_method - if (hasArg.use_identifier <- hasArg(use_identifier)) + if (hasArg.use_identifier <- hasArg(use_identifier)) .use_identifier <- use_identifier if (hasArg.date_format <- hasArg(date_format)) .date_format <- date_format if (hasArg.verbose <- hasArg(verbose)) .verbose <- verbose - if (hasArg.days_to_omit <- hasArg(days_to_omit)) + if (hasArg.days_to_omit <- hasArg(days_to_omit)) .days_to_omit <- days_to_omit if (hasArg.indexTZ <- hasArg(indexTZ)) .indexTZ <- indexTZ - + importDefaults("getSymbols.FI") - + # Now get the values for each formal that we'll use if not provided # by the user and not found in the SymbolLookup table default.from <- from @@ -360,12 +440,12 @@ getSymbols.FI <- function(Symbols, default.verbose <- verbose default.days_to_omit <- days_to_omit default.indexTZ <- indexTZ - + # quantmod:::getSymbols will provide auto.assign and env # so the next 2 if statements should always be TRUE auto.assign <- if(hasArg(auto.assign)) {auto.assign} else TRUE - env <- if(hasArg(env)) {env} else .GlobalEnv - + env <- if(hasArg(env)) {env} else .GlobalEnv + # make an argument matching function to sort out which values to use for each arg pickArg <- function(x, Symbol) { if(get(paste('hasArg', x, sep="."))) { @@ -374,13 +454,13 @@ getSymbols.FI <- function(Symbols, SymbolLookup[[Symbol]][[x]] } else get(paste("default", x, sep=".")) } - + SymbolLookup <- getSymbolLookup() fr <- NULL datl <- lapply(1:length(Symbols), function(i) { - #FIXME? Should nothing be saved if there are errors with any of + #FIXME? Should nothing be saved if there are errors with any of # the Symbols (current behavior)? Or, if auto.assign == TRUE, should - # we assign the data as we get it instead of making a list of data and + # we assign the data as we get it instead of making a list of data and # assigning at the end. from <- pickArg("from", Symbols[[i]]) to <- pickArg("to", Symbols[[i]]) @@ -396,9 +476,9 @@ getSymbols.FI <- function(Symbols, # if 'dir' is actually the 'base_dir' then we'll paste the instrument name (Symbol) to the end of it. # First, find out what the instrument name is instr_str <- NA - if(!is.na(use_identifier[1])) { + if(!is.na(use_identifier[1])) { tmp_instr <- try(getInstrument(Symbols[[i]], silent=FALSE)) - if (inherits(tmp_instr,'try-error') || !is.instrument(tmp_instr)) + if (inherits(tmp_instr,'try-error') || !is.instrument(tmp_instr)) stop("must define instrument first to call with 'use_identifier'") if (!use_identifier[1]=='primary_id') { instr_str <- make.names(unlist(tmp_instr$identifiers[use_identifier])) @@ -406,11 +486,11 @@ getSymbols.FI <- function(Symbols, if (length(instr_str) == 0L) stop("Could not find instrument. Try with use_identifier=NA") } - Symbol <- ifelse(is.na(instr_str), make.names(Symbols[[i]]), instr_str) - ndc<-nchar(dir) - if(substr(dir,ndc,ndc)=='/') dir <- substr(dir,1,ndc-1) #remove trailing forward slash + Symbol <- ifelse(is.na(instr_str), make.names(Symbols[[i]]), instr_str) + ndc<-nchar(dir) + if(substr(dir,ndc,ndc)=='/') dir <- substr(dir,1,ndc-1) #remove trailing forward slash dirs <- paste(dir, Symbol, sep="/") - + tmp <- list() dirstr<-paste(dirs, collapse=' ') if(!length(dirs)==1) warning(paste0('multiple directories ',dirstr,' referenced, merge may interleave dissimilar data.')) @@ -421,18 +501,18 @@ getSymbols.FI <- function(Symbols, if(verbose) cat("loading ",Symbols[[i]],".....\n") switch(split_method[1], days={ - StartDate <- as.Date(from) - EndDate <- as.Date(to) + StartDate <- as.Date(from) + EndDate <- as.Date(to) date.vec <- as.Date(StartDate:EndDate) - date.vec <- date.vec[!weekdays(date.vec) %in% days_to_omit] + date.vec <- date.vec[!weekdays(date.vec) %in% days_to_omit] date.vec <- format(date.vec, format=date_format) sym.files <- paste(date.vec, basename(dir), extension, sep=".") if (dir != "") sym.files <- file.path(dir, sym.files) dl <- lapply(sym.files, function(fp) { sf <- strsplit(fp, "/")[[1]] sf <- sf[length(sf)] - if (verbose) cat("Reading ", sf, "...") - if(!file.exists(fp)) { + if (verbose) cat("Reading ", sf, "...") + if(!file.exists(fp)) { if (verbose) cat(" failed. File not found in ", dir, " ... skipping\n") } else { if (verbose) cat(' done.\n') @@ -459,23 +539,23 @@ getSymbols.FI <- function(Symbols, if(verbose) cat("done.\n") #if(!is.xts(fr)) fr <- xts(fr[,-1],as.Date(fr[,1],origin='1970-01-01'),src='rda',updated=Sys.time()) } - } # end 'common'/default method (same as getSymbols.rda) + } # end 'common'/default method (same as getSymbols.rda) ) # end split_method switch fr <- convert.time.series(fr=fr,return.class=return.class) - Symbols[[i]] <-make.names(Symbols[[i]]) - if(dir==dirs[1]) tmp[[Symbols[[i]]]] <- fr + Symbols[[i]] <-make.names(Symbols[[i]]) + if(dir==dirs[1]) tmp[[Symbols[[i]]]] <- fr else tmp[[Symbols[[i]]]] <- rbind(tmp[[Symbols[[i]]]],fr) } # end Symbols else } if(verbose) cat("done.\n") tmp }) #end lapply loop over Symbols - + if (length(Filter("+", lapply(datl, length))) == 0) { warning("No data found.") - return(NULL) + return(NULL) } - + datl.names <- do.call(c, lapply(datl, names)) missing <- Symbols[!Symbols %in% datl.names] if (length(missing) > 0) warning('No data found for ', paste(missing, collapse=" ")) diff --git a/R/ls_by_currency.R b/R/ls_by_currency.R index 35d18c3..e947195 100755 --- a/R/ls_by_currency.R +++ b/R/ls_by_currency.R @@ -12,97 +12,156 @@ # ############################################################################### -#' shows or removes instruments of given currency denomination(s) +#' List instruments by currency denomination #' -#' ls_ functions get names of instruments denominated in a given currency (or -#' currencies) rm_ functions remove instruments of a given currency +#' Returns the names of instruments denominated in one or more specified +#' currencies. #' +#' @param currency Character vector containing the names of currencies. +#' @param pattern An optional regular expression. Only instrument names +#' matching `pattern` are returned. +#' @param match Logical. Should `pattern` be matched exactly? +#' @param show.currencies Logical. Should currency instruments themselves be +#' included in the returned names? +#' @param x Character vector of instrument names to remove. If missing, all +#' instruments denominated in `currency` are selected. +#' @param keep.currencies Logical. If `TRUE`, retain the currency instruments +#' themselves. +#' +#' @return A character vector containing instrument names denominated in the +#' requested currencies, or `NULL` when no matching instruments are found. #' -#' @aliases ls_by_currency rm_by_currency ls_USD ls_AUD ls_GBP ls_CAD ls_EUR ls_JPY ls_CHF ls_HKD ls_SEK ls_NZD -#' @param currency chr vector of names of currency -#' @param pattern an optional regular expression. Only names matching -#' \sQuote{pattern} are returned. -#' @param match exact match? -#' @param show.currencies include names of currency instruments in the returned -#' names? -#' @param keep.currencies Do not delete currency instruments when deleting -#' multiple instruments. -#' @param x what to remove. chr vector. -#' @return ls_ functions return vector of instrument names rm_ functions return -#' invisible / called for side-effect. #' @author Garrett See -#' @seealso ls_instruments, ls_currencies, rm_instruments, rm_currencies, -#' twsInstrument, instrument +#' +#' @seealso +#' [ls_instruments()], [ls_currencies()], [rm_instruments()], +#' [rm_currencies()], [instrument()] +#' #' @examples +#' example_dir <- tempfile("fi-currency-") +#' dir.create(example_dir) +#' +#' backup_name <- "backup.RData" +#' backup_path <- file.path(example_dir, backup_name) +#' +#' saveInstruments(backup_name, dir = example_dir) #' -#' \dontrun{ -#' #First create instruments -#' currency(c('USD','CAD','GBP') -#' stock(c('CM','CNQ'),'CAD') -#' stock(c('BET','BARC'),'GBP') -#' stock(c('SPY','DIA'),'USD') +#' tryCatch( +#' { +#' rm_instruments(keep.currencies = FALSE) #' -#' #now the examples -#' ls_by_currency(c('CAD','GBP')) +#' currency(c("USD", "CAD", "GBP")) +#' stock(c("CM", "CNQ"), currency = "CAD") +#' stock(c("BARC", "BET"), currency = "GBP") +#' stock(c("DIA", "SPY"), currency = "USD") #' -#' ls_USD() -#' ls_CAD() +#' ls_by_currency("CAD") +#' ls_by_currency("GBP") +#' ls_USD() +#' ls_CAD() +#' +#' rm_by_currency(currency = "CAD") +#' +#' }, +#' finally = { +#' if (file.exists(backup_path)) { +#' reloadInstruments( +#' backup_name, +#' dir = example_dir +#' ) +#' } +#' +#' unlink(example_dir, recursive = TRUE) +#' } +#' ) #' -#' #2 ways to remove all instruments of a currency -#' rm_instruments(ls_USD()) -#' #rm_instruments(ls_GBP(),keep.currencies=FALSE) -#' rm_by_currency( ,'CAD') -#' #rm_by_currency( ,'CAD', keep.currencies=FALSE) -#' } #' @export #' @rdname ls_by_currency -ls_by_currency <- function(currency, pattern=NULL, match=TRUE,show.currencies=FALSE) { - if (length(pattern) > 1 && !match) { - warning("Using match because length of pattern > 1.") - #should I use match? - #or, ignore pattern and return everything? - #or, do multiple ls calls and return unique - match <- TRUE - } +ls_by_currency <- function( + currency, + pattern = NULL, + match = TRUE, + show.currencies = FALSE +) { + if (length(pattern) > 1L && !match) { + warning("Using match because length of pattern > 1.") + match <- TRUE + } - if (!(currency %in% ls_currencies()) ) { - warning(paste(currency, 'is not a defined currency', sep=" ")) - } + undefined_currencies <- currency[ + !currency %in% ls_currencies() + ] - if (!is.null(pattern) && match) { #there's a pattern and match is TRUE - symbols <- ls_instruments() - symbols <- symbols[match(pattern,symbols)] - } else if (!match && length(pattern) == 1) { # pattern is length(1) and match is FALSE - symbols <- ls_instruments(pattern=pattern) - } else if (is.null(pattern)) { #no pattern - symbols <- ls_instruments() - } # else pattern length > 1 & don't match + if (length(undefined_currencies)) { + warning( + paste(undefined_currencies, collapse = ", "), + if (length(undefined_currencies) == 1L) { + " is not a defined currency" + } else { + " are not defined currencies" + }, + call. = FALSE + ) + } - tmp_symbols <- NULL - for (symbol in symbols) { - tmp_instr <- try(get(symbol, pos = .instrument),silent=TRUE) - if (is.instrument(tmp_instr) && - tmp_instr$currency == currency ){ - tmp_symbols <- c(tmp_symbols,symbol) - } + if (!is.null(pattern) && match) { + symbols <- ls_instruments() + symbols <- symbols[match(pattern, symbols)] + } else if (!match && length(pattern) == 1L) { + symbols <- ls_instruments(pattern = pattern) + } else if (is.null(pattern)) { + symbols <- ls_instruments() + } + + tmp_symbols <- NULL + + for (symbol in symbols) { + tmp_instr <- try( + get(symbol, pos = .instrument), + silent = TRUE + ) + + if ( + is.instrument(tmp_instr) && + !is.null(tmp_instr$currency) && + any(tmp_instr$currency %in% currency) + ) { + tmp_symbols <- c(tmp_symbols, symbol) } - if (show.currencies) { - tmp_symbols - } else if (!is.null(tmp_symbols)) { - ls_non_currencies(tmp_symbols) - } else NULL -} + } + if (show.currencies) { + tmp_symbols + } else if (!is.null(tmp_symbols)) { + ls_non_currencies(tmp_symbols) + } else { + NULL + } +} #' @export #' @rdname ls_by_currency -rm_by_currency <- function(x,currency,keep.currencies=TRUE) { - sc <- !keep.currencies #make show.currencies==opposite of keep - if (missing(x)) { - x <- ls_by_currency(currency,show.currencies=sc) - } else x <- ls_by_currency(currency,pattern=x,show.currencies=sc) - rm(list=x,pos=.instrument) -} +rm_by_currency <- function( + x, + currency, + keep.currencies = TRUE +) { + show_currencies <- !keep.currencies + if (missing(x)) { + x <- ls_by_currency( + currency = currency, + show.currencies = show_currencies + ) + } else { + x <- ls_by_currency( + currency = currency, + pattern = x, + show.currencies = show_currencies + ) + } + + rm(list = x, pos = .instrument) +} #AUD GBP CAD EUR JPY CHF HKD SEK NZD #' @export #' @rdname ls_by_currency diff --git a/R/ls_instruments.R b/R/ls_instruments.R index cac6c88..a87454b 100755 --- a/R/ls_instruments.R +++ b/R/ls_instruments.R @@ -52,8 +52,7 @@ #' instrument, stock, future, option, currency, FinancialInstrument::sort_ids #' @examples #' -#' \dontrun{ -#' #rm_instruments(keep.currencies=FALSE) #remove everything from .instrument +#' rm_instruments(keep.currencies=FALSE) #remove everything from .instrument #' #' # First, create some instruments #' currency(c("USD", "EUR", "JPY")) @@ -80,13 +79,13 @@ #' ls_derivatives() #' ls_puts() #' ls_non_derivatives() -#' #ls_by_expiry('20110618',ls_puts()) #put options that expire on Jun 18th, 2011 -#' #ls_puts(ls_by_expiry('20110618')) #same thing +#' ls_by_expiry('20110618',ls_puts()) #put options that expire on Jun 18th, 2011 +#' ls_puts(ls_by_expiry('20110618')) #' #' rm_options('SPY_110618C130') #' rm_futures() #' ls_instruments() -#' #rm_instruments('EUR') #Incorrect + #' rm_instruments('EUR', keep.currencies=FALSE) #remove the currency #' rm_currencies('JPY') #or remove currency like this #' ls_currencies() @@ -95,11 +94,7 @@ #' rm_instruments() #remove all but currencies #' rm_currencies() #' -#' option_series.yahoo('DIA') -#' ls_instruments_by('underlying_id','DIA') #underlying_id must exactly match 'DIA' -#' ls_derivatives('DIA',match=FALSE) #primary_ids that contain 'DIA' -#' rm_instruments() -#' } +#' #' @export #' @rdname ls_instruments ls_instruments <- function(pattern=NULL, match=TRUE, verbose=TRUE) { diff --git a/R/parse_id.R b/R/parse_id.R index 316d54a..8f07766 100755 --- a/R/parse_id.R +++ b/R/parse_id.R @@ -2,7 +2,7 @@ # R (http://r-project.org/) Instrument Class Model # # Copyright (c) 2009-2012 -# Peter Carl, Dirk Eddelbuettel, Jeffrey Ryan, +# Peter Carl, Dirk Eddelbuettel, Jeffrey Ryan, # Joshua Ulrich, Brian G. Peterson, and Garrett See # # This library is distributed under the terms of the GNU Public License (GPL) @@ -17,21 +17,21 @@ #' #' Extract/infer descriptive information about an instrument from its name. #' -#' This function is primarily intended to be used on the names of \code{\link{future_series}} -#' and \code{\link{option_series}} instruments, and it will work best if the id has an -#' underscore in it that separates the root_id from the suffix_id. (However, it should be able -#' to handle most ids even if the underscore is missing). -#' After splitting \code{x} into a root_id and suffix_id, the suffix_id is +#' This function is primarily intended to be used on the names of \code{\link{future_series}} +#' and \code{\link{option_series}} instruments, and it will work best if the id has an +#' underscore in it that separates the root_id from the suffix_id. (However, it should be able +#' to handle most ids even if the underscore is missing). +#' After splitting \code{x} into a root_id and suffix_id, the suffix_id is #' passed to \code{\link{parse_suffix}} (see also) for further processing. -#' +#' #' TODO: add support for bond_series. #' @param x the id to be parsed (e.g. \sQuote{ES_U11}, \sQuote{SPY_111217C130}) #' @param silent silence warnings? #' @param root character name of instrument root_id. Optionally provide this to make parsing easier. -#' @return a list of class \sQuote{id.list} containing \sQuote{root} and \sQuote{suffix} as well as +#' @return a list of class \sQuote{id.list} containing \sQuote{root} and \sQuote{suffix} as well as #' what is returned from \code{\link{parse_suffix}} (type, month, year, strike, right, cm, cc, format) #' @author Garrett See -#' @note this function will identify \code{x} as an \code{\link{exchange_rate}} only if it is +#' @note this function will identify \code{x} as an \code{\link{exchange_rate}} only if it is #' 6 characters long and made up of 2 previously defined \code{\link{currency}} instruments. #' @seealso \code{\link{parse_suffix}} #' @examples @@ -45,10 +45,10 @@ parse_id <- function(x, silent=TRUE, root=NULL) { x <- gsub("-", ".", x) all.numeric <- as.logical(!is.na(suppressWarnings(as.numeric(x)))) if (!is.null(root)) { - suffix <- sub(root,"",x) #turns ESU1 into U1, or ES_U11 into _U11 + suffix <- sub(root,"",x) #turns ESU1 into U1, or ES_U11 into _U11 suffix <- gsub("_","",suffix) #take out the underscore if there is one - } else if (identical(integer(0), grep("[0-9]",x))) { - #if there are no numbers in the id, then it has no year, so it is not a recognized future or option + } else if (identical(integer(0), grep("[0-9]",x))) { + #if there are no numbers in the id, then it has no year, so it is not a recognized future or option if (substr(x, nchar(x)-1, nchar(x)) %in% c(".O", ".K")) { #only one dot, and the id ends with ".O" -- it looks like an X.RIC for a NASDAQ stock suffix <- "" @@ -59,12 +59,12 @@ parse_id <- function(x, silent=TRUE, root=NULL) { suffix <- "" root <- substr(x, 1, nchar(x)-3) type <- 'root' - } else if (identical(all.equal(nchar(x) - nchar( gsub("\\.","",x)),1), TRUE)) { + } else if (identical(all.equal(nchar(x) - nchar( gsub("\\.","",x)),1), TRUE)) { #only 1 dot, so it's not a fly #SPY.DIA, EUR.USD, SPY110917C122.5, T2010917P25 if (suppressWarnings(!is.na(as.numeric(strsplit(x,"\\.")[[1]][2])))) { #probably an option with a decimal in the strike - #if we take out all the numbers, periods, and dashes, - #we should be left with the ticker and either "C" or "P" + #if we take out all the numbers, periods, and dashes, + #we should be left with the ticker and either "C" or "P" root <- gsub("[0-9.-]","",x) #now it looks like SPYC or TP root <- substr(root, 1,nchar(root)-1) suffix <- gsub(root,"",x) #whatever isn't the root @@ -74,9 +74,9 @@ parse_id <- function(x, silent=TRUE, root=NULL) { type <- 'synthetic' sufftype <- FALSE } - } else if (identical(all.equal(nchar(x) - nchar( gsub("\\.","",x)),2), TRUE)) { + } else if (identical(all.equal(nchar(x) - nchar( gsub("\\.","",x)),2), TRUE)) { #2 dots, so we'll treat it as a fly, although it could be a basket - #SPY.DIA.QQQ, + #SPY.DIA.QQQ, suffix <- "" root <- x type <- 'synthetic' @@ -90,43 +90,43 @@ parse_id <- function(x, silent=TRUE, root=NULL) { sufftype <- FALSE suffformat <- FALSE } else if (nchar(x) == 6) { - if (is.instrument(getInstrument(substr(x,1,3),silent=TRUE)) + if (is.instrument(getInstrument(substr(x,1,3),silent=TRUE)) && is.instrument(getInstrument(substr(x,4,6),silent=TRUE))) { type <- c('exchange_rate', 'root') sufftype <- FALSE } } } - } else if (!all.numeric && identical(x, gsub('_','',x))) { #no underscore; have to guess what is root and what is suffix - hasdot <- !identical(integer(0),grep("\\.",x)) - if (!silent && !hasdot) + } else if (!all.numeric && identical(x, gsub('_','',x))) { #no underscore; have to guess what is root and what is suffix + hasdot <- !identical(integer(0),grep("\\.",x)) + if (!silent && !hasdot) warning("id of future_series should have an underscore in it. Trying to parse anyway.") #if (nchar(x) < 9 && !hasdot) { #assume it's a future like ESU1 or ESU11 if (!hasdot) { #assume it's a future like ESU1 or ESU11 - if (suppressWarnings(!is.null(parse_suffix(substr(x,3,nchar(x)))) && + if (suppressWarnings(!is.null(parse_suffix(substr(x,3,nchar(x)))) && !is.na(parse_suffix(substr(x,3,nchar(x)))$format))) { root <- substr(x,1,2) suffix <- substr(x,3,nchar(x)) - } else if (suppressWarnings(!is.null(parse_suffix(substr(x,4,nchar(x)))) && + } else if (suppressWarnings(!is.null(parse_suffix(substr(x,4,nchar(x)))) && !is.na(parse_suffix(substr(x,4,nchar(x)))$format))) { - root <- substr(x,1,3) + root <- substr(x,1,3) suffix <- substr(x,4,nchar(x)) - } else if (suppressWarnings(!is.null(parse_suffix(substr(x,5,nchar(x)))) && + } else if (suppressWarnings(!is.null(parse_suffix(substr(x,5,nchar(x)))) && !is.na(parse_suffix(substr(x,5,nchar(x)))$format))) { - root <- substr(x,1,4) + root <- substr(x,1,4) suffix <- substr(x,5,nchar(x)) - } else if (suppressWarnings(!is.null(parse_suffix(substr(x,5,nchar(x)))) && + } else if (suppressWarnings(!is.null(parse_suffix(substr(x,5,nchar(x)))) && !is.na(parse_suffix(substr(x,6,nchar(x)))$format))) { - root <- substr(x,1,5) + root <- substr(x,1,5) suffix <- substr(x,6,nchar(x)) - } else if (suppressWarnings(!is.null(parse_suffix(substr(x,2,nchar(x)))) && + } else if (suppressWarnings(!is.null(parse_suffix(substr(x,2,nchar(x)))) && !is.na(parse_suffix(substr(x,2,nchar(x)))$format))) { root <- substr(x,1,1) suffix <- substr(x,2,nchar(x)) } else if (nchar(x) <= 3) { root <- substr(x, 1, nchar(x)) - suffix <- "" - } else { + suffix <- "" + } else { root <- substr(x,1,6) suffix <- substr(x,7,nchar(x)) } @@ -160,19 +160,19 @@ parse_id <- function(x, silent=TRUE, root=NULL) { format <- NA sufftype <- FALSE suffformat <- FALSE - } + } } - } else { #there _is_ an underscore and at least 1 number. + } else { #there _is_ an underscore and at least 1 number. #if there are dots then maybe it is a spread of futures? #e.g. "CL_N1.HO_M1" ss <- strsplit(x,"\\.")[[1]] has.und <- function(x) { #TRUE if it has an underscore sapply(x, FUN=function(x) !identical(x, gsub('_','',x))) } - if (length(ss) > 1) { - if (all(has.und(ss))) { #all parts have an underscore. + if (length(ss) > 1) { + if (all(has.und(ss))) { #all parts have an underscore. #e.g. CL_N1.HO_M1 --> "CL_N1" "HO_M1" - #or CL_N1.CL_M1 + #or CL_N1.CL_M1 ssu <- strsplit(ss,"_") tmprt <- ssu[[1]][1] if (all(sapply(ssu, FUN=function(x) x[1] == tmprt))) { @@ -185,25 +185,25 @@ parse_id <- function(x, silent=TRUE, root=NULL) { suffix <- "" type <- 'synthetic' sufftype <- FALSE - } + } } else if (has.und(ss[1]) && !has.und(ss[2])) { #First part has underscore, but second doesn't. e.g. CL_H1.M1 --> "CL_H1" "M1" spl.und <- strsplit(x,"_")[[1]] root <- spl.und[1] suffix <- paste(spl.und[2:length(spl.und)],collapse=".") - - } else { + + } else { suffix <- "" root <- x type <- 'synthetic' sufftype <- FALSE - } - } else { #ES_U1, ES_U1M1, + } + } else { #ES_U1, ES_U1M1, root <- strsplit(x,"_")[[1]][1] suffix <- strsplit(x,"_")[[1]][2] - } + } } - root <- gsub(" ","",root) + root <- gsub(" ","",root) if (!is.na(suffix)) { suff <- parse_suffix(suffix, silent=silent) if (sufftype) type <- suff$type @@ -213,18 +213,18 @@ parse_id <- function(x, silent=TRUE, root=NULL) { type <- "Asian" format <- "numeric" } - structure(list(root=root, suffix=suffix, type=type, month=suff$month, - year=suff$year, strike=suff$strike, right=suff$right, + structure(list(root=root, suffix=suffix, type=type, month=suff$month, + year=suff$year, strike=suff$strike, right=suff$right, cm=suff$cm, cc=suff$cc, format=format),class='id.list') } #' parse a suffix_id -#' +#' #' extract information from the suffix_id of an instrument #' -#' These would be recognized as a Sep 2011 outright futures contract: +#' These would be recognized as a Sep 2011 outright futures contract: #' U1, U11, SEP1, SEP11, U2011, Sep2011, SEP2011 -#' +#' #' These would be recognized as a call with a strike of 122.5 that expires Sep 17, 2011: #' 110917C122.5, 20110917C122.5, 110917C00122500, 20110917C00122500 #' @@ -239,7 +239,7 @@ parse_id <- function(x, silent=TRUE, root=NULL) { #' #' Synthetics and spreads: #' -#' SPY.DIA --> type == synthetic; +#' SPY.DIA --> type == synthetic; #' #' U1.Z1 or U11.Z11 --> type == "calendar", "spread"; month == 'SEP', year == 2011 #' @@ -249,8 +249,8 @@ parse_id <- function(x, silent=TRUE, root=NULL) { #' 110917C125.110917P125 --> type == option_spread, spread #' @param x the suffix_id to be parsed #' @param silent silence warnings? (warning will usually be about inferring a 4 digit year from a 1 or 2 digit year) -#' @return an object of class \sQuote{suffix.list} which is a list containing \sQuote{type} of instrument, -#' \sQuote{month} of expiration, \sQuote{year} of expiration, \sQuote{strike} price of option, +#' @return an object of class \sQuote{suffix.list} which is a list containing \sQuote{type} of instrument, +#' \sQuote{month} of expiration, \sQuote{year} of expiration, \sQuote{strike} price of option, #' \sQuote{right} of option (\dQuote{C} or \dQuote{P}), \sQuote{cm} (maturity in days of a constant maturity contract), #' \sQuote{cc} (method for calculating a continuous contract), \sQuote{format} (string that indicates the format of the unparsed id). #' @author Garrett See @@ -272,15 +272,15 @@ parse_suffix <- function(x, silent=TRUE) { if (x == "") { type <- "root" } else if (!identical(gsub("cm.","",x), x)) { - #A 30 day constant maturity synthetic futures contract - #on the vix would look like VX_cm.30 + #A 30 day constant maturity synthetic futures contract + #on the vix would look like VX_cm.30 type <- c('outright', 'cm') cm <- as.numeric(strsplit(x,"\\.")[[1]][2]) format <- 'cm' } else if (!identical(gsub("cc.","",x),x)) { - #cc.OI for rolling on Open Interest, - #cc.Vol for rolling on Volume, - #cc.Exp.1 for rolling 1 day before Expiration date. (Exp.0 would be rolling on expiration) + #cc.OI for rolling on Open Interest, + #cc.Vol for rolling on Volume, + #cc.Exp.1 for rolling 1 day before Expiration date. (Exp.0 would be rolling on expiration) type <- c('outright', 'cc') cc <- gsub('cc.','',x) format <- 'cc' @@ -289,19 +289,19 @@ parse_suffix <- function(x, silent=TRUE) { # 110917C125 or 20110917C125 or 110917C00125000 or 20110917C00125000 hasdot <- !identical(integer(0),grep("\\.",x)) if (!hasdot - || (hasdot + || (hasdot && !is.na(as.numeric(strsplit(x,"\\.")[[1]][2])))) { #&& nchar(strsplit(x,"\\.")[[1]][2]) <= 2)) { - #if it doesn't have a dot, or it does have dot, but what follows + #if it doesn't have a dot, or it does have dot, but what follows #the dot is numeric, then it's an option outright if (any(substr(x,7,7) == c("C","P"))) { type <- c("outright","option") month <- toupper(month.abb[as.numeric(substr(x,3,4))]) year <- 2000 + as.numeric(substr(x,1,2)) strike <- as.numeric(substr(x,8,nchar(x))) - if (nchar(x) >= 15) strike <- strike/1000 + if (nchar(x) >= 15) strike <- strike/1000 right <- substr(x,7,7) - format <- 'opt2' + format <- 'opt2' } else if (any(substr(x,9,9) == c("C","P"))) { type <- c("outright","option") month <- toupper(month.abb[as.numeric(substr(x,5,6))]) @@ -312,7 +312,7 @@ parse_suffix <- function(x, silent=TRUE) { format <- 'opt4' } else stop("how did you get here?") } else type <- c("option_spread","spread") - } else if (!identical(gsub("\\.","",x),x)) { #has a dot. U1.Z1, U11.Z11, SPY.DIA, + } else if (!identical(gsub("\\.","",x),x)) { #has a dot. U1.Z1, U11.Z11, SPY.DIA, if (identical(all.equal(nchar(x) - nchar( gsub("\\.","",x)),1), TRUE)) { #only 1 dot, so it's not a fly #U1.Z1, U11.Z11, SPY.DIA, EUR.USD, H2.0302 s <- strsplit(x,"\\.")[[1]] @@ -324,11 +324,11 @@ parse_suffix <- function(x, silent=TRUE) { if (inherits(s2,'try-error')) { s2 <- parse_id(s[2],silent=TRUE) } - + if (all(c(s1$type,s2$type) == 'root')) { type='synthetic' } else { - type <- if (!is.na(s1$format) + type <- if (!is.na(s1$format) && !is.na(s2$format) && !s1$format %in% c("opt2", "opt4", "NNNN") && (s2$format == "NNNN")) { @@ -336,7 +336,7 @@ parse_suffix <- function(x, silent=TRUE) { c("ICS", "spread") } else c("calendar", "spread") month <- s1$month - year <- s1$year + year <- s1$year format <- paste(s1$format, s2$format, sep=".") } } else if (identical(all.equal(nchar(x) - nchar(gsub("\\.","",x)),2), TRUE)) { #2 dots; it's a fly @@ -347,19 +347,19 @@ parse_suffix <- function(x, silent=TRUE) { s3 <- try(parse_suffix(s[3],silent=TRUE),silent=TRUE) if (inherits(s1,'try-error')) s1 <- parse_id(s[1],silent=TRUE) if (inherits(s2,'try-error')) s2 <- parse_id(s[2],silent=TRUE) - if (inherits(s3,'try-error')) s3 <- parse_id(s[3],silent=TRUE) + if (inherits(s3,'try-error')) s3 <- parse_id(s[3],silent=TRUE) if (all(c(s1$type,s2$type,s3$type) == 'root')) { type='synthetic' #don't really know if it's fly-like or a basket - } else { + } else { type=c('butterfly','spread') } format <- unique(c(s1$format, s2$format, s3$format)) } else { if (!silent) warning("limited functionality for suffix that implies more than 3 instruments") - type='synthetic' #condors, baskets, strips, packs/bundles, + type='synthetic' #condors, baskets, strips, packs/bundles, } ##End check for suffixes with a dot - } else if (any(substr(x,1,2) == c("1C","1D"))) { # Single-stock future (SSF) + } else if (any(substr(x,1,2) == c("1C","1D"))) { # Single-stock future (SSF) #1CU1, 1CU11, 1CSEP11 -- 1DU1 for dividend protected if (substr(x,1,2) == "1C") { type <- c('outright', 'SSF') @@ -392,25 +392,25 @@ parse_suffix <- function(x, silent=TRUE) { type <- 'root' } else if (!silent) warning("Could not parse 3 character suffix") } else if (nchar(x) == 4) { #SEP1, VXU1, 0911 - if (toupper(substr(x, 1, 3)) %in% toupper(C2M()) && !is.na(suppressWarnings(as.numeric(substr(x,4,4))))) { + if (toupper(substr(x, 1, 3)) %in% toupper(C2M()) && !is.na(suppressWarnings(as.numeric(substr(x,4,4))))) { #sep1, Sep1, SEP1 suff <- paste(M2C(tolower(substr(x,1,3))), substr(x,4,4), sep="") #convert from Sep1 to U1 out <- parse_suffix(suff,silent=silent) #call recursively with 2 character suffix out$format <- 'MMMY' - return(out) + return(out) # } else if (substr(x,3,3) %in% M2C() && !is.na(as.numeric(substr(x,4,4)))) { -# #xxU1, VXU1 #ignore the 1st 2 characters, and call recursively with 2 character suffix -# suff <- parse_suffix(substr(x,3,4),silent=silent) +# #xxU1, VXU1 #ignore the 1st 2 characters, and call recursively with 2 character suffix +# suff <- parse_suffix(substr(x,3,4),silent=silent) # month <- suff$month # year <- suff$year # format <- 'xxCY' - } else if (!is.na(as.numeric(x))) { + } else if (!is.na(as.numeric(x))) { #0911 #convert to U11 and call recursively suff <- paste(M2C()[as.numeric(substr(x,1,2))], substr(x, 3,4), sep="") out <- parse_suffix(suff,silent=silent) out$format <- "NNNN" - return(out) + return(out) } else { if (!silent) warning("Could not parse 4 character suffix") @@ -420,7 +420,7 @@ parse_suffix <- function(x, silent=TRUE) { } else if (nchar(x) == 5) { #SEP11, U2011 type <- c("outright","future") if (toupper(substr(x,1,3)) %in% toupper(C2M()) && !is.na(as.numeric(substr(x,4,5)))) { - #SEP11 + #SEP11 month <- toupper(substr(x,1,3)) year <- as.numeric(substr(x,4,5)) + 2000 if (!silent) warning('Converting 2 digit year to 4 digit year assumes there are no futures before 2000') @@ -436,7 +436,7 @@ parse_suffix <- function(x, silent=TRUE) { if (!silent) warning("I'm not programmed to handle a 6 character suffix") return(NULL) - + } else if (nchar(x) == 7) { #Sep2011 if (toupper(substr(x, 1, 3)) %in% toupper(C2M()) && !is.na(as.numeric(substr(x,4,7))) ) { @@ -444,15 +444,16 @@ parse_suffix <- function(x, silent=TRUE) { month <- toupper(substr(x,1,3)) year <- as.numeric(substr(x, 4,7)) format <- 'MMMYYYY' - } - } + } + } structure(list(type=type, month=month,year=year, strike=strike, right=right, cm=cm, cc=cc, format=format), class='suffix.list') } #' id.list class print method -#' +#' #' @keywords internal +#' @return The `id.list` object `x`, invisibly. #' @export print.id.list <- function(x, ...) { str(x, comp.str="", give.length=FALSE, give.attr=FALSE) @@ -460,8 +461,9 @@ print.id.list <- function(x, ...) { } #' suffix.list class print method -#' +#' #' @keywords internal +#' @return The `suffix.list` object `x`, invisibly. #' @export print.suffix.list <- function(x, ...) { str(x, comp.str="", give.length=FALSE, give.attr=FALSE) diff --git a/R/redenominate.R b/R/redenominate.R index 00039ef..b699e91 100755 --- a/R/redenominate.R +++ b/R/redenominate.R @@ -2,7 +2,7 @@ # R (http://r-project.org/) Instrument Class Model # # Copyright (c) 2009-2012 -# Peter Carl, Dirk Eddelbuettel, Jeffrey Ryan, +# Peter Carl, Dirk Eddelbuettel, Jeffrey Ryan, # Joshua Ulrich, Brian G. Peterson, and Garrett See # # This library is distributed under the terms of the GNU Public License (GPL) @@ -15,23 +15,16 @@ #' get an exchange rate series #' #' Try to find exchange rate data in an environment, inverting if necessary. -#' +#' #' @param ccy1 chr name of 1st currency #' @param ccy2 chr name of 2nd currency #' @param env environment in which to look for data. #' @return xts object with as many columns as practicable. #' @author Garrett See -#' @seealso +#' @seealso #' \code{\link{buildRatio}} #' \code{\link{redenominate}} -#' @examples -#' -#' \dontrun{ -#' EURUSD <- getSymbols("EURUSD=x",src='yahoo',auto.assign=FALSE) -#' USDEUR <- .get_rate("USD","EUR") -#' head(USDEUR) -#' head(EURUSD) -#' } +#' @keywords internal #' @rdname get_rate .get_rate <- function(ccy1, ccy2, env=.GlobalEnv) { rsym <- NA @@ -53,8 +46,8 @@ invert = TRUE } rate <- try(get(rsym,pos=env),silent=TRUE) - if (inherits(rate,'try-error')) - stop(paste('Could not find exchange rate for ', ccy1, + if (inherits(rate,'try-error')) + stop(paste('Could not find exchange rate for ', ccy1, ' and ', ccy2, ' in ', deparse(substitute(env)), sep='')) rsym <- paste(substr(rsym,1,3), substr(rsym,nchar(rsym)-2,nchar(rsym)),sep="") if (invert) { @@ -95,13 +88,13 @@ xts(x, order.by=as.Date(paste(index(x)))) } -#' construct price ratios of 2 instruments +#' construct price ratios of 2 instruments #' -#' Calculates time series of ratio of 2 instruments using available data. +#' Calculates time series of ratio of 2 instruments using available data. #' Returned object will be ratios calculated using Bids, Asks, and Mids, or Opens, Closes, and Adjusteds. #' #' \code{x} should be a vector of 2 instrument names. An attempt will be made to \code{get} the data -#' for both instruments. If there are no xts data stored under either of the names, it will try to +#' for both instruments. If there are no xts data stored under either of the names, it will try to #' return prebuilt data with a call to \code{\link{.get_rate}}. #' #' If the data are not of the same frequency, or are not of the same type (OHLC, BBO, etc.) @@ -109,7 +102,7 @@ #' #' If the data in \code{x[1]} is daily or slower and the data in \code{x[2]} is intraday # then the intraday data in \code{x[2]} will become univariate -#' (e.g. if you give it daily OHLC and intraday Bid Ask Mid, it will use all of +#' (e.g. if you give it daily OHLC and intraday Bid Ask Mid, it will use all of #' the OHLC columns of \code{x[1]} and only the the End of Day Mid price of the BAM object. #' #' If the data in \code{x[1]} is intraday, and the data in \code{x[2]} is daily or slower, @@ -118,7 +111,7 @@ #' @param x vector of instrument names. e.g. c("SPY","DIA") #' @param env environment where xts data is stored #' @param silent silence warnings? -#' @return +#' @return #' An xts object with columns of #' Bid, Ask, Mid #' OR @@ -149,31 +142,31 @@ buildRatio <- function(x,env=.GlobalEnv, silent=FALSE) { if (inherits(x1,'try-error') || inherits(x2,'try-error')) { #maybe we can get the ratio directly if (!silent) warning(paste('Nothing to build. Returning data found in', deparse(substitute(env)),'if any.')) - return(.get_rate(x[1],x[2],env)) + return(.get_rate(x[1],x[2],env)) } - #!#---#!# + #!#---#!# Bi <- #This, or Bid, should be exported from quantmod - function (x) + function (x) { - if (has.Bid(x)) - return(x[,has.Bid(x,1)]) + if (has.Bid(x)) + return(x[,has.Bid(x,1)]) #return(x[, grep("Bid", colnames(x), ignore.case = TRUE)]) stop("subscript out of bounds: no column name containing \"Bid\"") } As <- #This, or Ask, should be exported from quantmod - function (x) + function (x) { - if (has.Ask(x)) - return(x[,has.Ask(x,1)]) + if (has.Ask(x)) + return(x[,has.Ask(x,1)]) #return(x[, grep("Ask", colnames(x), ignore.case = TRUE)]) stop("subscript out of bounds: no column name containing \"Ask\"") } - + Mid <- #This should be exported from quantmod - function (x) + function (x) { - if (has.Mid(x)) - return(x[,has.Mid(x,1)]) + if (has.Mid(x)) + return(x[,has.Mid(x,1)]) #return(x[, grep("Mid", colnames(x), ignore.case = TRUE)]) stop("subscript out of bounds: no column name containing \"Mid\"") } @@ -200,7 +193,7 @@ buildRatio <- function(x,env=.GlobalEnv, silent=FALSE) { bid <- Bi(x1)[,1]/As(x2)[,1] * mrat ask <- As(x1)[,1]/Bi(x2)[,1] * mrat if (has.Mid(x1) && has.Mid(x2)) { - mid <- Mid(x1)[,1] / Mid(x2)[,1] * mrat + mid <- Mid(x1)[,1] / Mid(x2)[,1] * mrat } else { mid <- ((Bi(x1)[,1]+As(x1)[,1])/2) / ((Bi(x2)[,1]+As(x2)[,1])/2) * mrat } @@ -208,7 +201,7 @@ buildRatio <- function(x,env=.GlobalEnv, silent=FALSE) { colnames(rat) <- paste(rat.sym,c('Bid','Ask','Mid'),sep='.') } else if (NCOL(x1) == 1 && NCOL(x2) == 1) { rat <- x1 / x2 * mrat #coredata(x1) / coredata(x2) - } else if (periodicity(x1)$frequency >= 86400) { + } else if (periodicity(x1)$frequency >= 86400) { #if daily or slower use OHLC and Mid if (is.OHLC(x1)) { #If first leg is.OHLC, 2nd leg will be univariate div <- if (NCOL(x2) == 1) { @@ -233,9 +226,9 @@ buildRatio <- function(x,env=.GlobalEnv, silent=FALSE) { for (i in 2:NCOL(x2)) { rat <- cbind(rat, mrat * num/x2[,i]) } - colnames(rat) <- colnames(x2) - } - } + colnames(rat) <- colnames(x2) + } + } } else if (periodicity(x1)$frequency < 86400) { #if intraday, use BAM and Cl if (is.BBO(x1)) { #1st leg is.BBO, 2nd leg will be univariate @@ -244,10 +237,10 @@ buildRatio <- function(x,env=.GlobalEnv, silent=FALSE) { } else if (has.Cl(x2)) { Cl(x2)[,1] } else if (has.Ad(x2)) { - Ad(x2)[,1] + Ad(x2)[,1] } else getPrice(x2)[,1] rat <- mrat * x1[,1] / div - if (NCOL(x1) > 1) { + if (NCOL(x1) > 1) { for (i in 2:NCOL(x1)) { rat <- cbind(rat, mrat * x1[,i]/div) } @@ -263,7 +256,7 @@ buildRatio <- function(x,env=.GlobalEnv, silent=FALSE) { rat <- mrat * num / x2[,1] if (NCOL(x2) > 1){ for (i in 2:NCOL(x2)) { - rat <- cbind(rat, mrat * num/x2[,i]) + rat <- cbind(rat, mrat * num/x2[,i]) } } } @@ -279,29 +272,29 @@ buildRatio <- function(x,env=.GlobalEnv, silent=FALSE) { #' #' Redenominate (change the base of) an instrument #' -#' If \code{old_base} is not provided, \code{x} must be the name of an +#' If \code{old_base} is not provided, \code{x} must be the name of an #' instrument (or an object with the name of a defined instrument) so that the #' currency attribute of the instrument can be used. Otherwise, \code{old_base} #' must be provided. #' #' If you want to convert to JPY something that is denominated in EUR, #' you must have data for the EURJPY (or JPYEUR) exchange rate. If you don't have -#' data for EURJPY, but you do have data for EURUSD and USDJPY, -#' you could \code{redenominate} to USD, then \code{redenominate} to EUR, +#' data for EURJPY, but you do have data for EURUSD and USDJPY, +#' you could \code{redenominate} to USD, then \code{redenominate} to EUR, #' but this function is not yet smart enough to do that for you. #' -#' See the help for buildRatio also. +#' See the help for buildRatio also. #' #' @param x can be either an xts object or the name of an instrument. #' @param new_base change the denomination to this; usually a currency. -#' @param old_base what is the current denomination? +#' @param old_base what is the current denomination? #' @param EOD_time If data need to be converted to daily, this is the time of day to take the observation. #' @param env environment that holds the data #' @param silent silence warnings? #' @return xts object, with as many columns as practicable, that represents the value of an instrument in a different currency (base). #' @author Garrett See #' @note this does not yet define any instruments or assign anything. -#' @seealso +#' @seealso #' \code{\link{buildRatio}} #' @examples #' @@ -325,10 +318,10 @@ redenominate <- function(x, new_base='USD', old_base=NULL, EOD_time='15:00:00', instr <- try(getInstrument(Symbol,silent=TRUE)) if (!is.instrument(instr)) { if (is.null(old_base)) stop(paste("If old_base is not provided, ", Symbol, ' must be defined.', sep="")) - mult <- 1 + mult <- 1 } else { if (is.null(old_base)) old_base <- instr$currency - mult <- as.numeric(instr$multiplier) + mult <- as.numeric(instr$multiplier) } if (is.character(x)) x <- get(Symbol,pos=env) } @@ -341,12 +334,12 @@ redenominate <- function(x, new_base='USD', old_base=NULL, EOD_time='15:00:00', rate <- buildRatio(c(old_base, new_base), env=env) #maybe it's not FX } } else rate <- xts(rep(1L, nrow(x)), index(x)) - + #!#---#!# Define function we'll need #This should be exported from quantmod Mid <- function (x) { - if (has.Mid(x)) - return(x[,has.Mid(x,1)]) + if (has.Mid(x)) + return(x[,has.Mid(x,1)]) #return(x[, grep("Mid", colnames(x), ignore.case = TRUE)]) stop("subscript out of bounds: no column name containing \"Mid\"") } @@ -363,7 +356,7 @@ redenominate <- function(x, new_base='USD', old_base=NULL, EOD_time='15:00:00', } else if(is.BBO(rate)) { if (periodicity(x)$scale == 'daily') { rate <- .to_daily(rate, EOD_time) #This doesn't make OHLC, the rest do. - } else rate <- to.period(Mid(rate)[,1], periodicity(x)$units) + } else rate <- to.period(Mid(rate)[,1], periodicity(x)$units) } else rate <- to.period(getPrice(rate)[,1], periodicity(x)$units) } @@ -386,7 +379,7 @@ redenominate <- function(x, new_base='USD', old_base=NULL, EOD_time='15:00:00', rsym <- new_base assign(rsym,rate,pos=tmpenv) assign(Symbol,x,pos=tmpenv) - + buildRatio(c(Symbol,rsym),env=tmpenv, silent=TRUE) / mult #TODO: colnames #TODO: auto.assign @@ -395,16 +388,16 @@ redenominate <- function(x, new_base='USD', old_base=NULL, EOD_time='15:00:00', #dailyConvertFX <- function(xts_obj, rate, prefer=NULL, EOD_time="11:00:00", verbose=TRUE) { # #to convert a EUR denominated asset from EUR to USD, rate=EURUSD -# #DAX closes at 11:45 EDT or 10:45 Chicago time +# #DAX closes at 11:45 EDT or 10:45 Chicago time # #FRED data is noon EDT or 11:00:00 Chicago time. # if (periodicity(xts_obj)$scale != "daily") stop('xts_obj must be daily') -# rate <- getPrice(rate, prefer=prefer) +# rate <- getPrice(rate, prefer=prefer) # tmpdt <- as.Date(index(rate[1:2,])) # if (tmpdt[1] == tmpdt[2]) { #intraday data # if (verbose) warning('converting rate to daily') -# rate <- .to_daily(rate, EOD_time) +# rate <- .to_daily(rate, EOD_time) # rate <- rate[paste(start(xts_obj), end(xts_obj), sep="/")] -# } +# } # df <- cbind(rate, xts_obj, all=TRUE) # df <- df[paste(max(start(rate),start(xts_obj)), '::', sep="")] # if (verbose && (NROW(df) < NROW(xts_obj))) warning('Data removed where rate was missing') diff --git a/R/saveInstruments.R b/R/saveInstruments.R index 24a539d..e37cec5 100644 --- a/R/saveInstruments.R +++ b/R/saveInstruments.R @@ -2,7 +2,7 @@ # R (http://r-project.org/) Instrument Class Model # # Copyright (c) 2009-2012 -# Peter Carl, Dirk Eddelbuettel, Jeffrey Ryan, +# Peter Carl, Dirk Eddelbuettel, Jeffrey Ryan, # Joshua Ulrich, Brian G. Peterson, and Garrett See # # This library is distributed under the terms of the GNU Public License (GPL) @@ -14,29 +14,29 @@ #' Save and Load all instrument definitions -#' +#' #' Saves (loads) the .instrument environment to (from) disk. -#' +#' #' After you have defined some instruments, you can use \code{saveInstruments} #' to save the entire .instrument environment to disk. #' -#' \code{loadInstruments} will read a file that contains instruments and add -#' those instrument definitions to your .instrument environment. -#' \code{reloadInstruments} will remove all instruments in the current +#' \code{loadInstruments} will read a file that contains instruments and add +#' those instrument definitions to your .instrument environment. +#' \code{reloadInstruments} will remove all instruments in the current #' .instrument environment before loading instruments from disk. -#' -#' The \code{file_name} should have a file extension of \dQuote{RData}, +#' +#' The \code{file_name} should have a file extension of \dQuote{RData}, #' \dQuote{rda}, \dQuote{R}, or \dQuote{txt}. If the \code{file_name} does not -#' end with one of those, \dQuote{.RData} will be appended to the +#' end with one of those, \dQuote{.RData} will be appended to the #' \code{file_name} -#' +#' #' If the file extension is \dQuote{R} or \dQuote{txt}, \code{saveInstruments} -#' will create a text file of \R code that can be \code{\link{source}}d to +#' will create a text file of \R code that can be \code{\link{source}}d to #' load instruments back into the .instrument environment. -#' +#' #' @aliases saveInstruments loadInstruments -#' @param file_name name of file. e.g. \dQuote{MyInstruments.RData}. -#' As an experimental feature, a \code{list} or \code{environment} can be passed +#' @param file_name name of file. e.g. \dQuote{MyInstruments.RData}. +#' As an experimental feature, a \code{list} or \code{environment} can be passed #' to \code{file_name}. #' @param dir Directory of file (defaults to current working directory. ie. "") #' @param compress argument passed to \code{\link{save}}, default is "gzip" @@ -45,53 +45,116 @@ #' @seealso save, load load.instrument define_stocks, define_futures, #' define_options (option_series.yahoo) #' @examples -#' \dontrun{ -#' stock("SPY", currency("USD"), 1) -#' tmpdir <- tempdir() -#' saveInstruments("MyInstruments.RData", dir=tmpdir) -#' rm_instruments(keep.currencies=FALSE) -#' loadInstruments("MyInstruments.RData", dir=tmpdir) -#' # write .R file that can be sourced -#' saveInstruments("MyInstruments.R", dir=tmpdir) -#' rm_instruments(keep.currencies=FALSE) -#' loadInstruments("MyInstruments.R", dir=tmpdir) -#' #source(file=paste(tmpdir, "MyInstruments.R", sep="/")) # same -#' unlink(tmpdir, recursive=TRUE) -#' } -#' @export +#' +#' backup_file <- tempfile(fileext = ".RData") +#' output_dir <- tempfile("fi-save-test-") +#' dir.create(output_dir) +#' +#' saveInstruments(backup_file) +#' +#' tryCatch( +#' { +#' rm_instruments(keep.currencies = FALSE) +#' +#' currency("USD") +#' stock("SPY", currency = "USD") +#' +#' saveInstruments( +#' "test-instruments.R", +#' dir = output_dir +#' ) +#' +#' rm_instruments(keep.currencies = FALSE) +#' +#' source( +#' file.path(output_dir, "test-instruments.R") +#' ) +#' +#' stopifnot(is.instrument.name("SPY")) +#' stopifnot(getInstrument("SPY")$currency == "USD") +#' }, +#' finally = { +#' reloadInstruments(backup_file) +#' unlink(backup_file) +#' unlink(output_dir, recursive = TRUE) +#' } +#' ) +#' +#' @export #' @rdname saveInstruments -saveInstruments <- function(file_name="MyInstruments", dir="", compress="gzip") { - if (!is.null(dir) && !dir == "" && substr(dir,nchar(dir),nchar(dir)) != "/") - dir <- paste(dir,"/",sep="") - ssfn <- strsplit(file_name, "\\.")[[1]] - extension <- if (tolower(tail(ssfn, 1)) %in% c('rda', 'rdata', 'r', 'txt')) { - file_name <- paste(ssfn[1:(length(ssfn)-1)], collapse=".") - tail(ssfn, 1) - } else "RData" - file.name <- paste(dir, file_name, ".", extension, sep="") - # if extension is "R", "r", or "txt" create a file that can be sourced - if (tolower(extension) %in% c("r", "txt")) { - cat("#auto-generated by FinancialInstrument:::saveInstruments\n\n", - "require(FinancialInstrument)\n\n", file=file.name) - for (s in ls_instruments()) { - sink(file.name, append=TRUE) - cat('assign("', s, '", pos=FinancialInstrument:::.instrument, ', - 'value=\n', sep="", append=TRUE) - dput(getInstrument(s)) - cat(")\n\n", append=TRUE) - sink() - #system(paste("cat", file.name)) #for debugging - } - } else save(.instrument, file = file.name, compress=compress) -} +saveInstruments <- function(file_name = "MyInstruments", + dir = "", + compress = "gzip") { + + ssfn <- strsplit(file_name, "\\.")[[1]] + + extension <- if ( + tolower(tail(ssfn, 1L)) %in% c("rda", "rdata", "r", "txt") + ) { + file_name <- paste( + ssfn[seq_len(length(ssfn) - 1L)], + collapse = "." + ) + tail(ssfn, 1L) + } else { + "RData" + } + output_name <- paste0(file_name, ".", extension) + file.name <- if (is.null(dir) || identical(dir, "")) { + output_name + } else { + file.path(dir, output_name) + } + + if (tolower(extension) %in% c("r", "txt")) { + con <- file(file.name, open = "wt") + on.exit(close(con), add = TRUE) + + writeLines( + c( + "# Auto-generated by FinancialInstrument::saveInstruments", + "" + ), + con + ) + + for (s in ls_instruments()) { + writeLines( + "FinancialInstrument::loadInstruments(list(", + con + ) + + dput( + getInstrument(s), + file = con + ) + + writeLines( + c( + "))", + "" + ), + con + ) + } + } else { + save( + .instrument, + file = file.name, + compress = compress + ) + } + + invisible(NULL) +} #' @export #' @rdname saveInstruments loadInstruments <-function(file_name="MyInstruments", dir="") { if (is.environment(file_name) || is.list(file_name)) { ilist <- as.list(file_name) - if (!all(vapply(ilist, function(x) length(x[["primary_id"]]) == 1L, + if (!all(vapply(ilist, function(x) length(x[["primary_id"]]) == 1L, TRUE))) { stop("all instruments must have exactly one primary_id") } @@ -110,7 +173,8 @@ loadInstruments <-function(file_name="MyInstruments", dir="") { } else "RData" file.name <- paste(dir, file_name, ".", extension, sep="") if (tolower(extension) %in% c("r", "txt")) { - if (substr(readLines(file.name, 1L), 1, 5) != "#auto") { + first_line <- tolower(readLines(file.name, 1L)) + if (!startsWith(gsub("[[:space:]]", "", first_line), "#auto")) { warning(paste(file.name, "was not created by 'saveInstruments'")) } source(file.name) @@ -119,7 +183,7 @@ loadInstruments <-function(file_name="MyInstruments", dir="") { load(paste(dir,file_name,".",extension,sep=""),envir=tmpenv) il <- ls(tmpenv$.instrument, all.names=TRUE) for (i in il) { - assign(i, tmpenv$.instrument[[i]], + assign(i, tmpenv$.instrument[[i]], pos=.instrument, inherits=FALSE) } } diff --git a/R/saveSymbols.R b/R/saveSymbols.R index ae34186..7f04bc1 100644 --- a/R/saveSymbols.R +++ b/R/saveSymbols.R @@ -2,7 +2,7 @@ # R (http://r-project.org/) Instrument Class Model # # Copyright (c) 2009-2012 -# Peter Carl, Dirk Eddelbuettel, Jeffrey Ryan, +# Peter Carl, Dirk Eddelbuettel, Jeffrey Ryan, # Joshua Ulrich, Brian G. Peterson, and Garrett See # # This library is distributed under the terms of the GNU Public License (GPL) @@ -14,13 +14,13 @@ #' Save data to disk #' -#' Save data to disk the way that \code{getSymbols.FI} +#' Save data to disk the way that \code{getSymbols.FI} #' expects it to be saved. #' -#' If they do not already exist, subdirectories will be created for each of the -#' \code{Symbols}. \code{saveSymbols.common} will save a single \sQuote{rda} +#' If they do not already exist, subdirectories will be created for each of the +#' \code{Symbols}. \code{saveSymbols.common} will save a single \sQuote{rda} #' file for each of the \code{Symbols} in that symbol's subdirectory. -#' \code{saveSymbols.days} will split the data up into days and save a separate +#' \code{saveSymbols.days} will split the data up into days and save a separate #' \sQuote{rda} file for each day in that symbol's subdirectory. #' #' @param Symbols character vector of names of objects to be saved @@ -30,26 +30,53 @@ #' @return called for side-effect. #' @seealso \code{\link{getSymbols.FI}} #' @examples -#' \dontrun{ -#' getSymbols("SPY", src='yahoo') -#' dir.create("tmpdata") -#' saveSymbols.common("SPY", base_dir="tmpdata") -#' rm("SPY") -#' getSymbols("SPY", src='FI', dir="tmpdata", split_method='common') -#' unlink("tmpdata/SPY", recursive=TRUE) -#' } +#' example_dir <- tempfile("fi-symbol-data-") +#' dir.create(example_dir) +#' +#' example_env <- new.env() +#' +#' example_env$SPY <- xts::xts( +#' matrix( +#' 1:8, +#' ncol = 2L, +#' dimnames = list( +#' NULL, +#' c("Close", "Volume") +#' ) +#' ), +#' order.by = as.Date("2020-01-01") + 0:3 +#' ) +#' +#' tryCatch( +#' { +#' saveSymbols.common( +#' "SPY", +#' base_dir = example_dir, +#' env = example_env +#' ) +#' +#' list.files( +#' example_dir, +#' recursive = TRUE +#' ) +#' }, +#' finally = { +#' unlink(example_dir, recursive = TRUE) +#' } +#' ) +#' #' @export #' @rdname saveSymbols.days saveSymbols.days <- function(Symbols, base_dir="", extension="rda", env=.GlobalEnv) { - if (base_dir != "" && - !is.null(base_dir) && - substr(base_dir,nchar(base_dir),nchar(base_dir)) != "/") - base_dir <- paste(base_dir,"/",sep="") - tmpenv <- new.env() + if (base_dir != "" && + !is.null(base_dir) && + substr(base_dir,nchar(base_dir),nchar(base_dir)) != "/") + base_dir <- paste(base_dir,"/",sep="") + tmpenv <- new.env() for (symbol in Symbols) { - tmp <- try(get(symbol,pos=env), silent=TRUE) - if (!is.null(tmp) && !inherits(tmp,'try-error')) { + tmp <- try(get(symbol,pos=env), silent=TRUE) + if (!is.null(tmp) && !inherits(tmp,'try-error')) { D <- split(tmp,'days') if (!file.exists(paste(base_dir,symbol,sep=""))) dir.create(paste(base_dir,symbol,sep="")) fnames <- paste(base_dir,symbol,"/", @@ -65,16 +92,16 @@ function(Symbols, base_dir="", extension="rda", env=.GlobalEnv) { #' @export #' @rdname saveSymbols.days -saveSymbols.common <- function (Symbols, base_dir = "", extension="rda", env = .GlobalEnv) +saveSymbols.common <- function (Symbols, base_dir = "", extension="rda", env = .GlobalEnv) { - if (base_dir != "" && !is.null(base_dir) && substr(base_dir, - nchar(base_dir), nchar(base_dir)) != "/") + if (base_dir != "" && !is.null(base_dir) && substr(base_dir, + nchar(base_dir), nchar(base_dir)) != "/") base_dir <- paste(base_dir, "/", sep = "") tmpenv <- new.env() for (symbol in Symbols) { tmp <- try(get(symbol, pos = env), silent=TRUE) if (!is.null(tmp) && !inherits(tmp, "try-error")) { - if (!file.exists(paste(base_dir, symbol, sep = ""))) + if (!file.exists(paste(base_dir, symbol, sep = ""))) dir.create(paste(base_dir, symbol, sep = "")) fnames <- paste(base_dir, symbol, "/", symbol, ".", extension, sep = "") assign(symbol, tmp, envir = tmpenv) diff --git a/R/update_instruments.morningstar.R b/R/update_instruments.morningstar.R index 3b455e5..36e507b 100644 --- a/R/update_instruments.morningstar.R +++ b/R/update_instruments.morningstar.R @@ -1,27 +1,15 @@ #' Update instrument metadata for ETFs -#' +#' #' Currently, this only updates ETFs. It will add \dQuote{msName} and #' \dQuote{msCategory} attributes to the instruments. (ms for morningstar) #' @param Symbols character vector of Symbols of ETFs #' @param silent silence warnings? -#' @return called for side-effect. +#' @return called for side-effect. #' @author Garrett See #' @references \url{http://www.morningstar.com} -#' @seealso \code{\link{update_instruments.yahoo}}, +#' @seealso \code{\link{update_instruments.yahoo}}, #' \code{\link{update_instruments.TTR}} #' \code{\link{update_instruments.iShares}} -#' @examples -#' \dontrun{ -#' ## backup .instrument environment -#' ibak <- as.list(FinancialInstrument:::.instrument) -#' rm_instruments() -#' stock(s <- c("SPY", "USO", "LQD"), currency("USD")) -#' update_instruments.morningstar(s) -#' instrument.table(s) -#' ## cleanup and restore instrument environment -#' rm_instruments(keep.currencies=FALSE) -#' loadInstruments(ibak) -#' } #' @export update_instruments.morningstar <- function(Symbols, silent=FALSE) { if(!requireNamespace("XML", quietly=TRUE)) @@ -44,7 +32,7 @@ update_instruments.morningstar <- function(Symbols, silent=FALSE) { s <- Symbols[Symbols %in% tickers] if (length(s) > 0) { # only those that inherit stock or fund - s <- s[sapply(lapply(s, getInstrument, type=c("stock", "fund"), + s <- s[sapply(lapply(s, getInstrument, type=c("stock", "fund"), silent = TRUE), is.instrument)] } if (length(s) == 0) { @@ -58,10 +46,10 @@ update_instruments.morningstar <- function(Symbols, silent=FALSE) { for (i in 1:NROW(x)) { instrument_attr(rn[i], "msName", x$Name[i]) instrument_attr(rn[i], "msCategory", x$Category[i]) - #instrument_attr(x$Symbol[i], "msTradingVolume", + #instrument_attr(x$Symbol[i], "msTradingVolume", # as.numeric(gsub(",", "", x$TradingVolume[i]))) db <- getInstrument(rn[i])$defined.by - instrument_attr(rn[i], "defined.by", paste(c(db, "morningstar"), + instrument_attr(rn[i], "defined.by", paste(c(db, "morningstar"), collapse=";")) instrument_attr(rn[i], "updated", Sys.time()) } diff --git a/R/update_instruments.yahoo.R b/R/update_instruments.yahoo.R index 1bef6ca..a71ce20 100644 --- a/R/update_instruments.yahoo.R +++ b/R/update_instruments.yahoo.R @@ -38,7 +38,7 @@ #' \code{\link{update_instruments.masterDATA}}, #' \code{\link[TTR]{stockSymbols}}, \code{\link{stock}} #' @references Yahoo! Finance "finance.yahoo.com" and YahooQuote -#' \url{https://dirk.eddelbuettel.com/code/yahooquote.html} +#' #' @examples #' \dontrun{ #' stock('GS',currency('USD')) diff --git a/R/volep.R b/R/volep.R index 743593c..ab76445 100644 --- a/R/volep.R +++ b/R/volep.R @@ -1,12 +1,13 @@ - - #' generate endpoints for volume bars #' @param x time series containing 'Volume' column #' @param units volume sum to mark for bars +#' @return An integer vector containing row positions where cumulative volume +#' crosses successive multiples of `units`. These positions may be used as +#' endpoints for constructing volume bars. #' @author Joshua Ulrich #' @export volep <- function (x, units){ - incepSum <- runSum(Vo(x),1,TRUE) + incepSum <- runSum(Vo(x),1,TRUE) bins <- incepSum %/% units bins[1]<-0 ep<-which(diff(bins)!=0) diff --git a/cran-comments.md b/cran-comments.md index 43233d9..b3e12f8 100644 --- a/cran-comments.md +++ b/cran-comments.md @@ -1,29 +1,47 @@ ## Test environments -* Local Ubuntu Linux (22.04 and 24.04), R 4.6.1 (2026-06-24) -* win-builder (R release and R-devel) +* Local Ubuntu 22.04, R 4.6.1 +* Local Ubuntu 24.04, R 4.6.1 +* GitHub Actions, Ubuntu, R release +* win-builder, R release +* win-builder, R-devel ## R CMD check results -0 errors | 0 warnings | 0 notes - -## Submission Notes - -This is a resubmission of the previously archived package **FinancialInstrument**. The issues that led to archival have been addressed. - -Ross Bennett, the previous CRAN maintainer, has agreed to the maintainer transition and has sent confirmation directly to CRAN. - -The following changes were made for this release: - -* Justin M. Shea is now the package maintainer, and the `DESCRIPTION` file has been updated accordingly. -* Updated legacy roxygen2 documentation blocks across multiple source files (`instrument.R`, `ls_by_currency.R`, `ls_instruments.R`, and `FinancialInstrument-package.R`) to comply with current roxygen2 parsing requirements, including `@aliases` and `@importFrom` directives. -* Corrected S3 generic/method registration for `expires.spread`. -* Added the appropriate package-qualified Rd cross-reference for `quantmod::setSymbolLookup`. -* Added `Encoding: UTF-8` to the package metadata. -* Removed hyperlinks to Yahoo Finance from the documentation because Yahoo returns HTTP 429 responses during automated URL checks. - -Additional improvements made during the update include: - -* Migrated the testing framework from `testthat` to `tinytest`. -* Expanded test coverage for frequency-mixed `xts` time-series alignment. -* Added a `README.md` and GitHub Actions continuous integration workflows for automated package checks. +0 errors | 0 warnings | 1 note + +The remaining NOTE reports that FinancialInstrument was previously archived on +CRAN. This is expected for this resubmission. + +## Resubmission + +This is a resubmission of FinancialInstrument 1.4.1 addressing the reviewer +feedback received after the 1.4.0 submission. + +The following changes were made: + +* Added missing return-value documentation to exported functions and regenerated +the package manuals. +* Corrected broken and incomplete examples. +* Removed commented-out executable alternatives from examples. +* Replaced unnecessary `\dontrun{}` sections with executable examples or + `\donttest{}` where an external service is required. +* Updated examples that write files to use temporary directories and restore + instrument-registry state. +* Removed direct `FinancialInstrument:::.instrument` access from documentation + examples. +* Confirmed that `expires()` is exported. +* Restored and tested the previously exported `rm_by_currency()` function. +* Made progress output from `alltick2sec()` suppressible through a new trailing + `verbose = FALSE` argument. +* Removed default writes to fixed locations in the user’s home directory while + preserving calls that provide explicit paths. +* Added regression tests for instrument saving, loading, registry restoration, +and currency-based removal. + +FinancialInstrument provides general infrastructure for financial-instrument +metadata and does not implement a methodology associated with a specific +publication. Therefore, no DOI, ISBN, or methodological reference applies. + +Ross Bennett, the previous maintainer, agreed to the maintainer transition and +provided confirmation directly to CRAN. diff --git a/inst/tinytest/test_rm_by_currency.R b/inst/tinytest/test_rm_by_currency.R new file mode 100644 index 0000000..fe8ba28 --- /dev/null +++ b/inst/tinytest/test_rm_by_currency.R @@ -0,0 +1,78 @@ +original_names <- ls_instruments() + +original_instruments <- if (length(original_names)) { + unname(lapply(original_names, getInstrument)) +} else { + list() +} + +on.exit( + { + rm_instruments( + keep.currencies = FALSE + ) + + if (length(original_instruments)) { + loadInstruments(original_instruments) + } + }, + add = TRUE +) + +rm_instruments( + keep.currencies = FALSE +) + +currency( + c("USD", "CAD") +) + +stock( + "USD_STOCK", + currency = "USD" +) + +stock( + "CAD_STOCK", + currency = "CAD" +) + +expect_true( + "rm_by_currency" %in% + getNamespaceExports("FinancialInstrument") +) + +rm_by_currency( + currency = "CAD" +) + +expect_true( + is.instrument.name("USD_STOCK") +) + +expect_false( + is.instrument.name("CAD_STOCK") +) + +# Currency definition should remain by default. +expect_true( + is.instrument.name("CAD") +) + +stock( + "CAD_STOCK", + currency = "CAD" +) + +rm_by_currency( + currency = "CAD", + keep.currencies = FALSE +) + +expect_false( + is.instrument.name("CAD_STOCK") +) + +expect_false( + is.instrument.name("CAD") +) diff --git a/inst/tinytest/test_saveInstruments.R b/inst/tinytest/test_saveInstruments.R new file mode 100644 index 0000000..db2f05e --- /dev/null +++ b/inst/tinytest/test_saveInstruments.R @@ -0,0 +1,168 @@ +# Regression tests for saveInstruments(), loadInstruments(), +# and reloadInstruments(). + +original_names <- ls_instruments() + +original_instruments <- if (length(original_names)) { + unname(lapply(original_names, getInstrument)) +} else { + list() +} + +f_rdata <- tempfile( + "fi_test_instr_", + fileext = ".RData" +) + +f_r <- tempfile( + "fi_test_instr_", + fileext = ".R" +) + +f_dir <- tempfile( + "fi_test_dir_", + fileext = ".RData" +) + +f_reload <- tempfile( + "fi_test_reload_", + fileext = ".RData" +) + +test_files <- c( + f_rdata, + f_r, + f_dir, + f_reload +) + +on.exit( + { + unlink(test_files) + + rm_instruments( + keep.currencies = FALSE + ) + + if (length(original_instruments)) { + loadInstruments(original_instruments) + } + }, + add = TRUE +) + +rm_instruments( + keep.currencies = FALSE +) + +currency("USD") + +stock( + "SAVE_SPY", + currency = "USD", + multiplier = 1 +) + +stock( + "SAVE_DIA", + currency = "USD", + multiplier = 1 +) + +expected_spy <- getInstrument("SAVE_SPY") +expected_dia <- getInstrument("SAVE_DIA") + +# RData round trip +saveInstruments(f_rdata) + +expect_true( + file.exists(f_rdata) +) + +rm_instruments( + keep.currencies = FALSE +) + +loadInstruments(f_rdata) + +expect_equal( + getInstrument("SAVE_SPY"), + expected_spy +) + +expect_equal( + getInstrument("SAVE_DIA"), + expected_dia +) + +# Generated R-file round trip +saveInstruments(f_r) + +expect_true( + file.exists(f_r) +) + +rm_instruments( + keep.currencies = FALSE +) + +expect_silent( + loadInstruments(f_r) +) + +expect_equal( + getInstrument("SAVE_SPY"), + expected_spy +) + +expect_equal( + getInstrument("SAVE_DIA"), + expected_dia +) + +# Separate filename and directory arguments +saveInstruments( + basename(f_dir), + dir = dirname(f_dir) +) + +expect_true( + file.exists(f_dir) +) + +# reloadInstruments() should replace the registry +rm_instruments( + keep.currencies = FALSE +) + +currency("USD") + +stock( + c("SAVE_AA", "SAVE_BB"), + currency = "USD" +) + +saveInstruments(f_reload) + +stock( + "SAVE_CC", + currency = "USD" +) + +expect_true( + is.instrument.name("SAVE_CC") +) + +reloadInstruments(f_reload) + +expect_false( + is.instrument.name("SAVE_CC") +) + +expect_true( + is.instrument.name("SAVE_AA") +) + +expect_true( + is.instrument.name("SAVE_BB") +) diff --git a/man/CompareInstrumentFiles.Rd b/man/CompareInstrumentFiles.Rd index d4ee2f9..82f4445 100644 --- a/man/CompareInstrumentFiles.Rd +++ b/man/CompareInstrumentFiles.Rd @@ -9,8 +9,8 @@ CompareInstrumentFiles(file1, file2, ...) \arguments{ \item{file1}{A file containing an instrument environment} -\item{file2}{Another file containing an instrument environment. If not -provided, \code{file1} will be compared against the currently loaded +\item{file2}{Another file containing an instrument environment. If not +provided, \code{file1} will be compared against the currently loaded instrument environment.} \item{...}{Arguments to pass to \code{\link{all.equal.instrument}}} @@ -24,34 +24,60 @@ A list that contains the names of all instruments that were added, Compare the .instrument environments of two files } \details{ -This will load two instrument files (created by -\code{\link{saveInstruments}}) and find the differences between them. In -addition to returning a list of difference that are found, it will produce -messages indicating the number of instruments that were added, the number of -instruments that were removed, and the number of instruments that are +This will load two instrument files (created by +\code{\link{saveInstruments}}) and find the differences between them. In +addition to returning a list of difference that are found, it will produce +messages indicating the number of instruments that were added, the number of +instruments that were removed, and the number of instruments that are different. } \examples{ -\dontrun{ -#backup current .instrument environment -bak <- as.list(FinancialInstrument:::.instrument, all.names=TRUE) -old.wd <- getwd() -tmpdir <- tempdir() -setwd(tmpdir) -rm_instruments(keep=FALSE) -# create some instruments and save -stock(c("SPY", "DIA", "GLD"), currency("USD")) -saveInstruments("MyInstruments1") -# make some changes -rm_stocks("GLD") -stock("QQQ", "USD") -instrument_attr("SPY", "description", "S&P ETF") -saveInstruments("MyInstruments2") -CompareInstrumentFiles("MyInstruments1", "MyInstruments2") -#Clean up -setwd(old.wd) -reloadInstruments(bak) -} +example_dir <- tempfile("fi-compare-") +dir.create(example_dir) + +backup_name <- "backup.RData" +file1_name <- "instruments1.RData" +file2_name <- "instruments2.RData" + +backup_path <- file.path(example_dir, backup_name) +file1_path <- file.path(example_dir, file1_name) +file2_path <- file.path(example_dir, file2_name) + +saveInstruments(backup_name, dir = example_dir) + +tryCatch( + { + stopifnot(file.exists(backup_path)) + + rm_instruments(keep.currencies = FALSE) + currency("USD") + stock(c("SPY", "DIA", "GLD"), currency = "USD") + saveInstruments(file1_name, dir = example_dir) + + stopifnot(file.exists(file1_path)) + + rm_stocks("GLD") + stock("QQQ", currency = "USD") + instrument_attr( + "SPY", + "description", + "S&P 500 ETF" + ) + saveInstruments(file2_name, dir = example_dir) + + stopifnot(file.exists(file2_path)) + + CompareInstrumentFiles(file1_path, file2_path) + }, + finally = { + if (file.exists(backup_path)) { + reloadInstruments(backup_name, dir = example_dir) + } + + unlink(example_dir, recursive = TRUE) + } +) + } \seealso{ \code{\link{saveInstruments}}, \code{\link{all.equal.instrument}} diff --git a/man/FinancialInstrument-package.Rd b/man/FinancialInstrument-package.Rd index dfeadce..e4f7d5f 100644 --- a/man/FinancialInstrument-package.Rd +++ b/man/FinancialInstrument-package.Rd @@ -89,11 +89,11 @@ You may want to use \code{\link{setSymbolLookup.FI}} to define where and how your market data are stored so that \code{\link[quantmod]{getSymbols}} will work for you. -FinancialInstrument's functions build and manipulate objects that are stored -in an environment named ".instrument" at the top level of the package -(i.e. "FinancialInstrument:::.instrument") rather than the global -environment, \code{.GlobalEnv}. Objects may be listed using -\code{ls_instruments()} (or many other ls_* functions). +FinancialInstrument stores instrument definitions in an internal +package-level environment named `.instrument`, rather than in +`.GlobalEnv`. Users should access instrument definitions through exported +functions such as `getInstrument()`, `ls_instruments()`, +`saveInstruments()`, and `loadInstruments()`. We store instruments in their own environment for two reasons. First, it keeps the user's workspace less cluttered and lowers the probability of @@ -107,7 +107,8 @@ particular instrument from the environment. \seealso{ Useful links: \itemize{ - \item \url{https://github.com/braverock/FinancialInstrument} + \item \url{https://github.com/JustinMShea/FinancialInstrument} + \item Report bugs at \url{https://github.com/JustinMShea/FinancialInstrument/issues} } } diff --git a/man/FindCommonInstrumentAttributes.Rd b/man/FindCommonInstrumentAttributes.Rd index 0450e7a..079b6f7 100644 --- a/man/FindCommonInstrumentAttributes.Rd +++ b/man/FindCommonInstrumentAttributes.Rd @@ -9,11 +9,11 @@ FindCommonInstrumentAttributes(Symbols, ...) \arguments{ \item{Symbols}{character vector of primary_ids of instruments} -\item{\dots}{arguments to pass to +\item{\dots}{arguments to pass to \code{\link[FinancialInstrument]{getInstrument}}} } \value{ -character vector of names of attributes that all \code{Symbols}' +character vector of names of attributes that all \code{Symbols}' instruments have in common } \description{ @@ -23,18 +23,6 @@ Find attributes that more than one instrument have in common I really do not like the name of this function, so if it survives, its name may change } -\examples{ -\dontrun{ -ibak <- as.list(FinancialInstrument:::.instrument, all.names=TRUE) -Symbols <- c("SPY", "AAPL") -define_stocks(Symbols, addIBslot=FALSE) -update_instruments.SPDR("SPY") -update_instruments.TTR("AAPL", exchange="NASDAQ") -FindCommonInstrumentAttributes(Symbols) -FindCommonInstrumentAttributes(c(Symbols, "USD")) -reloadInstruments(ibak) -} -} \author{ gsee } diff --git a/man/Tick2Sec.Rd b/man/Tick2Sec.Rd index cd27528..3f1233e 100644 --- a/man/Tick2Sec.Rd +++ b/man/Tick2Sec.Rd @@ -8,57 +8,63 @@ to_secBATV(x) alltick2sec( - getdir = "~/TRTH/tick/", - savedir = "~/TRTH/sec/", - Symbols = list.files(getdir), - overwrite = FALSE + getdir = getOption("FinancialInstrument.tickdir", NULL), + savedir = getOption("FinancialInstrument.secdir", NULL), + Symbols = NULL, + overwrite = FALSE, + verbose = FALSE ) } \arguments{ \item{x}{the xts series to convert to 1 minute BATV} -\item{getdir}{Directory that contains tick data} +\item{getdir}{Directory containing tick data. If omitted, the value of +\code{getOption("FinancialInstrument.tickdir")} is used.} -\item{savedir}{Directory in which to save converted data} +\item{savedir}{Directory in which converted data will be saved. If omitted, +the value of \code{getOption("FinancialInstrument.secdir")} is used.} -\item{Symbols}{String names of instruments to convert} +\item{Symbols}{Character vector naming instruments to convert. If +\code{NULL}, all entries in \code{getdir} are considered.} -\item{overwrite}{TRUE/FALSE. If file already exists in savedir, should it be -overwritten?} +\item{overwrite}{Logical. If a destination file already exists, should it +be overwritten?} + +\item{verbose}{Logical. If \code{TRUE}, display progress messages.} } \value{ \code{to_secBATV} returns an xts object of one second frequency. \code{alltick2sec} returns a list of files that were converted. } \description{ -This is like taking a snapshot of the market at the end of every second, +This is like taking a snapshot of the market at the end of every second, except the volume over the second is summed. } \details{ -From tick data with columns: \dQuote{Price}, \dQuote{Volume}, -\dQuote{Bid.Price}, \dQuote{Bid.Size}, \dQuote{Ask.Price}, \dQuote{Ask.Size}, -to data of one second frequency with columns \dQuote{Bid.Price}, +From tick data with columns: \dQuote{Price}, \dQuote{Volume}, +\dQuote{Bid.Price}, \dQuote{Bid.Size}, \dQuote{Ask.Price}, \dQuote{Ask.Size}, +to data of one second frequency with columns \dQuote{Bid.Price}, \dQuote{Bid.Size}, \dQuote{Ask.Price}, \dQuote{Ask.Size}, \dQuote{Trade.Price}, and \dQuote{Volume} -The primary purpose of these functions is to reduce the amount of data on +The primary purpose of these functions is to reduce the amount of data on disk so that it will take less time to load the data into memory. -If there are no trades or bid/ask price updates in a given second, we will -not make a row for that timestamp. If there were no trades, but the bid or -ask price changed, then we _will_ have a row but the Volume and Trade.Price -will be NA. +If there are no trades or bid/ask price updates in a given second, we will +not make a row for that timestamp. If there were no trades, but the bid or +ask price changed, then we _will_ have a row but the Volume and Trade.Price +will be NA. -If there are multiple trades in the same second, Volume will be the sum of -the volume, but only the last trade price in that second will be printed. -Similarly, if there is a trade, and then later in the same second, there is +If there are multiple trades in the same second, Volume will be the sum of +the volume, but only the last trade price in that second will be printed. +Similarly, if there is a trade, and then later in the same second, there is a bid/ask update, the last Bid/Ask Price/Size will be used. -\code{alltick2sec} is used to convert the data of several files from tick to +\code{alltick2sec} is used to convert the data of several files from tick to one second frequency data. } \note{ -\code{to_secBATV} is used by the TRTH_BackFill.R script in the +\code{to_secBATV} is used by the TRTH_BackFill.R script in the inst/parser directory of the FinancialInstrument package. These functions are specific to to data created by that script and are not intended for more general use. @@ -67,7 +73,11 @@ one second frequency data. \dontrun{ getSymbols("CLU1") system.time(xsec <- to_secBATV(CLU1)) -convert.log <- alltick2sec() +convert.log <- alltick2sec( + getdir = "path/to/tick-data", + savedir = "path/to/second-data", + verbose = TRUE +) } } \author{ diff --git a/man/all.equal.instrument.Rd b/man/all.equal.instrument.Rd index 509f2ea..2affc0c 100644 --- a/man/all.equal.instrument.Rd +++ b/man/all.equal.instrument.Rd @@ -7,27 +7,30 @@ \method{all.equal}{instrument}(target, current, char.n = 2, collapse = ";", ...) } \arguments{ -\item{char.n}{If length of a character vector is \code{char.n} or less it +\item{char.n}{If length of a character vector is \code{char.n} or less it will be treated as a single element. A negative value for \code{char.n} will be treated as if it were positive \code{Inf}.} -\item{collapse}{Only used if a character vector is of length less than -\code{char.n}. Unless \code{collapse} is \code{NULL}, it will be used in a -call to \code{\link{paste}}. If \code{collapse} is \code{NULL}, each element +\item{collapse}{Only used if a character vector is of length less than +\code{char.n}. Unless \code{collapse} is \code{NULL}, it will be used in a +call to \code{\link{paste}}. If \code{collapse} is \code{NULL}, each element of the character vector will be compared separately.} } +\value{ +`TRUE` when `target` and `current` are equal. Otherwise, a character +vector describing differences between the two objects. +} \description{ -This is most useful for seeing the difference between two \code{instrument} +This is most useful for seeing the difference between two \code{instrument} objects. } \note{ ALPHA code. Subject to change } \examples{ -\dontrun{ currency("USD") stock("SPY", "USD", validExchanges=c("SMART", "ARCA", "BATS", "BEX")) -stock("DIA", "USD", validExchanges=c("SMART", "ARCA", "ISLAND"), +stock("DIA", "USD", validExchanges=c("SMART", "ARCA", "ISLAND"), ExtraField="something") all.equal(getInstrument("SPY"), getInstrument("DIA")) @@ -35,7 +38,7 @@ all.equal(getInstrument("SPY"), getInstrument("DIA"), char.n=5) all.equal(getInstrument("SPY"), getInstrument("DIA"), char.n=5, collapse=NULL) all.equal(getInstrument("DIA"), getInstrument("USD")) -} + } \seealso{ \code{\link{getInstrument}}, \code{\link{instrument.table}}, diff --git a/man/buildRatio.Rd b/man/buildRatio.Rd index d441fb1..ad73d7f 100644 --- a/man/buildRatio.Rd +++ b/man/buildRatio.Rd @@ -22,19 +22,19 @@ OR Price } \description{ -Calculates time series of ratio of 2 instruments using available data. +Calculates time series of ratio of 2 instruments using available data. Returned object will be ratios calculated using Bids, Asks, and Mids, or Opens, Closes, and Adjusteds. } \details{ \code{x} should be a vector of 2 instrument names. An attempt will be made to \code{get} the data -for both instruments. If there are no xts data stored under either of the names, it will try to +for both instruments. If there are no xts data stored under either of the names, it will try to return prebuilt data with a call to \code{\link{.get_rate}}. If the data are not of the same frequency, or are not of the same type (OHLC, BBO, etc.) An attempt will be made to make them compatible. Preference is given to the first leg. If the data in \code{x[1]} is daily or slower and the data in \code{x[2]} is intraday -(e.g. if you give it daily OHLC and intraday Bid Ask Mid, it will use all of +(e.g. if you give it daily OHLC and intraday Bid Ask Mid, it will use all of the OHLC columns of \code{x[1]} and only the the End of Day Mid price of the BAM object. If the data in \code{x[1]} is intraday, and the data in \code{x[2]} is daily or slower, diff --git a/man/build_series_symbols.Rd b/man/build_series_symbols.Rd index 213646d..2563483 100644 --- a/man/build_series_symbols.Rd +++ b/man/build_series_symbols.Rd @@ -2,7 +2,7 @@ % Please edit documentation in R/build_symbols.R \name{build_series_symbols} \alias{build_series_symbols} -\title{construct a series of symbols based on root symbol and suffix letters} +\title{Construct a series of symbols based on root symbol and suffix letters} \usage{ build_series_symbols(roots, yearlist = c(0, 1)) } @@ -11,6 +11,11 @@ build_series_symbols(roots, yearlist = c(0, 1)) \item{yearlist}{vector of year suffixes to be applied, see Details} } +\value{ +A character vector containing the constructed series identifiers. + Each identifier combines a root contract identifier, a contract-month + code, and a year suffix. +} \description{ The columns needed by this version of the function are \code{primary_id} and \code{month_cycle}. \code{primary_id} should match the \code{primary_id} diff --git a/man/build_spread_symbols.Rd b/man/build_spread_symbols.Rd index 86dcd80..f62b582 100644 --- a/man/build_spread_symbols.Rd +++ b/man/build_spread_symbols.Rd @@ -2,7 +2,7 @@ % Please edit documentation in R/build_symbols.R \name{build_spread_symbols} \alias{build_spread_symbols} -\title{build symbols for exchange guaranteed (calendar) spreads} +\title{Build symbols for exchange guaranteed (calendar) spreads} \usage{ build_spread_symbols( data = NULL, @@ -20,6 +20,12 @@ build_spread_symbols( \item{start_date}{date to start building from, of type \code{Date} or an ISO-8601 date string, defaults to \code{\link{Sys.Date}}} } +\value{ +If `outputfile` is `NULL`, a data frame with columns `symbol` and + `type`, containing the constructed spread identifiers and their instrument + types. If `outputfile` is supplied, the data frame is written to that CSV + file and the function returns `NULL` invisibly. +} \description{ The columns needed by this version of the function are \code{primary_id}, \code{month_cycle}, and code \code{contracts_ahead}. @@ -37,16 +43,16 @@ The correct values will vary based on your data source. \code{contracts_ahead} should contain a comma-delimited string describing the cycle on which the guaranteed calendar spreads are to be consructed, e.g. '1' for one-month spreads, '1,3' for one and three month spreads, -'1,6,12' for 1, 6, and 12 month spreads, etc. -For quarterly symbols, the correct \code{contracts_ahead} may be -something like '1,2,3' for quarterly, bi-annual, and annual spreads. +'1,6,12' for 1, 6, and 12 month spreads, etc. +For quarterly symbols, the correct \code{contracts_ahead} may be +something like '1,2,3' for quarterly, bi-annual, and annual spreads. -\code{active_months} is a numeric field indicating how many months including -the month of the \code{start_date} the contract is available to trade. +\code{active_months} is a numeric field indicating how many months including +the month of the \code{start_date} the contract is available to trade. This number will be used as the upper limit for symbol generation. -If \code{type} is also specified, it should be a specific instrument type, -e.g. 'future_series','option_series','guaranteed_spread' or 'calendar_spread' +If \code{type} is also specified, it should be a specific instrument type, +e.g. 'future_series','option_series','guaranteed_spread' or 'calendar_spread' One of \code{data} or \code{file} must be populated for input data. } diff --git a/man/exchange_rate.Rd b/man/exchange_rate.Rd index 609bb99..2b70909 100644 --- a/man/exchange_rate.Rd +++ b/man/exchange_rate.Rd @@ -2,7 +2,7 @@ % Please edit documentation in R/instrument.R \name{exchange_rate} \alias{exchange_rate} -\title{constructor for spot exchange rate instruments} +\title{Constructor for spot exchange rate instruments} \usage{ exchange_rate( primary_id = NULL, @@ -38,6 +38,12 @@ be thrown if there is already an instrument defined with the same \item{...}{any other passthru parameters} } +\value{ +When `assign_i = TRUE`, a character vector containing the + `primary_id` values of the exchange-rate instruments assigned to the + registry. When `assign_i = FALSE`, an exchange-rate instrument object is + returned for scalar input, or a list of such objects for vectorized input. +} \description{ Currency symbols (like any symbol) may be any combination of alphanumeric characters, but the FX market has a convention that says that the first @@ -54,5 +60,5 @@ Thanks to Garrett See for helping sort out the inconsistencies in different naming and calculating conventions. } \references{ -http://financial-dictionary.thefreedictionary.com/Base+Currency +https://www.investopedia.com/terms/b/basecurrency.asp } diff --git a/man/expires.Rd b/man/expires.Rd index ba6dc45..c10f0a3 100644 --- a/man/expires.Rd +++ b/man/expires.Rd @@ -2,7 +2,7 @@ % Please edit documentation in R/expires.R \name{expires} \alias{expires} -\title{extract the correct expires value from an \code{instrument}} +\title{Extract the correct expires value from an \code{instrument}} \usage{ expires(x, ...) } @@ -34,7 +34,6 @@ indicated by their suffix_id and most energy products expire in the month prior to their suffix_id month. } \examples{ -\dontrun{ instr <- instrument("FOO_U1", currency=currency("USD"), multiplier=1, expires=c("2001-09-01", "2011-09-01", "2021-09-01"), assign_i=FALSE) @@ -53,7 +52,7 @@ instrument("FOO_U1", currency=currency("USD"), multiplier=1, expires=c("2001-09-01", "2011-09-01", "2021-09-01"), assign_i=TRUE) expires("FOO_U1") -} + } \seealso{ \code{\link{expires.instrument}}, \code{\link{expires.character}}, diff --git a/man/expires.character.Rd b/man/expires.character.Rd index 86a5f01..7efdacd 100644 --- a/man/expires.character.Rd +++ b/man/expires.character.Rd @@ -2,7 +2,7 @@ % Please edit documentation in R/expires.R \name{expires.character} \alias{expires.character} -\title{character expires extraction method} +\title{Character expires extraction method} \usage{ \method{expires}{character}(x, Date, expired = TRUE, silent = FALSE, ...) } @@ -19,12 +19,18 @@ returned will be the last one before \code{Date}. If \code{expired} is \item{silent}{silence warnings?} } +\value{ +A `Date` value representing the selected or inferred expiration + date. If `x` identifies a defined instrument, its stored expiration + information is used; otherwise, the date is inferred from the identifier. + May return `NULL` when a defined instrument has no usable expiration. +} \description{ -if no \code{instrument} can be found by the id of \code{x}, or if the +If no \code{instrument} can be found by the id of \code{x}, or if the \code{instrument} does not have an \code{expires} attribute, an attempt will be made to infer the year and month of expiration using \code{parse_id} -in which case the returned value will be a string of the format -\dQuote{YYYY-MM}. Presently, \code{Date} and \code{expired} will be ignored +in which case the returned value will be a string. +Presently, \code{Date} and \code{expired} will be ignored if \code{x} is not the name of an instrument } \seealso{ diff --git a/man/expires.instrument.Rd b/man/expires.instrument.Rd index 91399d3..8aa7b73 100644 --- a/man/expires.instrument.Rd +++ b/man/expires.instrument.Rd @@ -20,6 +20,10 @@ if \code{expires} is a single value, \code{expired} will be ignored.} \item{silent}{silence warnings?} } +\value{ +A `Date` value representing the selected expiration date, or `NULL` +if the instrument has no usable expiration information. +} \description{ Returns either the last expiration date before \code{Date}, or the first expiration date after \code{Date} (if \code{expired==FALSE}). diff --git a/man/expires.spread.Rd b/man/expires.spread.Rd index 6dc843d..61f8a5b 100644 --- a/man/expires.spread.Rd +++ b/man/expires.spread.Rd @@ -17,6 +17,12 @@ of \code{expired}).} returned will be the last one before \code{Date}. If \code{expired} is \code{FALSE} the first one after \code{Date} will be returned.} } +\value{ +A `Date` value representing the spread expiration. The value is + taken from the spread itself when available; otherwise, it is determined + from the first-expiring member. May return `NULL` if no expiration can be + determined. +} \description{ \code{x$expires} will be returned if it is not \code{NULL}. Otherwise, the (character representation of the) exiration date of the first-to-expire of diff --git a/man/find.instrument.Rd b/man/find.instrument.Rd index f060105..372223d 100644 --- a/man/find.instrument.Rd +++ b/man/find.instrument.Rd @@ -14,7 +14,7 @@ find.instrument( ) } \arguments{ -\item{text}{character string containing a regular expression. This is used +\item{text}{character string containing a regular expression. This is used by \code{\link{grep}} (see also) as the \code{pattern} argument.} \item{where}{if \dQuote{anywhere} all levels/attributes of the instruments @@ -22,11 +22,11 @@ will be searched. Otherwise, \code{where} can be used to specify in which levels/attributes to look. (e.g. \code{c("name", "description")} would only look for \code{text} in those 2 places.} -\item{Symbols}{the character ids of instruments to be searched. All are +\item{Symbols}{the character ids of instruments to be searched. All are are searched by default.} \item{ignore.case}{passed to \code{\link{grep}}; if \code{FALSE}, the pattern -matching is case sensitive and if \code{TRUE}, case is ignored during +matching is case sensitive and if \code{TRUE}, case is ignored during matching.} \item{exclude}{character vector of names of levels/attributes that should not @@ -35,39 +35,44 @@ be searched.} \item{...}{other arguments to pass through to \code{\link{grep}}} } \value{ -character vector of primary_ids of instruments that contain the +character vector of primary_ids of instruments that contain the sought after \code{text}. } \description{ Uses regular expression matching to find \code{\link{instrument}}s } \examples{ -\dontrun{ -instruments.bak <- as.list(FinancialInstrument:::.instrument, all.names=TRUE) -rm_instruments(keep.currencies=FALSE) -currency("USD") -stock("SPY", "USD", description="S&P 500 ETF") -stock("DIA", "USD", description="DJIA ETF") -stock(c("AA", "AXP", "BA", "BAC", "CAT"), "USD", members.of='DJIA') -stock("BMW", currency("EUR")) -find.instrument("ETF") -find.instrument("DJIA") -find.instrument("DJIA", "members.of") -find.instrument("USD") -find.instrument("EUR") -find.instrument("EUR", Symbols=ls_stocks()) -find.instrument("USD", "type") +backup_file <- tempfile(fileext = ".RData") +saveInstruments(backup_file) -## Can be combined with buildHierachy -buildHierarchy(find.instrument("ETF"), "type", "description") +tryCatch( + { + rm_instruments(keep.currencies = FALSE) -## Cleanup. restore previous instrument environment -rm_instruments(); rm_currencies() -loadInstruments(instruments.bak) -} + currency(c("USD", "EUR")) + stock("SPY", "USD", description = "S&P 500 ETF") + stock("DIA", "USD", description = "DJIA ETF") + stock( + c("AA", "AXP", "BA", "BAC", "CAT"), + "USD", + members.of = "DJIA" + ) + stock("BMW", "EUR") + + find.instrument("ETF") + find.instrument("DJIA") + find.instrument("DJIA", where = "members.of") + find.instrument("USD") + find.instrument("EUR", Symbols = ls_stocks()) + }, + finally = { + reloadInstruments(backup_file) + unlink(backup_file) + } +) } \seealso{ -\code{\link{buildHierarchy}}, \code{\link{instrument.table}}, +\code{\link{buildHierarchy}}, \code{\link{instrument.table}}, \code{\link{regex}} } \author{ diff --git a/man/getInstrument.Rd b/man/getInstrument.Rd index 18a014b..6a82c1a 100644 --- a/man/getInstrument.Rd +++ b/man/getInstrument.Rd @@ -15,6 +15,11 @@ getInstrument(x, Dates = NULL, silent = FALSE, type = "instrument") \item{type}{class of object to look for. See Details} } +\value{ +An object inheriting from the requested instrument class when a + matching instrument is found. Returns `FALSE` when no matching instrument + is found. +} \description{ This function will search the \code{.instrument} environment for objects of class \code{type}, using first the \code{primary_id} and then any @@ -31,14 +36,14 @@ example, the root specs for options (or futures) on the stock with ticker until it finds an instrument of the appropriate \code{type} } \examples{ -\dontrun{ option('..VX', multiplier=100, - underlying_id=future('.VX',multiplier=1000, - underlying_id=synthetic('VIX', currency("USD")))) + underlying_id=future('.VX',multiplier=1000, + underlying_id=synthetic('VIX', currency("USD"))) + ) getInstrument("VIX") getInstrument('VX') #returns the future getInstrument("VX",type='option') getInstrument('..VX') #finds the option -} + } diff --git a/man/getSymbols.FI.Rd b/man/getSymbols.FI.Rd index 7071952..aa875a1 100644 --- a/man/getSymbols.FI.Rd +++ b/man/getSymbols.FI.Rd @@ -21,7 +21,7 @@ getSymbols.FI( ) } \arguments{ -\item{Symbols}{a character vector specifying the names of each symbol to be +\item{Symbols}{a character vector specifying the names of each symbol to be loaded} \item{from}{Retrieve data no earlier than this date. Default '2010-01-01'.} @@ -37,64 +37,107 @@ default ""} \item{extension}{file extension, default "rda"} -\item{split_method}{string specifying the method used to split the files, -currently \sQuote{days} or \sQuote{common}, see +\item{split_method}{string specifying the method used to split the files, +currently \sQuote{days} or \sQuote{common}, see \code{\link{setSymbolLookup.FI}}} -\item{use_identifier}{optional. identifier used to construct the -\code{primary_id} of the instrument. If you use this, you must have +\item{use_identifier}{optional. identifier used to construct the +\code{primary_id} of the instrument. If you use this, you must have previously defined the \code{\link{instrument}}} \item{date_format}{format as per the \code{\link{strptime}}, see Details} \item{verbose}{TRUE/FALSE} -\item{days_to_omit}{character vector of names of weekdays that should not be -loaded. Default is \code{c("Saturday", "Sunday")}. Use \code{NULL} to +\item{days_to_omit}{character vector of names of weekdays that should not be +loaded. Default is \code{c("Saturday", "Sunday")}. Use \code{NULL} to attempt to load data for all days of the week.} -\item{indexTZ}{valid TZ string. (e.g. \dQuote{America/Chicago} or +\item{indexTZ}{valid TZ string. (e.g. \dQuote{America/Chicago} or \dQuote{America/New_York}) See \code{\link[xts]{indexTZ}}.} } +\value{ +If `auto.assign = TRUE`, a character vector containing the names of + the objects assigned to `env`. If `auto.assign = FALSE`, an `xts` object + is returned for one requested symbol and a named list is returned for + multiple symbols. Returns `NULL` when no data are found. +} \description{ -This function should probably get folded back into getSymbols.rda in +This function should probably get folded back into getSymbols.rda in quantmod. } \details{ Meant to be called internally by \code{\link[quantmod]{getSymbols}} . -The symbol lookup table will most likely be loaded by +The symbol lookup table will most likely be loaded by \code{\link{setSymbolLookup.FI}} -If date_format is NULL (the Default), we will assume an ISO date as changed -by \code{\link{make.names}}, for example, 2010-12-01 would be assumed to be a +If date_format is NULL (the Default), we will assume an ISO date as changed +by \code{\link{make.names}}, for example, 2010-12-01 would be assumed to be a file containing 2010.12.01 If \code{indexTZ} is provided, the data will be converted to that timezone -If auto.assign is FALSE, \code{Symbols} should be of length 1. Otherwise, -\code{\link[quantmod]{getSymbols}} will give you an error that says +If auto.assign is FALSE, \code{Symbols} should be of length 1. Otherwise, +\code{\link[quantmod]{getSymbols}} will give you an error that says \dQuote{must use auto.assign=TRUE for multiple Symbols requests} -However, if you were to call \code{getSymbols.FI} directly (which is -\emph{NOT} recommended) with \code{auto.assign=FALSE} and more than one +However, if you were to call \code{getSymbols.FI} directly (which is +\emph{NOT} recommended) with \code{auto.assign=FALSE} and more than one Symbol, a list would be returned. -Argument matching for this function is as follows. If the user provides a +Argument matching for this function is as follows. If the user provides a value for an argument, that value will be used. If the user did not provide -a value for an argument, but there is a value for that argument for the -given \code{Symbol} in the Symbol Lookup Table (see -\code{\link{setSymbolLookup.FI}}), that value will be used. Otherwise, +a value for an argument, but there is a value for that argument for the +given \code{Symbol} in the Symbol Lookup Table (see +\code{\link{setSymbolLookup.FI}}), that value will be used. Otherwise, the formal defaults will be used. } \examples{ -\dontrun{ -getSymbols("SPY", src='yahoo') -dir.create("tmpdata") -saveSymbols.common("SPY", base_dir="tmpdata") -rm("SPY") -getSymbols("SPY", src='FI', dir="tmpdata", split_method='common') -unlink("tmpdata/SPY", recursive=TRUE) -} +example_dir <- tempfile("fi-symbol-data-") +dir.create(example_dir) + +data_env <- new.env() + +data_env$FI_EXAMPLE <- xts::xts( + cbind( + Close = c(100, 101, 102, 101), + Volume = c(1000, 1200, 900, 1100) + ), + order.by = as.Date("2020-01-01") + 0:3 +) + +tryCatch( + { + saveSymbols.common( + Symbols = "FI_EXAMPLE", + base_dir = example_dir, + env = data_env + ) + + loaded_data <- getSymbols.FI( + Symbols = "FI_EXAMPLE", + dir = example_dir, + split_method = "common", + auto.assign = FALSE, + verbose = FALSE + ) + + stopifnot( + xts::is.xts(loaded_data), + NROW(loaded_data) == 4L, + identical( + colnames(loaded_data), + c("Close", "Volume") + ) + ) + + loaded_data + }, + finally = { + unlink(example_dir, recursive = TRUE) + } +) + } \seealso{ \code{\link{saveSymbols.days}} diff --git a/man/get_rate.Rd b/man/get_rate.Rd index 0fca089..bc6e58e 100644 --- a/man/get_rate.Rd +++ b/man/get_rate.Rd @@ -19,15 +19,6 @@ xts object with as many columns as practicable. \description{ Try to find exchange rate data in an environment, inverting if necessary. } -\examples{ - -\dontrun{ -EURUSD <- getSymbols("EURUSD=x",src='yahoo',auto.assign=FALSE) -USDEUR <- .get_rate("USD","EUR") -head(USDEUR) -head(EURUSD) -} -} \seealso{ \code{\link{buildRatio}} \code{\link{redenominate}} @@ -35,3 +26,4 @@ head(EURUSD) \author{ Garrett See } +\keyword{internal} diff --git a/man/instrument.Rd b/man/instrument.Rd index bf4be59..cd5a45e 100644 --- a/man/instrument.Rd +++ b/man/instrument.Rd @@ -113,6 +113,12 @@ thrown and the instrument will not be created.} \item{underlying_id}{For derivatives, the identifier of the instrument that this one is derived from, may be \code{NULL} for cash settled instruments} } +\value{ +When `assign_i = TRUE`, a character vector containing the + `primary_id` values of the instruments assigned to the instrument + registry. When `assign_i = FALSE`, an instrument object is returned for + scalar input; vectorized constructors return a list of instrument objects. +} \description{ All 'currency' instruments must be defined before instruments of other types may be defined. diff --git a/man/instrument.auto.Rd b/man/instrument.auto.Rd index a58a083..2e5207a 100644 --- a/man/instrument.auto.Rd +++ b/man/instrument.auto.Rd @@ -77,7 +77,6 @@ This is not intended to be used to create instruments of type or \code{bond} although it may be updated in the future. } \examples{ -\dontrun{ instrument.auto("CL_H1.U1") getInstrument("CL_H1.U1") #guaranteed_spread @@ -93,7 +92,7 @@ getInstrument("VX_H11") #couldn't find future, didnt make future_series future("VX","USD",1000,underlying_id=synthetic("SPX","USD")) #make the root instrument.auto("VX_H11") #and try again getInstrument("VX_H11") #made a future_series -} + } \author{ Garrett See diff --git a/man/is.currency.Rd b/man/is.currency.Rd index 3408079..7b7ca87 100644 --- a/man/is.currency.Rd +++ b/man/is.currency.Rd @@ -9,6 +9,10 @@ is.currency(x) \arguments{ \item{x}{object to test for type} } +\value{ +A single logical value indicating whether `x` inherits from class + `"currency"`. +} \description{ class test for object supposedly of type 'currency' } diff --git a/man/is.currency.name.Rd b/man/is.currency.name.Rd index 79ed93c..1b63757 100644 --- a/man/is.currency.name.Rd +++ b/man/is.currency.name.Rd @@ -10,6 +10,10 @@ is.currency.name(x) \arguments{ \item{x}{character vector} } +\value{ +A logical vector indicating whether each element of `x` identifies + a defined currency instrument. Non-character input returns `FALSE`. +} \description{ check each element of a character vector to see if it is either the primary_id or an identifier of a \code{\link{currency}} diff --git a/man/is.instrument.Rd b/man/is.instrument.Rd index 48ec5ab..626e84f 100644 --- a/man/is.instrument.Rd +++ b/man/is.instrument.Rd @@ -9,6 +9,10 @@ is.instrument(x) \arguments{ \item{x}{object to test for type} } +\value{ +A single logical value indicating whether `x` inherits from class + `"instrument"`. +} \description{ class test for object supposedly of type 'instrument' } diff --git a/man/load.instruments.Rd b/man/load.instruments.Rd index 9a99e30..953fd70 100644 --- a/man/load.instruments.Rd +++ b/man/load.instruments.Rd @@ -2,7 +2,7 @@ % Please edit documentation in R/load.instruments.R \name{load.instruments} \alias{load.instruments} -\title{load instrument metadata into the .instrument environment} +\title{Load instrument metadata into the .instrument environment} \usage{ load.instruments( file = NULL, @@ -29,6 +29,10 @@ load.instruments( \item{overwrite}{TRUE/FALSE. See \code{\link{instrument}}.} } +\value{ +`NULL` invisibly. Called for the side effect of creating instrument + definitions in the package instrument registry. +} \description{ This function will load instrument metadata (data about the data) either from a file specified by the \code{file} argument or @@ -47,22 +51,54 @@ Use the \code{identifier_cols} argument to specify which fields (if any) in the Typically, columns will exist for \code{multiplier} and \code{tick_size}. -Any other columns necessary to define the specified instrument type will also be required to avoid fatal Errors. +Any other columns necessary to define the specified instrument type will also be required to avoid fatal Errors. Additional columns will be processed, either as additional identifiers for recognized identifier names, or as custom fields. See \code{\link{instrument}} for more information on custom fields. } \examples{ -\dontrun{ -load.instruments(system.file('data/currencies.csv.gz',package='FinancialInstrument')) -load.instruments(system.file('data/root_contracts.csv.gz',package='FinancialInstrument')) -load.instruments(system.file('data/future_series.csv.gz',package='FinancialInstrument')) +example_ids <- c( + "FI_EXAMPLE_A", + "FI_EXAMPLE_B" +) + +had_usd <- is.currency.name("USD") + +tryCatch( + { + if (!had_usd) { + currency("USD") + } + + metadata <- data.frame( + primary_id = example_ids, + type = c("stock", "stock"), + currency = c("USD", "USD"), + description = c( + "Example instrument A", + "Example instrument B" + ), + stringsAsFactors = FALSE + ) + + load.instruments(metadata = metadata) + + getInstrument("FI_EXAMPLE_A") + getInstrument("FI_EXAMPLE_B") + }, + finally = { + rm_instruments(example_ids) + + if (!had_usd) { + rm_currencies("USD") + } + } +) -} } \seealso{ \code{\link{loadInstruments}}, -\code{\link{instrument}}, -\code{\link{setSymbolLookup.FI}}, -\code{\link[quantmod]{getSymbols}}, +\code{\link{instrument}}, +\code{\link{setSymbolLookup.FI}}, +\code{\link[quantmod]{getSymbols}}, \code{\link{getSymbols.FI}} } diff --git a/man/ls_by_currency.Rd b/man/ls_by_currency.Rd old mode 100755 new mode 100644 index c745d69..c69e176 --- a/man/ls_by_currency.Rd +++ b/man/ls_by_currency.Rd @@ -13,7 +13,7 @@ \alias{ls_HKD} \alias{ls_SEK} \alias{ls_NZD} -\title{shows or removes instruments of given currency denomination(s)} +\title{List instruments by currency denomination} \usage{ ls_by_currency(currency, pattern = NULL, match = TRUE, show.currencies = FALSE) @@ -40,54 +40,72 @@ ls_SEK(pattern = NULL, match = TRUE, show.currencies = FALSE) ls_NZD(pattern = NULL, match = TRUE, show.currencies = FALSE) } \arguments{ -\item{currency}{chr vector of names of currency} +\item{currency}{Character vector containing the names of currencies.} -\item{pattern}{an optional regular expression. Only names matching -\sQuote{pattern} are returned.} +\item{pattern}{An optional regular expression. Only instrument names +matching `pattern` are returned.} -\item{match}{exact match?} +\item{match}{Logical. Should `pattern` be matched exactly?} -\item{show.currencies}{include names of currency instruments in the returned -names?} +\item{show.currencies}{Logical. Should currency instruments themselves be +included in the returned names?} -\item{x}{what to remove. chr vector.} +\item{x}{Character vector of instrument names to remove. If missing, all +instruments denominated in `currency` are selected.} -\item{keep.currencies}{Do not delete currency instruments when deleting -multiple instruments.} +\item{keep.currencies}{Logical. If `TRUE`, retain the currency instruments +themselves.} } \value{ -ls_ functions return vector of instrument names rm_ functions return -invisible / called for side-effect. +A character vector containing instrument names denominated in the + requested currencies, or `NULL` when no matching instruments are found. } \description{ -ls_ functions get names of instruments denominated in a given currency (or -currencies) rm_ functions remove instruments of a given currency +Returns the names of instruments denominated in one or more specified +currencies. } \examples{ +example_dir <- tempfile("fi-currency-") +dir.create(example_dir) -\dontrun{ -#First create instruments -currency(c('USD','CAD','GBP') -stock(c('CM','CNQ'),'CAD') -stock(c('BET','BARC'),'GBP') -stock(c('SPY','DIA'),'USD') +backup_name <- "backup.RData" +backup_path <- file.path(example_dir, backup_name) -#now the examples -ls_by_currency(c('CAD','GBP')) +saveInstruments(backup_name, dir = example_dir) -ls_USD() -ls_CAD() +tryCatch( + { + rm_instruments(keep.currencies = FALSE) + + currency(c("USD", "CAD", "GBP")) + stock(c("CM", "CNQ"), currency = "CAD") + stock(c("BARC", "BET"), currency = "GBP") + stock(c("DIA", "SPY"), currency = "USD") + + ls_by_currency("CAD") + ls_by_currency("GBP") + ls_USD() + ls_CAD() + + rm_by_currency(currency = "CAD") + + }, + finally = { + if (file.exists(backup_path)) { + reloadInstruments( + backup_name, + dir = example_dir + ) + } + + unlink(example_dir, recursive = TRUE) + } +) -#2 ways to remove all instruments of a currency -rm_instruments(ls_USD()) -#rm_instruments(ls_GBP(),keep.currencies=FALSE) -rm_by_currency( ,'CAD') -#rm_by_currency( ,'CAD', keep.currencies=FALSE) -} } \seealso{ -ls_instruments, ls_currencies, rm_instruments, rm_currencies, -twsInstrument, instrument +[ls_instruments()], [ls_currencies()], [rm_instruments()], +[rm_currencies()], [instrument()] } \author{ Garrett See diff --git a/man/ls_by_expiry.Rd b/man/ls_by_expiry.Rd old mode 100755 new mode 100644 diff --git a/man/ls_instruments.Rd b/man/ls_instruments.Rd old mode 100755 new mode 100644 index 66a5e1b..889e0df --- a/man/ls_instruments.Rd +++ b/man/ls_instruments.Rd @@ -156,8 +156,7 @@ denominated in USD. } \examples{ -\dontrun{ -#rm_instruments(keep.currencies=FALSE) #remove everything from .instrument +rm_instruments(keep.currencies=FALSE) #remove everything from .instrument # First, create some instruments currency(c("USD", "EUR", "JPY")) @@ -184,13 +183,12 @@ ls_futures() ls_derivatives() ls_puts() ls_non_derivatives() -#ls_by_expiry('20110618',ls_puts()) #put options that expire on Jun 18th, 2011 -#ls_puts(ls_by_expiry('20110618')) #same thing +ls_by_expiry('20110618',ls_puts()) #put options that expire on Jun 18th, 2011 +ls_puts(ls_by_expiry('20110618')) rm_options('SPY_110618C130') rm_futures() ls_instruments() -#rm_instruments('EUR') #Incorrect rm_instruments('EUR', keep.currencies=FALSE) #remove the currency rm_currencies('JPY') #or remove currency like this ls_currencies() @@ -199,11 +197,7 @@ ls_instruments() rm_instruments() #remove all but currencies rm_currencies() -option_series.yahoo('DIA') -ls_instruments_by('underlying_id','DIA') #underlying_id must exactly match 'DIA' -ls_derivatives('DIA',match=FALSE) #primary_ids that contain 'DIA' -rm_instruments() -} + } \seealso{ ls_instruments_by, ls_by_currency, ls_by_expiry, ls, rm, diff --git a/man/ls_instruments_by.Rd b/man/ls_instruments_by.Rd old mode 100755 new mode 100644 diff --git a/man/option_series.yahoo.Rd b/man/option_series.yahoo.Rd old mode 100755 new mode 100644 index e70aa80..fc90ea9 --- a/man/option_series.yahoo.Rd +++ b/man/option_series.yahoo.Rd @@ -2,7 +2,7 @@ % Please edit documentation in R/instrument.R \name{option_series.yahoo} \alias{option_series.yahoo} -\title{constructor for series of options using yahoo data} +\title{Constructor for series of options using yahoo data} \usage{ option_series.yahoo( symbol, @@ -52,7 +52,7 @@ Has only been tested with stock options. The options' currency should be the same as the underlying's. } \examples{ -\dontrun{ +\donttest{ option_series.yahoo('SPY') #only nearby calls and puts option_series.yahoo('DIA', Exp=NULL) #all chains ls_instruments() diff --git a/man/parse_id.Rd b/man/parse_id.Rd index e59e728..972a9a3 100644 --- a/man/parse_id.Rd +++ b/man/parse_id.Rd @@ -14,24 +14,24 @@ parse_id(x, silent = TRUE, root = NULL) \item{root}{character name of instrument root_id. Optionally provide this to make parsing easier.} } \value{ -a list of class \sQuote{id.list} containing \sQuote{root} and \sQuote{suffix} as well as +a list of class \sQuote{id.list} containing \sQuote{root} and \sQuote{suffix} as well as what is returned from \code{\link{parse_suffix}} (type, month, year, strike, right, cm, cc, format) } \description{ Extract/infer descriptive information about an instrument from its name. } \details{ -This function is primarily intended to be used on the names of \code{\link{future_series}} -and \code{\link{option_series}} instruments, and it will work best if the id has an -underscore in it that separates the root_id from the suffix_id. (However, it should be able -to handle most ids even if the underscore is missing). -After splitting \code{x} into a root_id and suffix_id, the suffix_id is +This function is primarily intended to be used on the names of \code{\link{future_series}} +and \code{\link{option_series}} instruments, and it will work best if the id has an +underscore in it that separates the root_id from the suffix_id. (However, it should be able +to handle most ids even if the underscore is missing). +After splitting \code{x} into a root_id and suffix_id, the suffix_id is passed to \code{\link{parse_suffix}} (see also) for further processing. TODO: add support for bond_series. } \note{ -this function will identify \code{x} as an \code{\link{exchange_rate}} only if it is +this function will identify \code{x} as an \code{\link{exchange_rate}} only if it is 6 characters long and made up of 2 previously defined \code{\link{currency}} instruments. } \examples{ diff --git a/man/parse_suffix.Rd b/man/parse_suffix.Rd index d5d54af..e3b6ea4 100644 --- a/man/parse_suffix.Rd +++ b/man/parse_suffix.Rd @@ -12,8 +12,8 @@ parse_suffix(x, silent = TRUE) \item{silent}{silence warnings? (warning will usually be about inferring a 4 digit year from a 1 or 2 digit year)} } \value{ -an object of class \sQuote{suffix.list} which is a list containing \sQuote{type} of instrument, -\sQuote{month} of expiration, \sQuote{year} of expiration, \sQuote{strike} price of option, +an object of class \sQuote{suffix.list} which is a list containing \sQuote{type} of instrument, +\sQuote{month} of expiration, \sQuote{year} of expiration, \sQuote{strike} price of option, \sQuote{right} of option (\dQuote{C} or \dQuote{P}), \sQuote{cm} (maturity in days of a constant maturity contract), \sQuote{cc} (method for calculating a continuous contract), \sQuote{format} (string that indicates the format of the unparsed id). } @@ -21,7 +21,7 @@ an object of class \sQuote{suffix.list} which is a list containing \sQuote{type} extract information from the suffix_id of an instrument } \details{ -These would be recognized as a Sep 2011 outright futures contract: +These would be recognized as a Sep 2011 outright futures contract: U1, U11, SEP1, SEP11, U2011, Sep2011, SEP2011 These would be recognized as a call with a strike of 122.5 that expires Sep 17, 2011: @@ -38,7 +38,7 @@ cc.Exp.1 (continuous contract rolled 1 day before Expiration) Synthetics and spreads: -SPY.DIA --> type == synthetic; +SPY.DIA --> type == synthetic; U1.Z1 or U11.Z11 --> type == "calendar", "spread"; month == 'SEP', year == 2011 diff --git a/man/print.id.list.Rd b/man/print.id.list.Rd index a1eefc6..d112496 100644 --- a/man/print.id.list.Rd +++ b/man/print.id.list.Rd @@ -6,6 +6,9 @@ \usage{ \method{print}{id.list}(x, ...) } +\value{ +The `id.list` object `x`, invisibly. +} \description{ id.list class print method } diff --git a/man/print.instrument.Rd b/man/print.instrument.Rd index 7f45e39..b89e1b0 100644 --- a/man/print.instrument.Rd +++ b/man/print.instrument.Rd @@ -6,6 +6,9 @@ \usage{ \method{print}{instrument}(x, ...) } +\value{ +The instrument object `x`, invisibly. +} \description{ instrument class print method } diff --git a/man/print.suffix.list.Rd b/man/print.suffix.list.Rd index 9de6ba2..3254593 100644 --- a/man/print.suffix.list.Rd +++ b/man/print.suffix.list.Rd @@ -6,6 +6,9 @@ \usage{ \method{print}{suffix.list}(x, ...) } +\value{ +The `suffix.list` object `x`, invisibly. +} \description{ suffix.list class print method } diff --git a/man/redenominate.Rd b/man/redenominate.Rd index 1b0bdc9..f65d2df 100644 --- a/man/redenominate.Rd +++ b/man/redenominate.Rd @@ -33,15 +33,15 @@ xts object, with as many columns as practicable, that represents the value of an Redenominate (change the base of) an instrument } \details{ -If \code{old_base} is not provided, \code{x} must be the name of an +If \code{old_base} is not provided, \code{x} must be the name of an instrument (or an object with the name of a defined instrument) so that the currency attribute of the instrument can be used. Otherwise, \code{old_base} must be provided. If you want to convert to JPY something that is denominated in EUR, you must have data for the EURJPY (or JPYEUR) exchange rate. If you don't have -data for EURJPY, but you do have data for EURUSD and USDJPY, -you could \code{redenominate} to USD, then \code{redenominate} to EUR, +data for EURJPY, but you do have data for EURUSD and USDJPY, +you could \code{redenominate} to USD, then \code{redenominate} to EUR, but this function is not yet smart enough to do that for you. See the help for buildRatio also. diff --git a/man/saveInstruments.Rd b/man/saveInstruments.Rd index 731ab8c..75aa433 100644 --- a/man/saveInstruments.Rd +++ b/man/saveInstruments.Rd @@ -14,7 +14,7 @@ reloadInstruments(file_name = "MyInstruments", dir = "") } \arguments{ \item{file_name}{name of file. e.g. \dQuote{MyInstruments.RData}. -As an experimental feature, a \code{list} or \code{environment} can be passed +As an experimental feature, a \code{list} or \code{environment} can be passed to \code{file_name}.} \item{dir}{Directory of file (defaults to current working directory. ie. "")} @@ -31,34 +31,56 @@ Saves (loads) the .instrument environment to (from) disk. After you have defined some instruments, you can use \code{saveInstruments} to save the entire .instrument environment to disk. -\code{loadInstruments} will read a file that contains instruments and add -those instrument definitions to your .instrument environment. -\code{reloadInstruments} will remove all instruments in the current +\code{loadInstruments} will read a file that contains instruments and add +those instrument definitions to your .instrument environment. +\code{reloadInstruments} will remove all instruments in the current .instrument environment before loading instruments from disk. -The \code{file_name} should have a file extension of \dQuote{RData}, +The \code{file_name} should have a file extension of \dQuote{RData}, \dQuote{rda}, \dQuote{R}, or \dQuote{txt}. If the \code{file_name} does not -end with one of those, \dQuote{.RData} will be appended to the +end with one of those, \dQuote{.RData} will be appended to the \code{file_name} If the file extension is \dQuote{R} or \dQuote{txt}, \code{saveInstruments} -will create a text file of \R code that can be \code{\link{source}}d to +will create a text file of \R code that can be \code{\link{source}}d to load instruments back into the .instrument environment. } \examples{ -\dontrun{ -stock("SPY", currency("USD"), 1) -tmpdir <- tempdir() -saveInstruments("MyInstruments.RData", dir=tmpdir) -rm_instruments(keep.currencies=FALSE) -loadInstruments("MyInstruments.RData", dir=tmpdir) -# write .R file that can be sourced -saveInstruments("MyInstruments.R", dir=tmpdir) -rm_instruments(keep.currencies=FALSE) -loadInstruments("MyInstruments.R", dir=tmpdir) -#source(file=paste(tmpdir, "MyInstruments.R", sep="/")) # same -unlink(tmpdir, recursive=TRUE) -} + +backup_file <- tempfile(fileext = ".RData") +output_dir <- tempfile("fi-save-test-") +dir.create(output_dir) + +saveInstruments(backup_file) + +tryCatch( + { + rm_instruments(keep.currencies = FALSE) + + currency("USD") + stock("SPY", currency = "USD") + + saveInstruments( + "test-instruments.R", + dir = output_dir + ) + + rm_instruments(keep.currencies = FALSE) + + source( + file.path(output_dir, "test-instruments.R") + ) + + stopifnot(is.instrument.name("SPY")) + stopifnot(getInstrument("SPY")$currency == "USD") + }, + finally = { + reloadInstruments(backup_file) + unlink(backup_file) + unlink(output_dir, recursive = TRUE) + } +) + } \seealso{ save, load load.instrument define_stocks, define_futures, diff --git a/man/saveSymbols.days.Rd b/man/saveSymbols.days.Rd index 8d49e67..3debe51 100644 --- a/man/saveSymbols.days.Rd +++ b/man/saveSymbols.days.Rd @@ -22,25 +22,52 @@ saveSymbols.common(Symbols, base_dir = "", extension = "rda", env = .GlobalEnv) called for side-effect. } \description{ -Save data to disk the way that \code{getSymbols.FI} +Save data to disk the way that \code{getSymbols.FI} expects it to be saved. } \details{ -If they do not already exist, subdirectories will be created for each of the -\code{Symbols}. \code{saveSymbols.common} will save a single \sQuote{rda} +If they do not already exist, subdirectories will be created for each of the +\code{Symbols}. \code{saveSymbols.common} will save a single \sQuote{rda} file for each of the \code{Symbols} in that symbol's subdirectory. -\code{saveSymbols.days} will split the data up into days and save a separate +\code{saveSymbols.days} will split the data up into days and save a separate \sQuote{rda} file for each day in that symbol's subdirectory. } \examples{ -\dontrun{ -getSymbols("SPY", src='yahoo') -dir.create("tmpdata") -saveSymbols.common("SPY", base_dir="tmpdata") -rm("SPY") -getSymbols("SPY", src='FI', dir="tmpdata", split_method='common') -unlink("tmpdata/SPY", recursive=TRUE) -} +example_dir <- tempfile("fi-symbol-data-") +dir.create(example_dir) + +example_env <- new.env() + +example_env$SPY <- xts::xts( + matrix( + 1:8, + ncol = 2L, + dimnames = list( + NULL, + c("Close", "Volume") + ) + ), + order.by = as.Date("2020-01-01") + 0:3 +) + +tryCatch( + { + saveSymbols.common( + "SPY", + base_dir = example_dir, + env = example_env + ) + + list.files( + example_dir, + recursive = TRUE + ) + }, + finally = { + unlink(example_dir, recursive = TRUE) + } +) + } \seealso{ \code{\link{getSymbols.FI}} diff --git a/man/series_instrument.Rd b/man/series_instrument.Rd index afdac7e..a8fa5de 100644 --- a/man/series_instrument.Rd +++ b/man/series_instrument.Rd @@ -77,6 +77,14 @@ stored for this instrument.} \item{payment_schedule}{Not currently being implemented} } +\value{ +For `future_series()` and `option_series()`, a character vector of + assigned `primary_id` values when `assign_i = TRUE`, or an instrument + object for scalar input and a list of instrument objects for vectorized + input when `assign_i = FALSE`. `bond_series()` returns the result of + creating or updating the bond-series instrument; this is normally the + assigned identifier for a newly created series. +} \description{ Constructors for series contracts on instruments such as options and futures } @@ -97,7 +105,6 @@ updated recently and may not support all the features supported for \code{option_series} and \code{future_series}. Patches welcome. } \examples{ -\dontrun{ currency("USD") future("ES","USD",multiplier=50, tick_size=0.25) future_series('ES_U1') @@ -112,5 +119,5 @@ option_series(root_id='.SPY',suffix_id='111119C130') #multiple series instruments at once. future_series(c("ES_H12","ES_M12")) option_series(c("SPY_110917C115","SPY_110917P115")) -} + } diff --git a/man/setSymbolLookup.FI.Rd b/man/setSymbolLookup.FI.Rd index f40ba13..e1c5546 100644 --- a/man/setSymbolLookup.FI.Rd +++ b/man/setSymbolLookup.FI.Rd @@ -2,7 +2,7 @@ % Please edit documentation in R/load.instruments.R \name{setSymbolLookup.FI} \alias{setSymbolLookup.FI} -\title{set quantmod-style SymbolLookup for instruments} +\title{Set quantmod-style SymbolLookup for instruments} \usage{ setSymbolLookup.FI( base_dir, @@ -32,18 +32,25 @@ setSymbolLookup.FI( \item{src}{which \code{\link[quantmod]{getSymbols}} sub-type to use, default \code{\link{getSymbols.FI}} by setting 'FI'} } +\value{ +No meaningful return value. Called for the side effect of updating + the `quantmod` symbol lookup table for the requested instruments. +} \description{ -This function exists to tell \code{\link[quantmod]{getSymbols}} where to look for your repository of market data. +This function exists to tell \code{\link[quantmod]{getSymbols}} where to look +for your repository of market data. } \details{ -The \code{base_dir} parameter \emph{must} be set or the function will fail. -This will vary by your local environment and operating system. For mixed-OS environments, -we recommend doing some OS-detection and setting the network share to your data to a common -location by operating system. For example, all Windows machines may use \dQuote{M:/} -and all *nix-style (linux, Mac) machines may use \dQuote{/mnt/mktdata/}. - -The \code{split_method} currently allows either \sQuote{days} or \sQuote{common}, and expects the -file or files to be in sub-directories named for the symbol. In high frequency data, it is standard practice to split +The \code{base_dir} parameter \emph{must} be set or the function will fail. +This will vary by your local environment and operating system. For mixed-OS +environments, we recommend doing some OS-detection and setting the network +share to your data to a common location by operating system. For example, +all Windows machines may use \dQuote{M:/} and all *nix-style (linux, Mac) +machines may use \dQuote{/mnt/mktdata/}. + +The \code{split_method} currently allows either \sQuote{days} or +\sQuote{common}, and expects the file or files to be in sub-directories named +for the symbol. In high frequency data, it is standard practice to split the data by days, which is why that option is the default. } \seealso{ diff --git a/man/sort.instrument.Rd b/man/sort.instrument.Rd index ce1ea36..ca5b1be 100644 --- a/man/sort.instrument.Rd +++ b/man/sort.instrument.Rd @@ -6,6 +6,10 @@ \usage{ \method{sort}{instrument}(x, decreasing = FALSE, na.last = NA, ...) } +\value{ +An instrument object of the same class as `x`. Core fields remain + first and additional named fields are sorted alphabetically. +} \description{ instrument class sort method } diff --git a/man/update_instruments.morningstar.Rd b/man/update_instruments.morningstar.Rd index 0cdb943..f8185f6 100644 --- a/man/update_instruments.morningstar.Rd +++ b/man/update_instruments.morningstar.Rd @@ -21,24 +21,11 @@ called for side-effect. Currently, this only updates ETFs. It will add \dQuote{msName} and \dQuote{msCategory} attributes to the instruments. (ms for morningstar) } -\examples{ -\dontrun{ -## backup .instrument environment -ibak <- as.list(FinancialInstrument:::.instrument) -rm_instruments() -stock(s <- c("SPY", "USO", "LQD"), currency("USD")) -update_instruments.morningstar(s) -instrument.table(s) -## cleanup and restore instrument environment -rm_instruments(keep.currencies=FALSE) -loadInstruments(ibak) -} -} \references{ \url{http://www.morningstar.com} } \seealso{ -\code{\link{update_instruments.yahoo}}, +\code{\link{update_instruments.yahoo}}, \code{\link{update_instruments.TTR}} \code{\link{update_instruments.iShares}} } diff --git a/man/update_instruments.yahoo.Rd b/man/update_instruments.yahoo.Rd index d63be8a..3bbb735 100644 --- a/man/update_instruments.yahoo.Rd +++ b/man/update_instruments.yahoo.Rd @@ -49,7 +49,6 @@ define the stocks if they do not already exist. } \references{ Yahoo! Finance "finance.yahoo.com" and YahooQuote -\url{https://dirk.eddelbuettel.com/code/yahooquote.html} } \seealso{ \code{\link{update_instruments.instrument}}, diff --git a/man/volep.Rd b/man/volep.Rd index d89acdd..712c65a 100644 --- a/man/volep.Rd +++ b/man/volep.Rd @@ -11,6 +11,11 @@ volep(x, units) \item{units}{volume sum to mark for bars} } +\value{ +An integer vector containing row positions where cumulative volume + crosses successive multiples of `units`. These positions may be used as + endpoints for constructing volume bars. +} \description{ generate endpoints for volume bars } diff --git a/sandbox/quanstrat-integration-test.R b/sandbox/quanstrat-integration-test.R new file mode 100644 index 0000000..6931749 --- /dev/null +++ b/sandbox/quanstrat-integration-test.R @@ -0,0 +1,74 @@ +library(FinancialInstrument) +library(blotter) +library(quantstrat) +library(xts) + +suppressWarnings( + rm(list = ls(envir = FinancialInstrument:::.instrument), + envir = FinancialInstrument:::.instrument) +) + +currency("USD") + +stock( + "TEST", + currency = "USD", + multiplier = 1 +) + +dates <- as.Date("2020-01-01") + 0:9 + +TEST <- xts( + cbind( + Open = 100:109, + High = 101:110, + Low = 99:108, + Close = 100:109, + Volume = rep(1000, 10), + Adjusted = 100:109 + ), + order.by = dates +) + +portfolio_name <- "fi_compat_portfolio" +account_name <- "fi_compat_account" + +initPortf( + portfolio_name, + symbols = "TEST", + initDate = "2019-12-31" +) + +initAcct( + account_name, + portfolios = portfolio_name, + initDate = "2019-12-31", + initEq = 100000 +) + +initOrders( + portfolio = portfolio_name, + initDate = "2019-12-31" +) + +addTxn( + Portfolio = portfolio_name, + Symbol = "TEST", + TxnDate = as.character(dates[2]), + TxnQty = 10, + TxnPrice = 101 +) + +updatePortf(portfolio_name) +updateAcct(account_name) +updateEndEq(account_name) + +stopifnot( + getInstrument("TEST")$multiplier == 1, + getPosQty( + portfolio_name, + "TEST", + as.character(dates[3]) + ) == 10 +) + diff --git a/sandbox/reverse-dependency-checks.sh b/sandbox/reverse-dependency-checks.sh new file mode 100644 index 0000000..25ffbce --- /dev/null +++ b/sandbox/reverse-dependency-checks.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT="$(pwd)" +LIB="$ROOT/revdep-library" +SRC="$ROOT/revdep-src" + +rm -rf "$LIB" "$SRC" +mkdir -p "$LIB" "$SRC" + +export R_LIBS_USER="$LIB" + +# Build and install the current FinancialInstrument branch +R CMD build . +FI_TARBALL=$(ls -t FinancialInstrument_*.tar.gz | head -1) +R CMD INSTALL --library="$LIB" "$FI_TARBALL" + +# blotter +git clone --depth 1 \ + https://github.com/braverock/blotter.git \ + "$SRC/blotter" + +R CMD build "$SRC/blotter" +BLOTTER_TARBALL=$(ls -t blotter_*.tar.gz | head -1) +R CMD check --no-manual "$BLOTTER_TARBALL" +R CMD INSTALL --library="$LIB" "$BLOTTER_TARBALL" + +# quantstrat +git clone --depth 1 \ + https://github.com/braverock/quantstrat.git \ + "$SRC/quantstrat" + +R CMD build "$SRC/quantstrat" +QUANTSTRAT_TARBALL=$(ls -t quantstrat_*.tar.gz | head -1) +R CMD check --no-manual "$QUANTSTRAT_TARBALL"