From a3403d68f1a08c74eeab2126c033f942149b091b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Jul 2026 07:34:15 +0000 Subject: [PATCH 01/16] Initial plan From e302839f7382152afab9494c0766760cb08b2d25 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Jul 2026 07:48:05 +0000 Subject: [PATCH 02/16] CRAN 1.4.1 compliance: verbose Tick2Sec, loadInstruments fix, tests, version bump --- DESCRIPTION | 2 +- NEWS | 40 +++++++++++++++++ R/Tick2Sec.R | 7 ++- R/saveInstruments.R | 3 +- cran-comments.md | 42 ++++++++++++------ inst/tinytest/test_saveInstruments.R | 66 ++++++++++++++++++++++++++++ man/Tick2Sec.Rd | 6 ++- 7 files changed, 148 insertions(+), 18 deletions(-) create mode 100644 inst/tinytest/test_saveInstruments.R 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/Tick2Sec.R b/R/Tick2Sec.R index 31e4b0e..02613f7 100644 --- a/R/Tick2Sec.R +++ b/R/Tick2Sec.R @@ -46,6 +46,8 @@ #' @param Symbols String names of instruments to convert #' @param overwrite TRUE/FALSE. If file already exists in savedir, should it be #' overwritten? +#' @param verbose logical. If \code{TRUE}, print progress messages. Default is +#' \code{FALSE}. #' @return \code{to_secBATV} returns an xts object of one second frequency. #' \code{alltick2sec} returns a list of files that were converted. #' @author gsee @@ -100,7 +102,8 @@ to_secBATV <- function(x) { alltick2sec <- function(getdir = '~/TRTH/tick/', savedir = '~/TRTH/sec/', Symbols=list.files(getdir), - overwrite = FALSE) { + overwrite = FALSE, + verbose = 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.") @@ -110,7 +113,7 @@ alltick2sec <- function(getdir = '~/TRTH/tick/', s=NULL `%dopar%` <- foreach::`%dopar%` foreach::foreach(s = Symbols) %dopar% { - cat("converting ", s, ' ...\n') + if (verbose) message("converting ", s) gdir <- paste(getdir, s, sep=gsep) if (file.exists(gdir)) { sdir <- paste(savedir, s, sep=ssep) diff --git a/R/saveInstruments.R b/R/saveInstruments.R index 94d85ea..e7ed836 100644 --- a/R/saveInstruments.R +++ b/R/saveInstruments.R @@ -171,7 +171,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) diff --git a/cran-comments.md b/cran-comments.md index 43233d9..51edf2b 100644 --- a/cran-comments.md +++ b/cran-comments.md @@ -9,21 +9,37 @@ ## Submission Notes -This is a resubmission of the previously archived package **FinancialInstrument**. The issues that led to archival have been addressed. +This is a resubmission of version 1.4.1, addressing the CRAN reviewer feedback +received after the 1.4.0 submission. -Ross Bennett, the previous CRAN maintainer, has agreed to the maintainer transition and has sent confirmation directly to CRAN. +### Changes addressing CRAN reviewer feedback -The following changes were made for this release: +* **Return-value documentation:** Added `\value` sections to all exported + functions that were previously missing them, describing the return class, + structure, and side-effects. -* 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. +* **Examples — file paths:** All examples that previously wrote to the current + working directory now use `tempdir()` / `tempfile()` and clean up with + `tryCatch(finally = ...)`. -Additional improvements made during the update include: +* **Examples — `FinancialInstrument:::.instrument`:** Removed all `:::` access + to the internal registry from documentation examples. Backup/restore patterns + now use the exported `saveInstruments()` / `reloadInstruments()` API. + +* **Examples — unmatched parenthesis:** Fixed the unmatched parenthesis in the + `ls_by_currency()` example. + +* **Examples — commented-out executable code:** Removed commented-out + executable alternatives from all examples. + +* **`\dontrun{}` vs `\donttest{}`:** Converted examples that can be run in a + standard R session from `\dontrun{}` to either runnable or `\donttest{}` + blocks. `\dontrun{}` is now reserved for genuinely unrunnable code. + +* **Console output (`alltick2sec`):** Added `verbose = FALSE` at the end of + the `alltick2sec()` signature. The previously unconditional `cat()` progress + message is now suppressed by default and uses `message()` when enabled, + preserving all existing return values. + +### There is no associated publication; no DOI or ISBN applies. -* 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. diff --git a/inst/tinytest/test_saveInstruments.R b/inst/tinytest/test_saveInstruments.R new file mode 100644 index 0000000..94016e6 --- /dev/null +++ b/inst/tinytest/test_saveInstruments.R @@ -0,0 +1,66 @@ +# Regression tests for saveInstruments / loadInstruments / reloadInstruments +# Covers: explicit output paths, RData and R/txt formats, state restore. + +test_dir <- tempfile("fi-test-save-") +dir.create(test_dir) + +# ---- helpers --------------------------------------------------------------- +cleanup <- function() { + unlink(test_dir, recursive = TRUE) + rm_instruments(keep.currencies = FALSE) +} +on.exit(cleanup(), add = TRUE) + +# ---- setup: define some instruments ---------------------------------------- +rm_instruments(keep.currencies = FALSE) +currency("USD") +stock("SAVE_SPY", currency = "USD") +stock("SAVE_DIA", currency = "USD") + +# ---- saveInstruments writes an RData file to explicit dir ------------------ +saveInstruments("test.RData", dir = test_dir) +expect_true(file.exists(file.path(test_dir, "test.RData"))) + +# ---- loadInstruments restores instruments from explicit path --------------- +rm_instruments(keep.currencies = FALSE) +expect_false(is.instrument.name("SAVE_SPY")) + +loadInstruments("test.RData", dir = test_dir) +expect_true(is.instrument.name("SAVE_SPY")) +expect_true(is.instrument.name("SAVE_DIA")) + +# ---- saveInstruments writes an .R (text) file to explicit dir -------------- +currency("USD") +stock("SAVE_SPY", currency = "USD") +saveInstruments("test.R", dir = test_dir) +expect_true(file.exists(file.path(test_dir, "test.R"))) + +# ---- loadInstruments reads the .R file from explicit path ------------------ +rm_instruments(keep.currencies = FALSE) +expect_false(is.instrument.name("SAVE_SPY")) + +loadInstruments("test.R", dir = test_dir) +expect_true(is.instrument.name("SAVE_SPY")) + +# ---- reloadInstruments replaces the registry -------------------------------- +currency("USD") +stock(c("SAVE_AA", "SAVE_BB"), currency = "USD") +saveInstruments("reload.RData", dir = test_dir) + +stock("SAVE_CC", currency = "USD") # add extra instrument +expect_true(is.instrument.name("SAVE_CC")) + +reloadInstruments("reload.RData", dir = test_dir) +expect_false(is.instrument.name("SAVE_CC")) # extra should be gone +expect_true(is.instrument.name("SAVE_AA")) + +# ---- file_name with full path (no dir argument) ---------------------------- +full_path <- tempfile(tmpdir = test_dir, fileext = ".RData") +currency("USD") +stock("SAVE_SPY", currency = "USD") +saveInstruments(full_path) +expect_true(file.exists(full_path)) + +rm_instruments(keep.currencies = FALSE) +loadInstruments(full_path) +expect_true(is.instrument.name("SAVE_SPY")) diff --git a/man/Tick2Sec.Rd b/man/Tick2Sec.Rd index cd27528..4a04d2e 100644 --- a/man/Tick2Sec.Rd +++ b/man/Tick2Sec.Rd @@ -11,7 +11,8 @@ alltick2sec( getdir = "~/TRTH/tick/", savedir = "~/TRTH/sec/", Symbols = list.files(getdir), - overwrite = FALSE + overwrite = FALSE, + verbose = FALSE ) } \arguments{ @@ -25,6 +26,9 @@ alltick2sec( \item{overwrite}{TRUE/FALSE. If file already exists in savedir, should it be overwritten?} + +\item{verbose}{logical. If \code{TRUE}, print progress messages. Default is +\code{FALSE}.} } \value{ \code{to_secBATV} returns an xts object of one second frequency. From 9d7fd4352ae7b5f24b6d0027c29c423cf2d78449 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Jul 2026 18:13:01 +0000 Subject: [PATCH 03/16] Fix test: dir.create needs recursive=TRUE for R CMD check temp path --- inst/tinytest/test_saveInstruments.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inst/tinytest/test_saveInstruments.R b/inst/tinytest/test_saveInstruments.R index 94016e6..fecb04f 100644 --- a/inst/tinytest/test_saveInstruments.R +++ b/inst/tinytest/test_saveInstruments.R @@ -2,7 +2,7 @@ # Covers: explicit output paths, RData and R/txt formats, state restore. test_dir <- tempfile("fi-test-save-") -dir.create(test_dir) +dir.create(test_dir, recursive = TRUE, showWarnings = FALSE) # ---- helpers --------------------------------------------------------------- cleanup <- function() { From 975f7c644aad3b9ee987d64279958f5783bc0ce0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Jul 2026 18:29:42 +0000 Subject: [PATCH 04/16] Fix test: use tempdir() directly, no subdirectory creation needed --- inst/tinytest/test_saveInstruments.R | 63 ++++++++++++++-------------- 1 file changed, 32 insertions(+), 31 deletions(-) diff --git a/inst/tinytest/test_saveInstruments.R b/inst/tinytest/test_saveInstruments.R index fecb04f..edaee15 100644 --- a/inst/tinytest/test_saveInstruments.R +++ b/inst/tinytest/test_saveInstruments.R @@ -1,15 +1,19 @@ # Regression tests for saveInstruments / loadInstruments / reloadInstruments -# Covers: explicit output paths, RData and R/txt formats, state restore. +# Covers: full-path save/load, RData and R/txt formats, dir argument, state restore. +# +# All test files are written directly into tempdir() (which always exists during +# R CMD check) to avoid issues with creating subdirectories in constrained envs. -test_dir <- tempfile("fi-test-save-") -dir.create(test_dir, recursive = TRUE, showWarnings = FALSE) +td <- tempdir() +f_rdata <- file.path(td, "fi_test_instr.RData") +f_r <- file.path(td, "fi_test_instr.R") +f_reload <- file.path(td, "fi_test_reload.RData") # ---- helpers --------------------------------------------------------------- -cleanup <- function() { - unlink(test_dir, recursive = TRUE) +on.exit({ + unlink(c(f_rdata, f_r, f_reload)) rm_instruments(keep.currencies = FALSE) -} -on.exit(cleanup(), add = TRUE) +}, add = TRUE) # ---- setup: define some instruments ---------------------------------------- rm_instruments(keep.currencies = FALSE) @@ -17,50 +21,47 @@ currency("USD") stock("SAVE_SPY", currency = "USD") stock("SAVE_DIA", currency = "USD") -# ---- saveInstruments writes an RData file to explicit dir ------------------ -saveInstruments("test.RData", dir = test_dir) -expect_true(file.exists(file.path(test_dir, "test.RData"))) +# ---- saveInstruments: full path, .RData ------------------------------------ +saveInstruments(f_rdata) +expect_true(file.exists(f_rdata)) -# ---- loadInstruments restores instruments from explicit path --------------- +# ---- loadInstruments: full path, .RData ------------------------------------ rm_instruments(keep.currencies = FALSE) expect_false(is.instrument.name("SAVE_SPY")) -loadInstruments("test.RData", dir = test_dir) +loadInstruments(f_rdata) expect_true(is.instrument.name("SAVE_SPY")) expect_true(is.instrument.name("SAVE_DIA")) -# ---- saveInstruments writes an .R (text) file to explicit dir -------------- +# ---- saveInstruments: full path, .R (text) --------------------------------- currency("USD") stock("SAVE_SPY", currency = "USD") -saveInstruments("test.R", dir = test_dir) -expect_true(file.exists(file.path(test_dir, "test.R"))) +saveInstruments(f_r) +expect_true(file.exists(f_r)) -# ---- loadInstruments reads the .R file from explicit path ------------------ +# ---- loadInstruments: full path, .R ---------------------------------------- rm_instruments(keep.currencies = FALSE) expect_false(is.instrument.name("SAVE_SPY")) -loadInstruments("test.R", dir = test_dir) +loadInstruments(f_r) expect_true(is.instrument.name("SAVE_SPY")) +# ---- saveInstruments with dir argument -------------------------------------- +currency("USD") +stock("SAVE_SPY", currency = "USD") +stock("SAVE_DIA", currency = "USD") +saveInstruments("fi_test_dir.RData", dir = td) +expect_true(file.exists(file.path(td, "fi_test_dir.RData"))) +unlink(file.path(td, "fi_test_dir.RData")) + # ---- reloadInstruments replaces the registry -------------------------------- currency("USD") stock(c("SAVE_AA", "SAVE_BB"), currency = "USD") -saveInstruments("reload.RData", dir = test_dir) +saveInstruments(f_reload) -stock("SAVE_CC", currency = "USD") # add extra instrument +stock("SAVE_CC", currency = "USD") expect_true(is.instrument.name("SAVE_CC")) -reloadInstruments("reload.RData", dir = test_dir) +reloadInstruments(f_reload) expect_false(is.instrument.name("SAVE_CC")) # extra should be gone expect_true(is.instrument.name("SAVE_AA")) - -# ---- file_name with full path (no dir argument) ---------------------------- -full_path <- tempfile(tmpdir = test_dir, fileext = ".RData") -currency("USD") -stock("SAVE_SPY", currency = "USD") -saveInstruments(full_path) -expect_true(file.exists(full_path)) - -rm_instruments(keep.currencies = FALSE) -loadInstruments(full_path) -expect_true(is.instrument.name("SAVE_SPY")) From ea6147cb7ec5a3fb678341636ed41d4d496608dd Mon Sep 17 00:00:00 2001 From: JustinMShea Date: Thu, 23 Jul 2026 13:19:30 -0500 Subject: [PATCH 05/16] Restore rm_by_currency public API --- NAMESPACE | 1 + R/ls_by_currency.R | 30 ++++++++++++++++++++++++++++++ man/ls_by_currency.Rd | 12 ++++++++++++ 3 files changed, 43 insertions(+) diff --git a/NAMESPACE b/NAMESPACE index d9f8ab5..8b3e0a0 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -103,6 +103,7 @@ export(redenominate) export(reloadInstruments) export(rm_FX) export(rm_bonds) +export(rm_by_currency) export(rm_by_expiry) export(rm_currencies) export(rm_derivatives) diff --git a/R/ls_by_currency.R b/R/ls_by_currency.R index bed1703..e947195 100755 --- a/R/ls_by_currency.R +++ b/R/ls_by_currency.R @@ -23,6 +23,10 @@ #' @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. @@ -55,6 +59,9 @@ #' ls_by_currency("GBP") #' ls_USD() #' ls_CAD() +#' +#' rm_by_currency(currency = "CAD") +#' #' }, #' finally = { #' if (file.exists(backup_path)) { @@ -131,7 +138,30 @@ ls_by_currency <- function( NULL } } +#' @export +#' @rdname ls_by_currency +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/man/ls_by_currency.Rd b/man/ls_by_currency.Rd index 95b9373..c69e176 100644 --- a/man/ls_by_currency.Rd +++ b/man/ls_by_currency.Rd @@ -2,6 +2,7 @@ % Please edit documentation in R/ls_by_currency.R \name{ls_by_currency} \alias{ls_by_currency} +\alias{rm_by_currency} \alias{ls_USD} \alias{ls_AUD} \alias{ls_GBP} @@ -16,6 +17,8 @@ \usage{ ls_by_currency(currency, pattern = NULL, match = TRUE, show.currencies = FALSE) +rm_by_currency(x, currency, keep.currencies = TRUE) + ls_USD(pattern = NULL, match = TRUE, show.currencies = FALSE) ls_AUD(pattern = NULL, match = TRUE, show.currencies = FALSE) @@ -46,6 +49,12 @@ matching `pattern` are returned.} \item{show.currencies}{Logical. Should currency instruments themselves be included in the returned names?} + +\item{x}{Character vector of instrument names to remove. If missing, all +instruments denominated in `currency` are selected.} + +\item{keep.currencies}{Logical. If `TRUE`, retain the currency instruments +themselves.} } \value{ A character vector containing instrument names denominated in the @@ -77,6 +86,9 @@ tryCatch( ls_by_currency("GBP") ls_USD() ls_CAD() + + rm_by_currency(currency = "CAD") + }, finally = { if (file.exists(backup_path)) { From 37e16e0d7380ec9b1087d203784445a62aea2c9a Mon Sep 17 00:00:00 2001 From: JustinMShea Date: Thu, 23 Jul 2026 13:29:01 -0500 Subject: [PATCH 06/16] #3 added `donttest` wraper back to the example, as run time is just over 5 seconds to test all DIA options chains. --- R/instrument.R | 3 ++- man/option_series.yahoo.Rd | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/R/instrument.R b/R/instrument.R index b388df3..2c989ae 100644 --- a/R/instrument.R +++ b/R/instrument.R @@ -720,10 +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 +#' \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) { diff --git a/man/option_series.yahoo.Rd b/man/option_series.yahoo.Rd index e3e7539..fc90ea9 100644 --- a/man/option_series.yahoo.Rd +++ b/man/option_series.yahoo.Rd @@ -52,10 +52,11 @@ Has only been tested with stock options. The options' currency should be the same as the underlying's. } \examples{ +\donttest{ option_series.yahoo('SPY') #only nearby calls and puts option_series.yahoo('DIA', Exp=NULL) #all chains ls_instruments() - +} } \references{ Yahoo Finance at "finance.yahoo.com" From ee943dc87fdc33af4a2158097f93abfdce86f031 Mon Sep 17 00:00:00 2001 From: JustinMShea Date: Thu, 23 Jul 2026 14:14:12 -0500 Subject: [PATCH 07/16] Strengthen instrument save and load tests --- inst/tinytest/test_saveInstruments.R | 201 ++++++++++++++++++++------- 1 file changed, 151 insertions(+), 50 deletions(-) diff --git a/inst/tinytest/test_saveInstruments.R b/inst/tinytest/test_saveInstruments.R index edaee15..db2f05e 100644 --- a/inst/tinytest/test_saveInstruments.R +++ b/inst/tinytest/test_saveInstruments.R @@ -1,67 +1,168 @@ -# Regression tests for saveInstruments / loadInstruments / reloadInstruments -# Covers: full-path save/load, RData and R/txt formats, dir argument, state restore. -# -# All test files are written directly into tempdir() (which always exists during -# R CMD check) to avoid issues with creating subdirectories in constrained envs. - -td <- tempdir() -f_rdata <- file.path(td, "fi_test_instr.RData") -f_r <- file.path(td, "fi_test_instr.R") -f_reload <- file.path(td, "fi_test_reload.RData") - -# ---- helpers --------------------------------------------------------------- -on.exit({ - unlink(c(f_rdata, f_r, f_reload)) - rm_instruments(keep.currencies = FALSE) -}, add = TRUE) - -# ---- setup: define some instruments ---------------------------------------- -rm_instruments(keep.currencies = FALSE) +# 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") -stock("SAVE_DIA", currency = "USD") -# ---- saveInstruments: full path, .RData ------------------------------------ +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)) -# ---- loadInstruments: full path, .RData ------------------------------------ -rm_instruments(keep.currencies = FALSE) -expect_false(is.instrument.name("SAVE_SPY")) +expect_true( + file.exists(f_rdata) +) + +rm_instruments( + keep.currencies = FALSE +) loadInstruments(f_rdata) -expect_true(is.instrument.name("SAVE_SPY")) -expect_true(is.instrument.name("SAVE_DIA")) -# ---- saveInstruments: full path, .R (text) --------------------------------- -currency("USD") -stock("SAVE_SPY", currency = "USD") +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)) -# ---- loadInstruments: full path, .R ---------------------------------------- -rm_instruments(keep.currencies = FALSE) -expect_false(is.instrument.name("SAVE_SPY")) +expect_true( + file.exists(f_r) +) -loadInstruments(f_r) -expect_true(is.instrument.name("SAVE_SPY")) +rm_instruments( + keep.currencies = FALSE +) -# ---- saveInstruments with dir argument -------------------------------------- -currency("USD") -stock("SAVE_SPY", currency = "USD") -stock("SAVE_DIA", currency = "USD") -saveInstruments("fi_test_dir.RData", dir = td) -expect_true(file.exists(file.path(td, "fi_test_dir.RData"))) -unlink(file.path(td, "fi_test_dir.RData")) +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 +) -# ---- reloadInstruments replaces the registry -------------------------------- currency("USD") -stock(c("SAVE_AA", "SAVE_BB"), 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")) +stock( + "SAVE_CC", + currency = "USD" +) + +expect_true( + is.instrument.name("SAVE_CC") +) reloadInstruments(f_reload) -expect_false(is.instrument.name("SAVE_CC")) # extra should be gone -expect_true(is.instrument.name("SAVE_AA")) + +expect_false( + is.instrument.name("SAVE_CC") +) + +expect_true( + is.instrument.name("SAVE_AA") +) + +expect_true( + is.instrument.name("SAVE_BB") +) From c521d0f636a24534d47a52b17b464138c30e912a Mon Sep 17 00:00:00 2001 From: JustinMShea Date: Thu, 23 Jul 2026 14:16:33 -0500 Subject: [PATCH 08/16] "Add rm_by_currency compatibility tests" --- inst/tinytest/test_rm_by_currency.R | 78 +++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 inst/tinytest/test_rm_by_currency.R 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") +) From 33d3a497f47b95c3d5515accb38d61de5788eef5 Mon Sep 17 00:00:00 2001 From: JustinMShea Date: Thu, 23 Jul 2026 14:21:53 -0500 Subject: [PATCH 09/16] Correct CRAN resubmission notes --- cran-comments.md | 43 ++++++++++--------------------------------- 1 file changed, 10 insertions(+), 33 deletions(-) diff --git a/cran-comments.md b/cran-comments.md index 51edf2b..fd7bb58 100644 --- a/cran-comments.md +++ b/cran-comments.md @@ -2,44 +2,21 @@ * Local Ubuntu Linux (22.04 and 24.04), R 4.6.1 (2026-06-24) * win-builder (R release and R-devel) +* Github Actions ## R CMD check results -0 errors | 0 warnings | 0 notes +0 errors | 0 warnings | 1 note -## Submission Notes +The remaining NOTE reports that FinancialInstrument was previously +archived on CRAN. This is expected for this resubmission. -This is a resubmission of version 1.4.1, addressing the CRAN reviewer feedback -received after the 1.4.0 submission. +## Submission notes -### Changes addressing CRAN reviewer feedback +This is a resubmission of version 1.4.1 addressing the CRAN +reviewer feedback received after the 1.4.0 submission. -* **Return-value documentation:** Added `\value` sections to all exported - functions that were previously missing them, describing the return class, - structure, and side-effects. - -* **Examples — file paths:** All examples that previously wrote to the current - working directory now use `tempdir()` / `tempfile()` and clean up with - `tryCatch(finally = ...)`. - -* **Examples — `FinancialInstrument:::.instrument`:** Removed all `:::` access - to the internal registry from documentation examples. Backup/restore patterns - now use the exported `saveInstruments()` / `reloadInstruments()` API. - -* **Examples — unmatched parenthesis:** Fixed the unmatched parenthesis in the - `ls_by_currency()` example. - -* **Examples — commented-out executable code:** Removed commented-out - executable alternatives from all examples. - -* **`\dontrun{}` vs `\donttest{}`:** Converted examples that can be run in a - standard R session from `\dontrun{}` to either runnable or `\donttest{}` - blocks. `\dontrun{}` is now reserved for genuinely unrunnable code. - -* **Console output (`alltick2sec`):** Added `verbose = FALSE` at the end of - the `alltick2sec()` signature. The previously unconditional `cat()` progress - message is now suppressed by default and uses `message()` when enabled, - preserving all existing return values. - -### There is no associated publication; no DOI or ISBN applies. +Ross Bennett, the previous maintainer, agreed to the maintainer +transition and provided confirmation directly to CRAN. +There is no associated publication; no DOI or ISBN applies. From e5de3e5a6f495fd2a98bf225ffe4b8b91df067b6 Mon Sep 17 00:00:00 2001 From: JustinMShea Date: Thu, 23 Jul 2026 15:48:12 -0500 Subject: [PATCH 10/16] #3 updated paths in examples for allticks2sec and args in related documentation. Clarified example in covert.log. refactored allticks2sec and imroved the call to dopar in case back end isn't registered. --- R/Tick2Sec.R | 177 +++++++++++++++++++++++++++++++++++------------- man/Tick2Sec.Rd | 56 ++++++++------- 2 files changed, 160 insertions(+), 73 deletions(-) diff --git a/R/Tick2Sec.R b/R/Tick2Sec.R index 02613f7..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,44 +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 verbose logical. If \code{TRUE}, print progress messages. Default is -#' \code{FALSE}. +#' @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. @@ -59,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 @@ -67,60 +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, - verbose = 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% { + + 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="/"))) @@ -137,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/man/Tick2Sec.Rd b/man/Tick2Sec.Rd index 4a04d2e..3f1233e 100644 --- a/man/Tick2Sec.Rd +++ b/man/Tick2Sec.Rd @@ -8,9 +8,9 @@ to_secBATV(x) alltick2sec( - getdir = "~/TRTH/tick/", - savedir = "~/TRTH/sec/", - Symbols = list.files(getdir), + getdir = getOption("FinancialInstrument.tickdir", NULL), + savedir = getOption("FinancialInstrument.secdir", NULL), + Symbols = NULL, overwrite = FALSE, verbose = FALSE ) @@ -18,51 +18,53 @@ alltick2sec( \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}, print progress messages. Default is -\code{FALSE}.} +\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. @@ -71,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{ From daeab47e9001cb90da04e2ccd698891f3a000d34 Mon Sep 17 00:00:00 2001 From: JustinMShea Date: Thu, 23 Jul 2026 15:57:36 -0500 Subject: [PATCH 11/16] Add blotter and quantstrat compatibility checks --- .../workflows/reverse-dependency-check.yaml | 106 ++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 .github/workflows/reverse-dependency-check.yaml diff --git a/.github/workflows/reverse-dependency-check.yaml b/.github/workflows/reverse-dependency-check.yaml new file mode 100644 index 0000000..1978fc4 --- /dev/null +++ b/.github/workflows/reverse-dependency-check.yaml @@ -0,0 +1,106 @@ +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 + + 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 + + - 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 blotter dependencies + shell: Rscript {0} + run: | + pak::local_install_dev_deps( + "revdeps/blotter", + upgrade = FALSE, + ask = FALSE + ) + + - name: Reinstall current FinancialInstrument + run: R CMD INSTALL . + + - name: Check blotter + shell: Rscript {0} + run: | + result <- rcmdcheck::rcmdcheck( + "revdeps/blotter", + args = c( + "--no-manual", + "--no-vignettes" + ), + error_on = "error" + ) + + print(result) + + - name: Install checked blotter + run: R CMD INSTALL revdeps/blotter + + - name: Install quantstrat dependencies + shell: Rscript {0} + run: | + pak::local_install_dev_deps( + "revdeps/quantstrat", + upgrade = FALSE, + ask = FALSE + ) + + - name: Reinstall tested dependency chain + run: | + R CMD INSTALL . + R CMD INSTALL revdeps/blotter + + - name: Check quantstrat + shell: Rscript {0} + run: | + result <- rcmdcheck::rcmdcheck( + "revdeps/quantstrat", + args = c( + "--no-manual", + "--no-vignettes" + ), + error_on = "error" + ) + + print(result) From 35399c9f02a7118785b28ad1de83afce4f2e4428 Mon Sep 17 00:00:00 2001 From: JustinMShea Date: Thu, 23 Jul 2026 16:14:09 -0500 Subject: [PATCH 12/16] Simplify reverse dependency checks --- .../workflows/reverse-dependency-check.yaml | 110 +++++++++++++++--- 1 file changed, 94 insertions(+), 16 deletions(-) diff --git a/.github/workflows/reverse-dependency-check.yaml b/.github/workflows/reverse-dependency-check.yaml index 1978fc4..e3ca297 100644 --- a/.github/workflows/reverse-dependency-check.yaml +++ b/.github/workflows/reverse-dependency-check.yaml @@ -17,6 +17,9 @@ jobs: name: blotter and quantstrat runs-on: ubuntu-latest + env: + _R_CHECK_FORCE_SUGGESTS_: false + steps: - name: Check out FinancialInstrument uses: actions/checkout@v4 @@ -32,6 +35,11 @@ jobs: with: extra-packages: | any::rcmdcheck + any::tinytest + any::testthat + any::PerformanceAnalytics + any::foreach + any::iterators - name: Install current FinancialInstrument run: R CMD INSTALL . @@ -48,18 +56,70 @@ jobs: https://github.com/braverock/quantstrat.git \ revdeps/quantstrat - - name: Install blotter dependencies + - name: Install remaining CRAN dependencies shell: Rscript {0} run: | - pak::local_install_dev_deps( - "revdeps/blotter", - upgrade = FALSE, - ask = FALSE + 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 <- read.dcf( + "DESCRIPTION", + fields = "Version" + )[1, 1] + + expected_blotter <- read.dcf( + "revdeps/blotter/DESCRIPTION", + fields = "Version" + )[1, 1] + + installed_fi <- as.character( + packageVersion("FinancialInstrument") + ) + + installed_blotter <- as.character( + packageVersion("blotter") + ) + + cat("FinancialInstrument:", installed_fi, "\n") + cat("blotter:", installed_blotter, "\n") + + stopifnot( + identical(installed_fi, expected_fi), + identical(installed_blotter, expected_blotter) + ) + - name: Check blotter shell: Rscript {0} run: | @@ -69,27 +129,44 @@ jobs: "--no-manual", "--no-vignettes" ), + build_args = "--no-build-vignettes", error_on = "error" ) print(result) - - name: Install checked blotter - run: R CMD INSTALL revdeps/blotter + - name: Reinstall tested dependency chain + run: | + R CMD INSTALL . + R CMD INSTALL revdeps/blotter + + - name: Install quantstrat + run: R CMD INSTALL revdeps/quantstrat - - name: Install quantstrat dependencies + - name: Verify quantstrat dependency chain shell: Rscript {0} run: | - pak::local_install_dev_deps( - "revdeps/quantstrat", - upgrade = FALSE, - ask = FALSE + library(FinancialInstrument) + library(blotter) + library(quantstrat) + + cat( + "FinancialInstrument:", + as.character(packageVersion("FinancialInstrument")), + "\n" ) - - name: Reinstall tested dependency chain - run: | - R CMD INSTALL . - R CMD INSTALL revdeps/blotter + cat( + "blotter:", + as.character(packageVersion("blotter")), + "\n" + ) + + cat( + "quantstrat:", + as.character(packageVersion("quantstrat")), + "\n" + ) - name: Check quantstrat shell: Rscript {0} @@ -100,6 +177,7 @@ jobs: "--no-manual", "--no-vignettes" ), + build_args = "--no-build-vignettes", error_on = "error" ) From fa181dd7e06a8c31b5eda4d79529c9e4b3294ad6 Mon Sep 17 00:00:00 2001 From: JustinMShea Date: Thu, 23 Jul 2026 17:19:32 -0500 Subject: [PATCH 13/16] relax verification script --- .../workflows/reverse-dependency-check.yaml | 51 ++++++++++++------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/.github/workflows/reverse-dependency-check.yaml b/.github/workflows/reverse-dependency-check.yaml index e3ca297..5136ea5 100644 --- a/.github/workflows/reverse-dependency-check.yaml +++ b/.github/workflows/reverse-dependency-check.yaml @@ -91,33 +91,46 @@ jobs: - name: Install blotter run: R CMD INSTALL revdeps/blotter - - name: Verify FinancialInstrument and blotter + - name: Verify FinancialInstrument and blotter shell: Rscript {0} run: | - expected_fi <- read.dcf( - "DESCRIPTION", - fields = "Version" - )[1, 1] - - expected_blotter <- read.dcf( - "revdeps/blotter/DESCRIPTION", - fields = "Version" - )[1, 1] - - installed_fi <- as.character( - packageVersion("FinancialInstrument") + expected_fi <- package_version( + read.dcf( + "DESCRIPTION", + fields = "Version" + )[1, 1] ) - installed_blotter <- as.character( - packageVersion("blotter") + expected_blotter <- package_version( + read.dcf( + "revdeps/blotter/DESCRIPTION", + fields = "Version" + )[1, 1] ) - cat("FinancialInstrument:", installed_fi, "\n") - cat("blotter:", installed_blotter, "\n") + installed_fi <- packageVersion( + "FinancialInstrument" + ) + + installed_blotter <- packageVersion( + "blotter" + ) + + cat( + "FinancialInstrument:", + as.character(installed_fi), + "\n" + ) + + cat( + "blotter:", + as.character(installed_blotter), + "\n" + ) stopifnot( - identical(installed_fi, expected_fi), - identical(installed_blotter, expected_blotter) + installed_fi == expected_fi, + installed_blotter == expected_blotter ) - name: Check blotter From be0af4de91fd9d74c13cae99885fe8fa44b2963f Mon Sep 17 00:00:00 2001 From: JustinMShea Date: Thu, 23 Jul 2026 17:20:17 -0500 Subject: [PATCH 14/16] fix indent --- .github/workflows/reverse-dependency-check.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/reverse-dependency-check.yaml b/.github/workflows/reverse-dependency-check.yaml index 5136ea5..c7becc1 100644 --- a/.github/workflows/reverse-dependency-check.yaml +++ b/.github/workflows/reverse-dependency-check.yaml @@ -91,7 +91,7 @@ jobs: - name: Install blotter run: R CMD INSTALL revdeps/blotter - - name: Verify FinancialInstrument and blotter + - name: Verify FinancialInstrument and blotter shell: Rscript {0} run: | expected_fi <- package_version( From 0e61e7f4ee7a944745f4e5ed22eb9855358bb994 Mon Sep 17 00:00:00 2001 From: JustinMShea Date: Thu, 23 Jul 2026 17:53:43 -0500 Subject: [PATCH 15/16] quantstrat fails, but likely independent of updates to FinancialIntrument --- .../workflows/reverse-dependency-check.yaml | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/.github/workflows/reverse-dependency-check.yaml b/.github/workflows/reverse-dependency-check.yaml index c7becc1..14d8171 100644 --- a/.github/workflows/reverse-dependency-check.yaml +++ b/.github/workflows/reverse-dependency-check.yaml @@ -182,6 +182,8 @@ jobs: ) - name: Check quantstrat + id: check_quantstrat + continue-on-error: true shell: Rscript {0} run: | result <- rcmdcheck::rcmdcheck( @@ -191,7 +193,32 @@ jobs: "--no-vignettes" ), build_args = "--no-build-vignettes", - error_on = "error" + check_dir = "revdep-checks", + error_on = "never" ) print(result) + + if (length(result$errors)) { + quit(status = 1) + } + + - name: Show quantstrat test output + if: always() + run: | + find revdep-checks \ + -type f \ + \( -name 'testthat.Rout*' -o -name '00check.log' \) \ + -print \ + -exec cat {} \; + + - name: Upload quantstrat check results + if: always() + uses: actions/upload-artifact@v4 + with: + name: quantstrat-check-results + path: revdep-checks + + - name: Fail on quantstrat errors + if: steps.check_quantstrat.outcome == 'failure' + run: exit 1 From e75940ff06c06fea886f154aa711b453ebf7e30e Mon Sep 17 00:00:00 2001 From: JustinMShea Date: Thu, 23 Jul 2026 18:14:32 -0500 Subject: [PATCH 16/16] quanstrat fails identically, regardless of current FinancialInstrument updates/fixes so make this test informal for now. --- .../workflows/reverse-dependency-check.yaml | 22 ++----------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/.github/workflows/reverse-dependency-check.yaml b/.github/workflows/reverse-dependency-check.yaml index 14d8171..2e3bc25 100644 --- a/.github/workflows/reverse-dependency-check.yaml +++ b/.github/workflows/reverse-dependency-check.yaml @@ -182,7 +182,6 @@ jobs: ) - name: Check quantstrat - id: check_quantstrat continue-on-error: true shell: Rscript {0} run: | @@ -193,32 +192,15 @@ jobs: "--no-vignettes" ), build_args = "--no-build-vignettes", - check_dir = "revdep-checks", + check_dir = "quantstrat-check", error_on = "never" ) print(result) - if (length(result$errors)) { - quit(status = 1) - } - - - name: Show quantstrat test output - if: always() - run: | - find revdep-checks \ - -type f \ - \( -name 'testthat.Rout*' -o -name '00check.log' \) \ - -print \ - -exec cat {} \; - - name: Upload quantstrat check results if: always() uses: actions/upload-artifact@v4 with: name: quantstrat-check-results - path: revdep-checks - - - name: Fail on quantstrat errors - if: steps.check_quantstrat.outcome == 'failure' - run: exit 1 + path: quantstrat-check