diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c5376c92..80af1448 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -969,6 +969,31 @@ jobs: R CMD INSTALL bindings/r Rscript -e 'library(testthat); library(wickra); test_dir("bindings/r/tests/testthat", stop_on_failure = TRUE)' + # Full R CMD check, gated on the documentation problems r-universe surfaces + # (undocumented exported objects, codoc mismatches) that R CMD INSTALL above + # does not catch — exactly what shipped stale to r-universe with the 0.9.3 + # data layer. Ubuntu-only; these checks are platform-independent. Vignettes + # are skipped here (building them needs pandoc and the vignette is exercised + # in the next step), so the two --no-build-vignettes warnings are ignored. + - name: R CMD check (documentation & consistency, like r-universe) + if: matrix.os == 'ubuntu-latest' + shell: bash + run: | + export WICKRA_INCLUDE_DIR="${WICKRA_INCLUDE_DIR//\\//}" + export WICKRA_LIB_DIR="${WICKRA_LIB_DIR//\\//}" + R CMD build bindings/r --no-build-vignettes --no-manual + R CMD check wickra_*.tar.gz --no-manual --no-vignettes --no-tests || true + log=$(find . -maxdepth 2 -name 00check.log | head -1) + echo "::group::00check.log"; cat "$log"; echo "::endgroup::" + problems=$(grep -E '\.\.\. (WARNING|ERROR)' "$log" \ + | grep -vE "checking (files in .vignettes.|package vignettes)" || true) + if [ -n "$problems" ]; then + echo "::error::R CMD check found problems (run roxygen2::roxygenise() in bindings/r if the docs are stale):" + echo "$problems" + exit 1 + fi + echo "R CMD check: documentation and consistency clean." + - name: Build the vignette code shell: bash # The getting-started vignette runs at R CMD check time on r-universe / diff --git a/bindings/r/NAMESPACE b/bindings/r/NAMESPACE index 426f9672..74e5112b 100644 --- a/bindings/r/NAMESPACE +++ b/bindings/r/NAMESPACE @@ -1,7 +1,7 @@ # Generated by roxygen2: do not edit by hand +S3method(base::flush,wickra_indicator) S3method(batch,wickra_indicator) -S3method(flush,wickra_indicator) S3method(is_ready,wickra_indicator) S3method(name,wickra_indicator) S3method(push,wickra_indicator) diff --git a/bindings/r/R/indicators.R b/bindings/r/R/indicators.R index 956dffa0..6bfaa44b 100644 --- a/bindings/r/R/indicators.R +++ b/bindings/r/R/indicators.R @@ -479,7 +479,14 @@ Camarilla <- function() { .wk_obj("camarilla", ptr, "Camarilla") } -#' CandleReader: parse OHLCV candles from a CSV string +#' Parse OHLCV candles from a CSV string +#' +#' Builds a reader over an in-memory CSV string with a header row and OHLCV +#' columns. Pass the result to [read()] to get every parsed candle. +#' +#' @param csv A length-one character string of CSV text (a header row followed +#' by OHLCV data rows). +#' @return A `wickra_indicator` candle reader; read its candles with [read()]. #' @keywords internal #' @export CandleReader <- function(csv) { @@ -2687,7 +2694,14 @@ RenkoTrailingStop <- function(block_size) { .wk_obj("renko_trailing_stop", ptr, "RenkoTrailingStop") } -#' Resampler indicator +#' Resample candles to a higher timeframe +#' +#' Aggregates a stream of candles into higher-timeframe bars. Feed candles with +#' [update()] (it returns a completed higher-timeframe bar, or `NULL` while the +#' current bar is still open) and emit the final, still-open bar with [flush()]. +#' +#' @param timeframe Integer number of input candles per higher-timeframe bar. +#' @return A `wickra_indicator` resampler. #' @keywords internal #' @export Resampler <- function(timeframe) { @@ -3487,7 +3501,15 @@ Thrusting <- function() { .wk_obj("thrusting", ptr, "Thrusting") } -#' TickAggregator indicator +#' Aggregate trade ticks into time-bucketed candles +#' +#' Builds OHLCV candles from a stream of trade ticks. Feed ticks with [push()], +#' which returns the candles closed by that tick. +#' +#' @param bucket Time-bucket width, in the same unit as the tick timestamps. +#' @param gap_fill Logical; if `TRUE`, empty buckets with no trades are still +#' emitted (carried forward) instead of skipped. +#' @return A `wickra_indicator` tick aggregator; feed it ticks with [push()]. #' @keywords internal #' @export TickAggregator <- function(bucket, gap_fill) { @@ -4151,6 +4173,13 @@ Zlema <- function(period) { #' 15 = 1M). `base_url` overrides the endpoint (`NULL` = production). Not #' available in the wasm (r-universe/webR) build, which has no raw sockets. #' +#' @param symbols Comma-separated trading symbols (case-insensitive), e.g. +#' `"btcusdt,ethusdt"`. +#' @param interval Integer interval code `0:15` (`0` = 1s, `1` = 1m, ..., +#' `12` = 1d, `13` = 3d, `14` = 1w, `15` = 1M). +#' @param base_url Optional endpoint override; `NULL` uses production. +#' @return A `wickra_binance_feed`; poll it with [binance_next()] and close it +#' with [binance_close()]. #' @keywords internal #' @export BinanceFeed <- function(symbols, interval, base_url = NULL) { @@ -4165,6 +4194,10 @@ BinanceFeed <- function(symbols, interval, base_url = NULL) { #' `high`, `low`, `close`, `volume`, `open_time`, `is_closed`) when an event #' arrives, or `NULL` on timeout. Errors once the stream is closed. #' +#' @param feed A `wickra_binance_feed` created by [BinanceFeed()]. +#' @param timeout_ms Poll timeout in milliseconds. +#' @return A named list (`symbol`, `open`, `high`, `low`, `close`, `volume`, +#' `open_time`, `is_closed`) for an event, or `NULL` on timeout. #' @keywords internal #' @export binance_next <- function(feed, timeout_ms = 1000) { @@ -4173,6 +4206,8 @@ binance_next <- function(feed, timeout_ms = 1000) { #' Close a Binance feed #' +#' @param feed A `wickra_binance_feed` created by [BinanceFeed()]. +#' @return `NULL`, invisibly. #' @keywords internal #' @export binance_close <- function(feed) { @@ -4190,6 +4225,14 @@ binance_close <- function(feed) { #' `volume`, `timestamp`. Blocks until the response arrives. Not available in the #' wasm (r-universe/webR) build, which has no raw sockets. #' +#' @param symbol Trading symbol (case-insensitive), e.g. `"btcusdt"`. +#' @param interval Integer interval code `0:15` (see [BinanceFeed()]). +#' @param limit Number of klines to fetch (`1:1000`). +#' @param start_ms,end_ms Optional inclusive Unix-millisecond bounds; a negative +#' value means unset. +#' @param base_url Optional host override; `NULL` uses production. +#' @return An `n x 6` numeric matrix with columns `open`, `high`, `low`, +#' `close`, `volume`, `timestamp`. #' @keywords internal #' @export fetch_binance_klines <- function(symbol, interval, limit, start_ms = -1, diff --git a/bindings/r/R/methods.R b/bindings/r/R/methods.R index 045f6719..4b93419f 100644 --- a/bindings/r/R/methods.R +++ b/bindings/r/R/methods.R @@ -151,7 +151,7 @@ name.wickra_indicator <- function(object) { #' @param timestamp Trade timestamp, in the same unit as the aggregator bucket. #' @return A numeric matrix with six named columns (possibly zero rows). #' @examples -#' agg <- TickAggregator(1000) +#' agg <- TickAggregator(1000, FALSE) #' push(agg, 100, 1, 0) #' push(agg, 102, 1, 1000) # closes the first bucket #' @export diff --git a/bindings/r/man/AwesomeOscillatorHistogram.Rd b/bindings/r/man/AwesomeOscillatorHistogram.Rd index 92155d91..f8e1f3f3 100644 --- a/bindings/r/man/AwesomeOscillatorHistogram.Rd +++ b/bindings/r/man/AwesomeOscillatorHistogram.Rd @@ -4,7 +4,7 @@ \alias{AwesomeOscillatorHistogram} \title{AwesomeOscillatorHistogram indicator} \usage{ -AwesomeOscillatorHistogram(fast, slow, sma_period) +AwesomeOscillatorHistogram(fast, slow, lookback) } \description{ AwesomeOscillatorHistogram indicator diff --git a/bindings/r/man/BinanceFeed.Rd b/bindings/r/man/BinanceFeed.Rd new file mode 100644 index 00000000..83b48ef3 --- /dev/null +++ b/bindings/r/man/BinanceFeed.Rd @@ -0,0 +1,29 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/indicators.R +\name{BinanceFeed} +\alias{BinanceFeed} +\title{Connect to a live Binance kline feed} +\usage{ +BinanceFeed(symbols, interval, base_url = NULL) +} +\arguments{ +\item{symbols}{Comma-separated trading symbols (case-insensitive), e.g. +\code{"btcusdt,ethusdt"}.} + +\item{interval}{Integer interval code \code{0:15} (\code{0} = 1s, \code{1} = 1m, ..., +\code{12} = 1d, \code{13} = 3d, \code{14} = 1w, \code{15} = 1M).} + +\item{base_url}{Optional endpoint override; \code{NULL} uses production.} +} +\value{ +A \code{wickra_binance_feed}; poll it with \code{\link[=binance_next]{binance_next()}} and close it +with \code{\link[=binance_close]{binance_close()}}. +} +\description{ +Opens a live Binance kline stream for one or more comma-separated \code{symbols} +(case-insensitive) at the given \code{interval} code (an integer \code{0:15}, the same +order as the other bindings: 0 = 1s, 1 = 1m, ... 12 = 1d, 13 = 3d, 14 = 1w, +15 = 1M). \code{base_url} overrides the endpoint (\code{NULL} = production). Not +available in the wasm (r-universe/webR) build, which has no raw sockets. +} +\keyword{internal} diff --git a/bindings/r/man/CandleReader.Rd b/bindings/r/man/CandleReader.Rd new file mode 100644 index 00000000..0e51db30 --- /dev/null +++ b/bindings/r/man/CandleReader.Rd @@ -0,0 +1,20 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/indicators.R +\name{CandleReader} +\alias{CandleReader} +\title{Parse OHLCV candles from a CSV string} +\usage{ +CandleReader(csv) +} +\arguments{ +\item{csv}{A length-one character string of CSV text (a header row followed +by OHLCV data rows).} +} +\value{ +A \code{wickra_indicator} candle reader; read its candles with \code{\link[=read]{read()}}. +} +\description{ +Builds a reader over an in-memory CSV string with a header row and OHLCV +columns. Pass the result to \code{\link[=read]{read()}} to get every parsed candle. +} +\keyword{internal} diff --git a/bindings/r/man/Resampler.Rd b/bindings/r/man/Resampler.Rd new file mode 100644 index 00000000..893a669e --- /dev/null +++ b/bindings/r/man/Resampler.Rd @@ -0,0 +1,20 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/indicators.R +\name{Resampler} +\alias{Resampler} +\title{Resample candles to a higher timeframe} +\usage{ +Resampler(timeframe) +} +\arguments{ +\item{timeframe}{Integer number of input candles per higher-timeframe bar.} +} +\value{ +A \code{wickra_indicator} resampler. +} +\description{ +Aggregates a stream of candles into higher-timeframe bars. Feed candles with +\code{\link[=update]{update()}} (it returns a completed higher-timeframe bar, or \code{NULL} while the +current bar is still open) and emit the final, still-open bar with \code{\link[=flush]{flush()}}. +} +\keyword{internal} diff --git a/bindings/r/man/TickAggregator.Rd b/bindings/r/man/TickAggregator.Rd new file mode 100644 index 00000000..5230129d --- /dev/null +++ b/bindings/r/man/TickAggregator.Rd @@ -0,0 +1,22 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/indicators.R +\name{TickAggregator} +\alias{TickAggregator} +\title{Aggregate trade ticks into time-bucketed candles} +\usage{ +TickAggregator(bucket, gap_fill) +} +\arguments{ +\item{bucket}{Time-bucket width, in the same unit as the tick timestamps.} + +\item{gap_fill}{Logical; if \code{TRUE}, empty buckets with no trades are still +emitted (carried forward) instead of skipped.} +} +\value{ +A \code{wickra_indicator} tick aggregator; feed it ticks with \code{\link[=push]{push()}}. +} +\description{ +Builds OHLCV candles from a stream of trade ticks. Feed ticks with \code{\link[=push]{push()}}, +which returns the candles closed by that tick. +} +\keyword{internal} diff --git a/bindings/r/man/binance_close.Rd b/bindings/r/man/binance_close.Rd new file mode 100644 index 00000000..362f627b --- /dev/null +++ b/bindings/r/man/binance_close.Rd @@ -0,0 +1,18 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/indicators.R +\name{binance_close} +\alias{binance_close} +\title{Close a Binance feed} +\usage{ +binance_close(feed) +} +\arguments{ +\item{feed}{A \code{wickra_binance_feed} created by \code{\link[=BinanceFeed]{BinanceFeed()}}.} +} +\value{ +\code{NULL}, invisibly. +} +\description{ +Close a Binance feed +} +\keyword{internal} diff --git a/bindings/r/man/binance_next.Rd b/bindings/r/man/binance_next.Rd new file mode 100644 index 00000000..d36b587e --- /dev/null +++ b/bindings/r/man/binance_next.Rd @@ -0,0 +1,23 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/indicators.R +\name{binance_next} +\alias{binance_next} +\title{Poll a Binance feed for the next kline event} +\usage{ +binance_next(feed, timeout_ms = 1000) +} +\arguments{ +\item{feed}{A \code{wickra_binance_feed} created by \code{\link[=BinanceFeed]{BinanceFeed()}}.} + +\item{timeout_ms}{Poll timeout in milliseconds.} +} +\value{ +A named list (\code{symbol}, \code{open}, \code{high}, \code{low}, \code{close}, \code{volume}, +\code{open_time}, \code{is_closed}) for an event, or \code{NULL} on timeout. +} +\description{ +Waits up to \code{timeout_ms} milliseconds. Returns a named list (\code{symbol}, \code{open}, +\code{high}, \code{low}, \code{close}, \code{volume}, \code{open_time}, \code{is_closed}) when an event +arrives, or \code{NULL} on timeout. Errors once the stream is closed. +} +\keyword{internal} diff --git a/bindings/r/man/fetch_binance_klines.Rd b/bindings/r/man/fetch_binance_klines.Rd new file mode 100644 index 00000000..c6c3a496 --- /dev/null +++ b/bindings/r/man/fetch_binance_klines.Rd @@ -0,0 +1,41 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/indicators.R +\name{fetch_binance_klines} +\alias{fetch_binance_klines} +\title{Fetch historical Binance klines over REST} +\usage{ +fetch_binance_klines( + symbol, + interval, + limit, + start_ms = -1, + end_ms = -1, + base_url = NULL +) +} +\arguments{ +\item{symbol}{Trading symbol (case-insensitive), e.g. \code{"btcusdt"}.} + +\item{interval}{Integer interval code \code{0:15} (see \code{\link[=BinanceFeed]{BinanceFeed()}}).} + +\item{limit}{Number of klines to fetch (\code{1:1000}).} + +\item{start_ms, end_ms}{Optional inclusive Unix-millisecond bounds; a negative +value means unset.} + +\item{base_url}{Optional host override; \code{NULL} uses production.} +} +\value{ +An \verb{n x 6} numeric matrix with columns \code{open}, \code{high}, \code{low}, +\code{close}, \code{volume}, \code{timestamp}. +} +\description{ +Downloads up to \code{limit} (\code{1:1000}) historical klines for \code{symbol} at the given +\code{interval} code (an integer \code{0:15}, the same order as the other bindings). +\code{start_ms}/\code{end_ms} are optional inclusive Unix-millisecond bounds (a negative +value means unset); \code{base_url} overrides the host (\code{NULL} = production). Returns +an \verb{n x 6} numeric matrix with columns \code{open}, \code{high}, \code{low}, \code{close}, +\code{volume}, \code{timestamp}. Blocks until the response arrives. Not available in the +wasm (r-universe/webR) build, which has no raw sockets. +} +\keyword{internal} diff --git a/bindings/r/man/flush.wickra_indicator.Rd b/bindings/r/man/flush.wickra_indicator.Rd new file mode 100644 index 00000000..d31f82d7 --- /dev/null +++ b/bindings/r/man/flush.wickra_indicator.Rd @@ -0,0 +1,25 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/methods.R +\name{flush.wickra_indicator} +\alias{flush.wickra_indicator} +\title{Flush a resampler's final candle} +\usage{ +\method{flush}{wickra_indicator}(con) +} +\arguments{ +\item{con}{A \code{wickra_indicator} created by \code{\link[=Resampler]{Resampler()}}.} +} +\value{ +A named numeric vector (\code{open}, \code{high}, \code{low}, \code{close}, \code{volume}, +\code{timestamp}), or \code{NULL} if nothing is pending. +} +\description{ +Emit the final, still-open candle a \code{\link[=Resampler]{Resampler()}} is aggregating (the partial +higher-timeframe bar that no later input has closed yet). Extends the base +generic \code{\link[base:flush]{base::flush()}}. +} +\examples{ +r <- Resampler(5) +for (t in 0:6) update(r, 100 + t, 101 + t, 99 + t, 100 + t, 10, t) +flush(r) +} diff --git a/bindings/r/man/is_ready.Rd b/bindings/r/man/is_ready.Rd new file mode 100644 index 00000000..a82aa358 --- /dev/null +++ b/bindings/r/man/is_ready.Rd @@ -0,0 +1,26 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/methods.R +\name{is_ready} +\alias{is_ready} +\alias{is_ready.wickra_indicator} +\title{Whether an indicator has consumed enough input to emit a value} +\usage{ +is_ready(object) + +\method{is_ready}{wickra_indicator}(object) +} +\arguments{ +\item{object}{A \code{wickra_indicator}.} +} +\value{ +A single logical. +} +\description{ +Not available for the alt-chart bar builders, which have no warmup. +} +\examples{ +sma <- Sma(3) +is_ready(sma) # FALSE +for (x in c(1, 2, 3)) update(sma, x) +is_ready(sma) # TRUE +} diff --git a/bindings/r/man/name.Rd b/bindings/r/man/name.Rd new file mode 100644 index 00000000..41c631ac --- /dev/null +++ b/bindings/r/man/name.Rd @@ -0,0 +1,24 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/methods.R +\name{name} +\alias{name} +\alias{name.wickra_indicator} +\title{Canonical name of an indicator} +\usage{ +name(object) + +\method{name}{wickra_indicator}(object) +} +\arguments{ +\item{object}{A \code{wickra_indicator}.} +} +\value{ +A single character string. +} +\description{ +Returns the stable, human-readable name of the indicator (the same name +reported by every other Wickra binding), e.g. \code{"SMA"} for \code{\link[=Sma]{Sma()}}. +} +\examples{ +name(Sma(14)) # "SMA" +} diff --git a/bindings/r/man/push.Rd b/bindings/r/man/push.Rd new file mode 100644 index 00000000..d0c08154 --- /dev/null +++ b/bindings/r/man/push.Rd @@ -0,0 +1,33 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/methods.R +\name{push} +\alias{push} +\alias{push.wickra_indicator} +\title{Push a trade tick into a tick aggregator} +\usage{ +push(object, price, size, timestamp) + +\method{push}{wickra_indicator}(object, price, size, timestamp) +} +\arguments{ +\item{object}{A \code{wickra_indicator} created by \code{\link[=TickAggregator]{TickAggregator()}}.} + +\item{price}{Trade price.} + +\item{size}{Trade size (volume).} + +\item{timestamp}{Trade timestamp, in the same unit as the aggregator bucket.} +} +\value{ +A numeric matrix with six named columns (possibly zero rows). +} +\description{ +Feeds one trade tick to a \code{\link[=TickAggregator]{TickAggregator()}} and returns the candles it +closed as a numeric matrix with columns \code{open}, \code{high}, \code{low}, \code{close}, +\code{volume}, \code{timestamp} (zero rows while the open bar merely grows). +} +\examples{ +agg <- TickAggregator(1000, FALSE) +push(agg, 100, 1, 0) +push(agg, 102, 1, 1000) # closes the first bucket +} diff --git a/bindings/r/man/read.Rd b/bindings/r/man/read.Rd new file mode 100644 index 00000000..19cd4d06 --- /dev/null +++ b/bindings/r/man/read.Rd @@ -0,0 +1,25 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/methods.R +\name{read} +\alias{read} +\alias{read.wickra_indicator} +\title{Read every candle parsed by a CSV candle reader} +\usage{ +read(object) + +\method{read}{wickra_indicator}(object) +} +\arguments{ +\item{object}{A \code{wickra_indicator} created by \code{\link[=CandleReader]{CandleReader()}}.} +} +\value{ +A numeric matrix with six named columns (zero rows for an empty CSV). +} +\description{ +Returns all the candles a \code{\link[=CandleReader]{CandleReader()}} parsed from its CSV, as a numeric +matrix with columns \code{open}, \code{high}, \code{low}, \code{close}, \code{volume}, \code{timestamp}. +} +\examples{ +r <- CandleReader("timestamp,open,high,low,close,volume\n0,100,101,99,100.5,10\n") +read(r) +} diff --git a/bindings/r/man/sample_ohlcv.Rd b/bindings/r/man/sample_ohlcv.Rd index 9f52c135..b06deb61 100644 --- a/bindings/r/man/sample_ohlcv.Rd +++ b/bindings/r/man/sample_ohlcv.Rd @@ -7,12 +7,12 @@ \format{ A data frame with 250 rows and 6 columns: \describe{ - \item{date}{Trading date (\code{Date}).} - \item{open}{Opening price.} - \item{high}{Session high.} - \item{low}{Session low.} - \item{close}{Closing price.} - \item{volume}{Traded volume.} +\item{date}{Trading date (\code{Date}).} +\item{open}{Opening price.} +\item{high}{Session high.} +\item{low}{Session low.} +\item{close}{Closing price.} +\item{volume}{Traded volume.} } } \usage{ diff --git a/bindings/r/man/warmup_period.Rd b/bindings/r/man/warmup_period.Rd new file mode 100644 index 00000000..5842c0fe --- /dev/null +++ b/bindings/r/man/warmup_period.Rd @@ -0,0 +1,24 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/methods.R +\name{warmup_period} +\alias{warmup_period} +\alias{warmup_period.wickra_indicator} +\title{Number of updates an indicator needs before it produces a value} +\usage{ +warmup_period(object) + +\method{warmup_period}{wickra_indicator}(object) +} +\arguments{ +\item{object}{A \code{wickra_indicator}.} +} +\value{ +A single integer: the warmup period. +} +\description{ +Not available for the alt-chart bar builders (\code{\link[=RenkoBars]{RenkoBars()}}, \code{\link[=KagiBars]{KagiBars()}}, +\code{\link[=PointAndFigureBars]{PointAndFigureBars()}}, …), which have no warmup. +} +\examples{ +warmup_period(Sma(14)) # 14 +} diff --git a/bindings/r/man/wickra-package.Rd b/bindings/r/man/wickra-package.Rd index 077701fc..4a6c94ee 100644 --- a/bindings/r/man/wickra-package.Rd +++ b/bindings/r/man/wickra-package.Rd @@ -18,6 +18,7 @@ Useful links: \itemize{ \item \url{https://github.com/wickra-lib/wickra} \item \url{https://docs.wickra.org} + \item \url{https://wickra-lib.r-universe.dev} \item Report bugs at \url{https://github.com/wickra-lib/wickra/issues} }