diff --git a/.Rbuildignore b/.Rbuildignore index fcb397c48..8eb10b69e 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -106,3 +106,7 @@ src/ta-lib/Makefile.in ^codegen$ ^CRAN-SUBMISSION$ ^tests/testthat/test-parity.R + +## Claude.md +^src/ta-lib/CLAUDE.md +^src/ta-lib/.claude \ No newline at end of file diff --git a/NAMESPACE b/NAMESPACE index 90d8b5b0d..5d7e079b9 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -33,6 +33,13 @@ S3method(aroon_oscillator,default) S3method(aroon_oscillator,ggplot) S3method(aroon_oscillator,matrix) S3method(aroon_oscillator,plotly) +S3method(as.maType,double) +S3method(as.maType,integer) +S3method(as.maType,maType) +S3method(average_deviation,data.frame) +S3method(average_deviation,default) +S3method(average_deviation,matrix) +S3method(average_deviation,numeric) S3method(average_directional_movement_index,data.frame) S3method(average_directional_movement_index,default) S3method(average_directional_movement_index,ggplot) @@ -322,9 +329,17 @@ S3method(mesa_adaptive_moving_average,ggplot) S3method(mesa_adaptive_moving_average,matrix) S3method(mesa_adaptive_moving_average,numeric) S3method(mesa_adaptive_moving_average,plotly) +S3method(midpoint_period,data.frame) +S3method(midpoint_period,default) +S3method(midpoint_period,ggplot) +S3method(midpoint_period,matrix) +S3method(midpoint_period,numeric) +S3method(midpoint_period,plotly) S3method(midpoint_price,data.frame) S3method(midpoint_price,default) +S3method(midpoint_price,ggplot) S3method(midpoint_price,matrix) +S3method(midpoint_price,plotly) S3method(minus_directional_indicator,data.frame) S3method(minus_directional_indicator,default) S3method(minus_directional_indicator,ggplot) @@ -411,18 +426,6 @@ S3method(plus_directional_movement,matrix) S3method(plus_directional_movement,plotly) S3method(print,talib_chart) S3method(print,talib_gg_chart) -S3method(rate_of_change,data.frame) -S3method(rate_of_change,default) -S3method(rate_of_change,ggplot) -S3method(rate_of_change,matrix) -S3method(rate_of_change,numeric) -S3method(rate_of_change,plotly) -S3method(ratio_of_change,data.frame) -S3method(ratio_of_change,default) -S3method(ratio_of_change,ggplot) -S3method(ratio_of_change,matrix) -S3method(ratio_of_change,numeric) -S3method(ratio_of_change,plotly) S3method(relative_strength_index,data.frame) S3method(relative_strength_index,default) S3method(relative_strength_index,ggplot) @@ -443,14 +446,8 @@ S3method(rolling_beta,default) S3method(rolling_beta,numeric) S3method(rolling_correlation,default) S3method(rolling_correlation,numeric) -S3method(rolling_max,default) -S3method(rolling_max,numeric) -S3method(rolling_min,default) -S3method(rolling_min,numeric) S3method(rolling_standard_deviation,default) S3method(rolling_standard_deviation,numeric) -S3method(rolling_sum,default) -S3method(rolling_sum,numeric) S3method(rolling_variance,default) S3method(rolling_variance,numeric) S3method(separating_lines,data.frame) @@ -636,6 +633,11 @@ S3method(upside_gap_2_crows,default) S3method(upside_gap_2_crows,ggplot) S3method(upside_gap_2_crows,matrix) S3method(upside_gap_2_crows,plotly) +S3method(variable_moving_average_period,data.frame) +S3method(variable_moving_average_period,default) +S3method(variable_moving_average_period,ggplot) +S3method(variable_moving_average_period,matrix) +S3method(variable_moving_average_period,plotly) S3method(weighted_close_price,data.frame) S3method(weighted_close_price,default) S3method(weighted_close_price,matrix) @@ -664,6 +666,7 @@ export(APO) export(AROON) export(AROONOSC) export(ATR) +export(AVGDEV) export(AVGPRICE) export(BBANDS) export(BETA) @@ -747,11 +750,11 @@ export(MACD) export(MACDEXT) export(MACDFIX) export(MAMA) -export(MAX) +export(MAVP) export(MEDPRICE) export(MFI) +export(MIDPOINT) export(MIDPRICE) -export(MIN) export(MINUS_DI) export(MINUS_DM) export(MOM) @@ -760,8 +763,6 @@ export(OBV) export(PLUS_DI) export(PLUS_DM) export(PPO) -export(ROC) -export(ROCR) export(RSI) export(SAR) export(SAREXT) @@ -770,7 +771,6 @@ export(STDDEV) export(STOCH) export(STOCHF) export(STOCHRSI) -export(SUM) export(T3) export(TEMA) export(TRANGE) @@ -789,6 +789,7 @@ export(acceleration_bands) export(advance_block) export(aroon) export(aroon_oscillator) +export(average_deviation) export(average_directional_movement_index) export(average_directional_movement_index_rating) export(average_price) @@ -847,6 +848,7 @@ export(mat_hold) export(matching_low) export(median_price) export(mesa_adaptive_moving_average) +export(midpoint_period) export(midpoint_price) export(minus_directional_indicator) export(minus_directional_movement) @@ -864,17 +866,12 @@ export(phasor_components) export(piercing) export(plus_directional_indicator) export(plus_directional_movement) -export(rate_of_change) -export(ratio_of_change) export(relative_strength_index) export(rickshaw_man) export(rise_fall_3_methods) export(rolling_beta) export(rolling_correlation) -export(rolling_max) -export(rolling_min) export(rolling_standard_deviation) -export(rolling_sum) export(rolling_variance) export(separating_lines) export(set_theme) @@ -911,6 +908,7 @@ export(typical_price) export(ultimate_oscillator) export(unique_3_river) export(upside_gap_2_crows) +export(variable_moving_average_period) export(weighted_close_price) export(weighted_moving_average) export(williams_oscillator) diff --git a/R/ta_ACCBANDS.R b/R/ta_ACCBANDS.R index 2e344ecb6..1d92376a9 100644 --- a/R/ta_ACCBANDS.R +++ b/R/ta_ACCBANDS.R @@ -12,6 +12,7 @@ ## splice:documentation:end #' #' @template description +#' #' @template returns acceleration_bands <- function( x, diff --git a/R/ta_AD.R b/R/ta_AD.R index c0d439301..b752fcb19 100644 --- a/R/ta_AD.R +++ b/R/ta_AD.R @@ -12,6 +12,7 @@ ## splice:documentation:end #' #' @template description +#' #' @template returns chaikin_accumulation_distribution_line <- function( x, diff --git a/R/ta_ADOSC.R b/R/ta_ADOSC.R index 6f8861cb6..ce4a07aaf 100644 --- a/R/ta_ADOSC.R +++ b/R/ta_ADOSC.R @@ -12,6 +12,8 @@ ## splice:documentation:end #' #' @template description +#' @param fastPeriod ([integer]). Number of period for the fast MA. Defaults to `3`. +#' @param slowPeriod ([integer]). Number of period for the slow MA. Defaults to `10`. #' @template returns chaikin_accumulation_distribution_oscillator <- function( x, diff --git a/R/ta_ADX.R b/R/ta_ADX.R index 04b2ddc47..a884ecb81 100644 --- a/R/ta_ADX.R +++ b/R/ta_ADX.R @@ -12,6 +12,7 @@ ## splice:documentation:end #' #' @template description +#' #' @template returns average_directional_movement_index <- function( x, diff --git a/R/ta_ADXR.R b/R/ta_ADXR.R index 484d2f24f..47453c323 100644 --- a/R/ta_ADXR.R +++ b/R/ta_ADXR.R @@ -12,6 +12,7 @@ ## splice:documentation:end #' #' @template description +#' #' @template returns average_directional_movement_index_rating <- function( x, diff --git a/R/ta_APO.R b/R/ta_APO.R index 3e4255ff4..bc252c042 100644 --- a/R/ta_APO.R +++ b/R/ta_APO.R @@ -12,6 +12,9 @@ ## splice:documentation:end #' #' @template description +#' @param fastPeriod ([integer]). Number of period for the fast MA. Defaults to `12`. +#' @param slowPeriod ([integer]). Number of period for the slow MA. Defaults to `26`. +#' @param maType ([integer]). Type of Moving Average. Defaults to `0` ([SMA]). Can also be passed as talib::SMA. #' @template returns absolute_price_oscillator <- function( x, @@ -71,7 +74,7 @@ absolute_price_oscillator.default <- function( constructed_series[[1]], as.integer(fastPeriod), as.integer(slowPeriod), - as.integer(maType), + as.maType(maType), as.logical(na.bridge) ) @@ -146,7 +149,7 @@ absolute_price_oscillator_lookback <- function( C_impl_ta_APO_lookback, as.integer(fastPeriod), as.integer(slowPeriod), - as.integer(maType) + as.maType(maType) ) } #' @usage NULL @@ -177,7 +180,7 @@ absolute_price_oscillator.numeric <- function( as.double(x), as.integer(fastPeriod), as.integer(slowPeriod), - as.integer(maType), + as.maType(maType), as.logical(na.bridge) ) diff --git a/R/ta_AROON.R b/R/ta_AROON.R index 2ee51324b..e48f8face 100644 --- a/R/ta_AROON.R +++ b/R/ta_AROON.R @@ -12,6 +12,7 @@ ## splice:documentation:end #' #' @template description +#' #' @template returns aroon <- function( x, diff --git a/R/ta_AROONOSC.R b/R/ta_AROONOSC.R index 644e4b980..7a601c1f2 100644 --- a/R/ta_AROONOSC.R +++ b/R/ta_AROONOSC.R @@ -12,6 +12,7 @@ ## splice:documentation:end #' #' @template description +#' #' @template returns aroon_oscillator <- function( x, diff --git a/R/ta_ATR.R b/R/ta_ATR.R index 498f2d633..c048deda1 100644 --- a/R/ta_ATR.R +++ b/R/ta_ATR.R @@ -12,6 +12,7 @@ ## splice:documentation:end #' #' @template description +#' #' @template returns average_true_range <- function( x, diff --git a/R/ta_AVGDEV.R b/R/ta_AVGDEV.R index c8b34dbc8..89bd26974 100644 --- a/R/ta_AVGDEV.R +++ b/R/ta_AVGDEV.R @@ -4,7 +4,7 @@ #' @title Average Deviation #' @templateVar .title Average Deviation #' @templateVar .author Serkan Korkmaz -#' @templateVar .fun AVGDEV +#' @templateVar .fun average_deviation #' @templateVar .family Price Transform #' @templateVar .formula ~close #' @@ -12,29 +12,30 @@ ## splice:documentation:end #' #' @template description +#' #' @template returns -AVGDEV <- function( +average_deviation <- function( x, cols, timePeriod = 14, na.bridge = FALSE, ... ) { - UseMethod("AVGDEV") + UseMethod("average_deviation") } #' @export #' @usage NULL -#' @rdname AVGDEV +#' @rdname average_deviation #' -#' @aliases AVGDEV -AVGDEV <- AVGDEV +#' @aliases average_deviation +AVGDEV <- average_deviation #' @usage NULL -#' @aliases AVGDEV +#' @aliases average_deviation #' #' @export -AVGDEV.default <- function( +average_deviation.default <- function( x, cols, timePeriod = 14, @@ -77,10 +78,10 @@ AVGDEV.default <- function( } #' @usage NULL -#' @aliases AVGDEV +#' @aliases average_deviation #' #' @export -AVGDEV.data.frame <- function( +average_deviation.data.frame <- function( x, cols, timePeriod = 14, @@ -88,7 +89,7 @@ AVGDEV.data.frame <- function( ... ) { map_dfr( - AVGDEV.default( + average_deviation.default( x = x, cols = cols, timePeriod = timePeriod, @@ -99,17 +100,17 @@ AVGDEV.data.frame <- function( } #' @usage NULL -#' @aliases AVGDEV +#' @aliases average_deviation #' #' @export -AVGDEV.matrix <- function( +average_deviation.matrix <- function( x, cols, timePeriod = 14, na.bridge = FALSE, ... ) { - AVGDEV.default( + average_deviation.default( x = x, cols = cols, timePeriod = timePeriod, @@ -119,7 +120,7 @@ AVGDEV.matrix <- function( } #' @usage NULL -AVGDEV_lookback <- function( +average_deviation_lookback <- function( x, cols, timePeriod = 14, @@ -132,10 +133,10 @@ AVGDEV_lookback <- function( ) } #' @usage NULL -#' @aliases AVGDEV +#' @aliases average_deviation #' #' @export -AVGDEV.numeric <- function( +average_deviation.numeric <- function( x, cols, timePeriod = 14, diff --git a/R/ta_AVGPRICE.R b/R/ta_AVGPRICE.R index 9bce754c9..989f300af 100644 --- a/R/ta_AVGPRICE.R +++ b/R/ta_AVGPRICE.R @@ -12,6 +12,7 @@ ## splice:documentation:end #' #' @template description +#' #' @template returns average_price <- function( x, diff --git a/R/ta_BBANDS.R b/R/ta_BBANDS.R index 87b370543..7111e0c0c 100644 --- a/R/ta_BBANDS.R +++ b/R/ta_BBANDS.R @@ -12,6 +12,9 @@ ## splice:documentation:end #' #' @template description +#' @param deviationsUp ([double]). Deviation multiplier for upper band. Defaults to `2`. +#' @param deviationsDown ([double]). Deviation multiplier for lower band. Defaults to `2`. +#' @param maType ([integer]). Type of Moving Average. Defaults to `0` ([SMA]). Can also be passed as talib::SMA. #' @template returns bollinger_bands <- function( x, @@ -74,7 +77,7 @@ bollinger_bands.default <- function( as.integer(timePeriod), as.double(deviationsUp), as.double(deviationsDown), - as.integer(maType), + as.maType(maType), as.logical(na.bridge) ) @@ -155,7 +158,7 @@ bollinger_bands_lookback <- function( as.integer(timePeriod), as.double(deviationsUp), as.double(deviationsDown), - as.integer(maType) + as.maType(maType) ) } #' @usage NULL @@ -188,7 +191,7 @@ bollinger_bands.numeric <- function( as.integer(timePeriod), as.double(deviationsUp), as.double(deviationsDown), - as.integer(maType), + as.maType(maType), as.logical(na.bridge) ) diff --git a/R/ta_BETA.R b/R/ta_BETA.R index edf439921..ac1c0dd00 100644 --- a/R/ta_BETA.R +++ b/R/ta_BETA.R @@ -10,9 +10,11 @@ ## splice:documentation:end #' #' @template rolling_description +#' #' @template rolling_returns rolling_beta <- function( x, + y, timePeriod = 5, na.bridge = FALSE ) { @@ -32,6 +34,7 @@ BETA <- rolling_beta #' @export rolling_beta.default <- function( x, + y, timePeriod = 5, na.bridge = FALSE ) { @@ -41,6 +44,7 @@ rolling_beta.default <- function( C_impl_ta_BETA, ## splice:call:start as.double(x), + as.double(y), as.integer(timePeriod), ## splice:call:end as.logical(na.bridge) @@ -61,6 +65,7 @@ rolling_beta.default <- function( #' @export rolling_beta.numeric <- function( x, + y, timePeriod = 5, na.bridge = FALSE ) { @@ -68,6 +73,7 @@ rolling_beta.numeric <- function( ## return as data.frame x <- rolling_beta.default( x = x, + y = y, timePeriod = timePeriod, na.bridge = na.bridge ) diff --git a/R/ta_BOP.R b/R/ta_BOP.R index 4dcc833d1..fdab8f600 100644 --- a/R/ta_BOP.R +++ b/R/ta_BOP.R @@ -12,6 +12,7 @@ ## splice:documentation:end #' #' @template description +#' #' @template returns balance_of_power <- function( x, diff --git a/R/ta_CCI.R b/R/ta_CCI.R index 43344dadc..94b2c7d8a 100644 --- a/R/ta_CCI.R +++ b/R/ta_CCI.R @@ -12,6 +12,7 @@ ## splice:documentation:end #' #' @template description +#' #' @template returns commodity_channel_index <- function( x, diff --git a/R/ta_CMO.R b/R/ta_CMO.R index 21d706834..429d18117 100644 --- a/R/ta_CMO.R +++ b/R/ta_CMO.R @@ -12,6 +12,7 @@ ## splice:documentation:end #' #' @template description +#' #' @template returns chande_momentum_oscillator <- function( x, diff --git a/R/ta_CORREL.R b/R/ta_CORREL.R index 3af561a26..1fa0d5839 100644 --- a/R/ta_CORREL.R +++ b/R/ta_CORREL.R @@ -10,9 +10,11 @@ ## splice:documentation:end #' #' @template rolling_description +#' #' @template rolling_returns rolling_correlation <- function( x, + y, timePeriod = 30, na.bridge = FALSE ) { @@ -32,6 +34,7 @@ CORREL <- rolling_correlation #' @export rolling_correlation.default <- function( x, + y, timePeriod = 30, na.bridge = FALSE ) { @@ -41,6 +44,7 @@ rolling_correlation.default <- function( C_impl_ta_CORREL, ## splice:call:start as.double(x), + as.double(y), as.integer(timePeriod), ## splice:call:end as.logical(na.bridge) @@ -61,6 +65,7 @@ rolling_correlation.default <- function( #' @export rolling_correlation.numeric <- function( x, + y, timePeriod = 30, na.bridge = FALSE ) { @@ -68,6 +73,7 @@ rolling_correlation.numeric <- function( ## return as data.frame x <- rolling_correlation.default( x = x, + y = y, timePeriod = timePeriod, na.bridge = na.bridge ) diff --git a/R/ta_DEMA.R b/R/ta_DEMA.R index 9e633b227..099697d1c 100644 --- a/R/ta_DEMA.R +++ b/R/ta_DEMA.R @@ -18,6 +18,7 @@ #' indicators that supports various Moving Average specifications. #' #' @template description +#' #' @template returns double_exponential_moving_average <- function( x, @@ -39,7 +40,8 @@ double_exponential_moving_average <- function( as.integer(timePeriod) }, maType = 3L - ) + ), + class = "maType" ) return(x) diff --git a/R/ta_DX.R b/R/ta_DX.R index 663b50bb1..2907cf97b 100644 --- a/R/ta_DX.R +++ b/R/ta_DX.R @@ -12,6 +12,7 @@ ## splice:documentation:end #' #' @template description +#' #' @template returns directional_movement_index <- function( x, diff --git a/R/ta_EMA.R b/R/ta_EMA.R index adffb6d18..3d636e3bb 100644 --- a/R/ta_EMA.R +++ b/R/ta_EMA.R @@ -18,6 +18,7 @@ #' indicators that supports various Moving Average specifications. #' #' @template description +#' #' @template returns exponential_moving_average <- function( x, @@ -39,7 +40,8 @@ exponential_moving_average <- function( as.integer(timePeriod) }, maType = 1L - ) + ), + class = "maType" ) return(x) diff --git a/R/ta_HT_DCPERIOD.R b/R/ta_HT_DCPERIOD.R index 13c9796e7..f487f87b0 100644 --- a/R/ta_HT_DCPERIOD.R +++ b/R/ta_HT_DCPERIOD.R @@ -12,6 +12,7 @@ ## splice:documentation:end #' #' @template description +#' #' @template returns dominant_cycle_period <- function( x, diff --git a/R/ta_HT_DCPHASE.R b/R/ta_HT_DCPHASE.R index 1428acdb1..42b4ba1d0 100644 --- a/R/ta_HT_DCPHASE.R +++ b/R/ta_HT_DCPHASE.R @@ -12,6 +12,7 @@ ## splice:documentation:end #' #' @template description +#' #' @template returns dominant_cycle_phase <- function( x, diff --git a/R/ta_HT_PHASOR.R b/R/ta_HT_PHASOR.R index 877e94761..9d8659275 100644 --- a/R/ta_HT_PHASOR.R +++ b/R/ta_HT_PHASOR.R @@ -12,6 +12,7 @@ ## splice:documentation:end #' #' @template description +#' #' @template returns phasor_components <- function( x, diff --git a/R/ta_HT_SINE.R b/R/ta_HT_SINE.R index 4eac57d90..7cf71f1f9 100644 --- a/R/ta_HT_SINE.R +++ b/R/ta_HT_SINE.R @@ -12,6 +12,7 @@ ## splice:documentation:end #' #' @template description +#' #' @template returns sine_wave <- function( x, diff --git a/R/ta_HT_TRENDLINE.R b/R/ta_HT_TRENDLINE.R index 43f7e06b4..df807f6e9 100644 --- a/R/ta_HT_TRENDLINE.R +++ b/R/ta_HT_TRENDLINE.R @@ -12,6 +12,7 @@ ## splice:documentation:end #' #' @template description +#' #' @template returns trendline <- function( x, diff --git a/R/ta_HT_TRENDMODE.R b/R/ta_HT_TRENDMODE.R index 70942acde..976364718 100644 --- a/R/ta_HT_TRENDMODE.R +++ b/R/ta_HT_TRENDMODE.R @@ -12,6 +12,7 @@ ## splice:documentation:end #' #' @template description +#' #' @template returns trend_cycle_mode <- function( x, diff --git a/R/ta_IMI.R b/R/ta_IMI.R index 2d800921d..d6b2d2eee 100644 --- a/R/ta_IMI.R +++ b/R/ta_IMI.R @@ -12,6 +12,7 @@ ## splice:documentation:end #' #' @template description +#' #' @template returns intraday_movement_index <- function( x, diff --git a/R/ta_KAMA.R b/R/ta_KAMA.R index 8d244a2cd..fbbcff867 100644 --- a/R/ta_KAMA.R +++ b/R/ta_KAMA.R @@ -18,6 +18,7 @@ #' indicators that supports various Moving Average specifications. #' #' @template description +#' #' @template returns kaufman_adaptive_moving_average <- function( x, @@ -39,7 +40,8 @@ kaufman_adaptive_moving_average <- function( as.integer(timePeriod) }, maType = 6L - ) + ), + class = "maType" ) return(x) diff --git a/R/ta_MACD.R b/R/ta_MACD.R index 0655ba9a2..87870a7cf 100644 --- a/R/ta_MACD.R +++ b/R/ta_MACD.R @@ -12,6 +12,9 @@ ## splice:documentation:end #' #' @template description +#' @param fastPeriod ([integer]). Number of period for the fast MA. Defaults to `12`. +#' @param slowPeriod ([integer]). Number of period for the slow MA. Defaults to `26`. +#' @param signalPeriod ([integer]). Smoothing for the signal line (nb of period). Defaults to `9`. #' @template returns moving_average_convergence_divergence <- function( x, diff --git a/R/ta_MACDEXT.R b/R/ta_MACDEXT.R index f022e3695..4f9dce300 100644 --- a/R/ta_MACDEXT.R +++ b/R/ta_MACDEXT.R @@ -12,6 +12,12 @@ ## splice:documentation:end #' #' @template description +#' @param fastPeriod ([integer]). Number of period for the fast MA. Defaults to `12`. +#' @param fastMa ([integer]). Type of Moving Average for fast MA. Defaults to `0` ([SMA]). Can also be passed as talib::SMA. +#' @param slowPeriod ([integer]). Number of period for the slow MA. Defaults to `26`. +#' @param slowMa ([integer]). Type of Moving Average for slow MA. Defaults to `0` ([SMA]). Can also be passed as talib::SMA. +#' @param signalPeriod ([integer]). Smoothing for the signal line (nb of period). Defaults to `9`. +#' @param signalMa ([integer]). Type of Moving Average for signal line. Defaults to `0` ([SMA]). Can also be passed as talib::SMA. #' @template returns extended_moving_average_convergence_divergence <- function( x, @@ -76,11 +82,11 @@ extended_moving_average_convergence_divergence.default <- function( C_impl_ta_MACDEXT, constructed_series[[1]], as.integer(fastPeriod), - as.integer(fastMa), + as.maType(fastMa), as.integer(slowPeriod), - as.integer(slowMa), + as.maType(slowMa), as.integer(signalPeriod), - as.integer(signalMa), + as.maType(signalMa), as.logical(na.bridge) ) @@ -169,11 +175,11 @@ extended_moving_average_convergence_divergence_lookback <- function( .Call( C_impl_ta_MACDEXT_lookback, as.integer(fastPeriod), - as.integer(fastMa), + as.maType(fastMa), as.integer(slowPeriod), - as.integer(slowMa), + as.maType(slowMa), as.integer(signalPeriod), - as.integer(signalMa) + as.maType(signalMa) ) } #' @usage NULL @@ -206,11 +212,11 @@ extended_moving_average_convergence_divergence.numeric <- function( C_impl_ta_MACDEXT, as.double(x), as.integer(fastPeriod), - as.integer(fastMa), + as.maType(fastMa), as.integer(slowPeriod), - as.integer(slowMa), + as.maType(slowMa), as.integer(signalPeriod), - as.integer(signalMa), + as.maType(signalMa), as.logical(na.bridge) ) diff --git a/R/ta_MACDFIX.R b/R/ta_MACDFIX.R index 43b4f9286..afc5f6006 100644 --- a/R/ta_MACDFIX.R +++ b/R/ta_MACDFIX.R @@ -12,6 +12,7 @@ ## splice:documentation:end #' #' @template description +#' @param signalPeriod ([integer]). Smoothing for the signal line (nb of period). Defaults to `9`. #' @template returns fixed_moving_average_convergence_divergence <- function( x, diff --git a/R/ta_MAMA.R b/R/ta_MAMA.R index 4a3e58f92..bac49954b 100644 --- a/R/ta_MAMA.R +++ b/R/ta_MAMA.R @@ -18,6 +18,8 @@ #' indicators that supports various Moving Average specifications. #' #' @template description +#' @param fastLimit ([double]). Upper limit use in the adaptive algorithm. Defaults to `0.5`. +#' @param slowLimit ([double]). Lower limit use in the adaptive algorithm. Defaults to `0.05`. #' @template returns mesa_adaptive_moving_average <- function( x, @@ -51,7 +53,8 @@ mesa_adaptive_moving_average <- function( as.double(slowLimit) }, maType = 7L - ) + ), + class = "maType" ) return(x) diff --git a/R/ta_MAVP.R b/R/ta_MAVP.R index 5d522133c..bb9dcc131 100644 --- a/R/ta_MAVP.R +++ b/R/ta_MAVP.R @@ -4,16 +4,21 @@ #' @title Moving average with variable period #' @templateVar .title Moving average with variable period #' @templateVar .author Serkan Korkmaz -#' @templateVar .fun MAVP +#' @templateVar .fun variable_moving_average_period #' @templateVar .family Overlap Studies -#' @templateVar .formula ~close +#' @templateVar .formula ~close + periods #' ## splice:documentation:start +#' @templateVar .custom_example TRUE +#' @example man/examples/MAVP-example.R ## splice:documentation:end #' #' @template description +#' @param minimumPeriod ([integer]). Value less than minimum will be changed to Minimum period. Defaults to `2`. +#' @param maximumPeriod ([integer]). Value higher than maximum will be changed to Maximum period. Defaults to `30`. +#' @param maType ([integer]). Type of Moving Average. Defaults to `0` ([SMA]). Can also be passed as talib::SMA. #' @template returns -MAVP <- function( +variable_moving_average_period <- function( x, cols, minimumPeriod = 2, @@ -22,21 +27,21 @@ MAVP <- function( na.bridge = FALSE, ... ) { - UseMethod("MAVP") + UseMethod("variable_moving_average_period") } #' @export #' @usage NULL -#' @rdname MAVP +#' @rdname variable_moving_average_period #' -#' @aliases MAVP -MAVP <- MAVP +#' @aliases variable_moving_average_period +MAVP <- variable_moving_average_period #' @usage NULL -#' @aliases MAVP +#' @aliases variable_moving_average_period #' #' @export -MAVP.default <- function( +variable_moving_average_period.default <- function( x, cols, minimumPeriod = 2, @@ -55,7 +60,7 @@ MAVP.default <- function( ## from input constructed_series <- series( x = cols, - default_formula = ~close, + default_formula = ~ close + periods, data = x, ... ) @@ -72,7 +77,7 @@ MAVP.default <- function( constructed_series[[2]], as.integer(minimumPeriod), as.integer(maximumPeriod), - as.integer(maType), + as.maType(maType), as.logical(na.bridge) ) @@ -84,10 +89,10 @@ MAVP.default <- function( } #' @usage NULL -#' @aliases MAVP +#' @aliases variable_moving_average_period #' #' @export -MAVP.data.frame <- function( +variable_moving_average_period.data.frame <- function( x, cols, minimumPeriod = 2, @@ -97,7 +102,7 @@ MAVP.data.frame <- function( ... ) { map_dfr( - MAVP.default( + variable_moving_average_period.default( x = x, cols = cols, minimumPeriod = minimumPeriod, @@ -110,10 +115,10 @@ MAVP.data.frame <- function( } #' @usage NULL -#' @aliases MAVP +#' @aliases variable_moving_average_period #' #' @export -MAVP.matrix <- function( +variable_moving_average_period.matrix <- function( x, cols, minimumPeriod = 2, @@ -122,7 +127,7 @@ MAVP.matrix <- function( na.bridge = FALSE, ... ) { - MAVP.default( + variable_moving_average_period.default( x = x, cols = cols, minimumPeriod = minimumPeriod, @@ -134,7 +139,7 @@ MAVP.matrix <- function( } #' @usage NULL -MAVP_lookback <- function( +variable_moving_average_period_lookback <- function( x, cols, minimumPeriod = 2, @@ -147,6 +152,170 @@ MAVP_lookback <- function( C_impl_ta_MAVP_lookback, as.integer(minimumPeriod), as.integer(maximumPeriod), - as.integer(maType) + as.maType(maType) ) } +#' @usage NULL +#' @aliases variable_moving_average_period +#' +#' @export +variable_moving_average_period.plotly <- function( + x, + cols, + minimumPeriod = 2, + maximumPeriod = 30, + maType = 0, + na.bridge = FALSE, + ## splice:optional-plotly:start + ## splice:optional-plotly:end + ... +) { + ## check that input value + ## 'x' is -object + assert_plotly_object(x) + + ## check that input value + ## 'cols' is a -objet + if (!missing(cols)) { + assert_formula(cols) + } + + ## construct series from + ## {plotly}-object + constructed_series <- series( + x = x, + formula = cols, + default_formula = ~ close + periods, + ... + ) + + ## construct indicator + ## from the series + constructed_indicator <- variable_moving_average_period( + x = constructed_series, + cols = rebuild_formula( + names(constructed_series) + ), + minimumPeriod = minimumPeriod, + maximumPeriod = maximumPeriod, + maType = maType, + na.bridge = TRUE + ) + + ## add conditional idx + constructed_indicator[["idx"]] <- add_idx( + constructed_series + ) + + ## construct {plotly}-object + ## splice:plotly-assembly:start + traces <- lapply( + setdiff(colnames(constructed_indicator), "idx"), + function(col) { + list( + y = stats::as.formula( + paste0("~", col) + ), + name = col + ) + } + ) + name <- sprintf( + "MAVP(%d, %d, %s)", + min(minimumPeriod, maximumPeriod), + max(minimumPeriod, maximumPeriod), + mapMaType(maType) + ) + ## splice:plotly-assembly:end + + state <- .chart_state() + plotly_object <- build_plotly( + init = state[["main"]], + traces = traces, + decorators = list(), + name = get0( + x = "name", + ifnotfound = NULL + ), + data = constructed_indicator + ) + state[["main"]] <- plotly_object + + plotly_object +} + +#' @usage NULL +#' @aliases variable_moving_average_period +#' +#' @export +variable_moving_average_period.ggplot <- function( + x, + cols, + minimumPeriod = 2, + maximumPeriod = 30, + maType = 0, + na.bridge = FALSE, + ## splice:optional-ggplot:start + ## splice:optional-ggplot:end + ... +) { + ## check ggplot2 availability + assert_ggplot2() + + ## check that input value + ## 'cols' is a -objet + if (!missing(cols)) { + assert_formula(cols) + } + + ## construct series from + ## {ggplot}-object + constructed_series <- series( + x = x, + formula = cols, + default_formula = ~ close + periods, + ... + ) + + ## construct indicator + ## from the series + constructed_indicator <- variable_moving_average_period( + x = constructed_series, + cols = rebuild_formula( + names(constructed_series) + ), + minimumPeriod = minimumPeriod, + maximumPeriod = maximumPeriod, + maType = maType, + na.bridge = TRUE + ) + + ## add conditional idx + constructed_indicator[["idx"]] <- add_idx( + constructed_series + ) + + ## construct {ggplot2}-object + ## splice:ggplot-assembly:start + layers <- lapply( + setdiff(colnames(constructed_indicator), "idx"), + function(col) list(y = col) + ) + name <- "MAVP" + ## splice:ggplot-assembly:end + + state <- .chart_state() + ggplot_object <- build_ggplot( + init = state[["main"]], + layers = layers, + decorators = list(), + name = get0( + x = "name", + ifnotfound = NULL + ), + data = constructed_indicator + ) + state[["main"]] <- ggplot_object + + ggplot_object +} diff --git a/R/ta_MEDPRICE.R b/R/ta_MEDPRICE.R index 501e1c307..d9d2bfe14 100644 --- a/R/ta_MEDPRICE.R +++ b/R/ta_MEDPRICE.R @@ -12,6 +12,7 @@ ## splice:documentation:end #' #' @template description +#' #' @template returns median_price <- function( x, diff --git a/R/ta_MFI.R b/R/ta_MFI.R index 434cdea89..40d9bf132 100644 --- a/R/ta_MFI.R +++ b/R/ta_MFI.R @@ -12,6 +12,7 @@ ## splice:documentation:end #' #' @template description +#' #' @template returns money_flow_index <- function( x, diff --git a/R/ta_MIDPOINT.R b/R/ta_MIDPOINT.R index a4c036038..3c4b02314 100644 --- a/R/ta_MIDPOINT.R +++ b/R/ta_MIDPOINT.R @@ -4,7 +4,7 @@ #' @title MidPoint over period #' @templateVar .title MidPoint over period #' @templateVar .author Serkan Korkmaz -#' @templateVar .fun MIDPOINT +#' @templateVar .fun midpoint_period #' @templateVar .family Overlap Studies #' @templateVar .formula ~close #' @@ -12,29 +12,30 @@ ## splice:documentation:end #' #' @template description +#' #' @template returns -MIDPOINT <- function( +midpoint_period <- function( x, cols, timePeriod = 14, na.bridge = FALSE, ... ) { - UseMethod("MIDPOINT") + UseMethod("midpoint_period") } #' @export #' @usage NULL -#' @rdname MIDPOINT +#' @rdname midpoint_period #' -#' @aliases MIDPOINT -MIDPOINT <- MIDPOINT +#' @aliases midpoint_period +MIDPOINT <- midpoint_period #' @usage NULL -#' @aliases MIDPOINT +#' @aliases midpoint_period #' #' @export -MIDPOINT.default <- function( +midpoint_period.default <- function( x, cols, timePeriod = 14, @@ -77,10 +78,10 @@ MIDPOINT.default <- function( } #' @usage NULL -#' @aliases MIDPOINT +#' @aliases midpoint_period #' #' @export -MIDPOINT.data.frame <- function( +midpoint_period.data.frame <- function( x, cols, timePeriod = 14, @@ -88,7 +89,7 @@ MIDPOINT.data.frame <- function( ... ) { map_dfr( - MIDPOINT.default( + midpoint_period.default( x = x, cols = cols, timePeriod = timePeriod, @@ -99,17 +100,17 @@ MIDPOINT.data.frame <- function( } #' @usage NULL -#' @aliases MIDPOINT +#' @aliases midpoint_period #' #' @export -MIDPOINT.matrix <- function( +midpoint_period.matrix <- function( x, cols, timePeriod = 14, na.bridge = FALSE, ... ) { - MIDPOINT.default( + midpoint_period.default( x = x, cols = cols, timePeriod = timePeriod, @@ -119,7 +120,7 @@ MIDPOINT.matrix <- function( } #' @usage NULL -MIDPOINT_lookback <- function( +midpoint_period_lookback <- function( x, cols, timePeriod = 14, @@ -132,10 +133,10 @@ MIDPOINT_lookback <- function( ) } #' @usage NULL -#' @aliases MIDPOINT +#' @aliases midpoint_period #' #' @export -MIDPOINT.numeric <- function( +midpoint_period.numeric <- function( x, cols, timePeriod = 14, @@ -165,3 +166,155 @@ MIDPOINT.numeric <- function( x } + +#' @usage NULL +#' @aliases midpoint_period +#' +#' @export +midpoint_period.plotly <- function( + x, + cols, + timePeriod = 14, + na.bridge = FALSE, + ## splice:optional-plotly:start + ## splice:optional-plotly:end + ... +) { + ## check that input value + ## 'x' is -object + assert_plotly_object(x) + + ## check that input value + ## 'cols' is a -objet + if (!missing(cols)) { + assert_formula(cols) + } + + ## construct series from + ## {plotly}-object + constructed_series <- series( + x = x, + formula = cols, + default_formula = ~close, + ... + ) + + ## construct indicator + ## from the series + constructed_indicator <- midpoint_period( + x = constructed_series, + cols = rebuild_formula( + names(constructed_series) + ), + timePeriod = timePeriod, + na.bridge = TRUE + ) + + ## add conditional idx + constructed_indicator[["idx"]] <- add_idx( + constructed_series + ) + + ## construct {plotly}-object + ## splice:plotly-assembly:start + traces <- lapply( + setdiff(colnames(constructed_indicator), "idx"), + function(col) { + list( + y = stats::as.formula( + paste0("~", col) + ), + name = col + ) + } + ) + name <- "MIDPOINT" + ## splice:plotly-assembly:end + + state <- .chart_state() + plotly_object <- build_plotly( + init = state[["main"]], + traces = traces, + decorators = list(), + name = get0( + x = "name", + ifnotfound = NULL + ), + data = constructed_indicator + ) + state[["main"]] <- plotly_object + + plotly_object +} + +#' @usage NULL +#' @aliases midpoint_period +#' +#' @export +midpoint_period.ggplot <- function( + x, + cols, + timePeriod = 14, + na.bridge = FALSE, + ## splice:optional-ggplot:start + ## splice:optional-ggplot:end + ... +) { + ## check ggplot2 availability + assert_ggplot2() + + ## check that input value + ## 'cols' is a -objet + if (!missing(cols)) { + assert_formula(cols) + } + + ## construct series from + ## {ggplot}-object + constructed_series <- series( + x = x, + formula = cols, + default_formula = ~close, + ... + ) + + ## construct indicator + ## from the series + constructed_indicator <- midpoint_period( + x = constructed_series, + cols = rebuild_formula( + names(constructed_series) + ), + timePeriod = timePeriod, + na.bridge = TRUE + ) + + ## add conditional idx + constructed_indicator[["idx"]] <- add_idx( + constructed_series + ) + + ## construct {ggplot2}-object + ## splice:ggplot-assembly:start + layers <- lapply( + setdiff(colnames(constructed_indicator), "idx"), + function(col) list(y = col) + ) + name <- "MIDPOINT" + ## splice:ggplot-assembly:end + + state <- .chart_state() + ggplot_object <- build_ggplot( + init = state[["main"]], + layers = layers, + decorators = list(), + name = get0( + x = "name", + ifnotfound = NULL + ), + data = constructed_indicator + ) + state[["main"]] <- ggplot_object + + ggplot_object +} diff --git a/R/ta_MIDPRICE.R b/R/ta_MIDPRICE.R index 2a90eb7af..dd433948c 100644 --- a/R/ta_MIDPRICE.R +++ b/R/ta_MIDPRICE.R @@ -12,6 +12,7 @@ ## splice:documentation:end #' #' @template description +#' #' @template returns midpoint_price <- function( x, @@ -132,3 +133,154 @@ midpoint_price_lookback <- function( as.integer(timePeriod) ) } +#' @usage NULL +#' @aliases midpoint_price +#' +#' @export +midpoint_price.plotly <- function( + x, + cols, + timePeriod = 14, + na.bridge = FALSE, + ## splice:optional-plotly:start + ## splice:optional-plotly:end + ... +) { + ## check that input value + ## 'x' is -object + assert_plotly_object(x) + + ## check that input value + ## 'cols' is a -objet + if (!missing(cols)) { + assert_formula(cols) + } + + ## construct series from + ## {plotly}-object + constructed_series <- series( + x = x, + formula = cols, + default_formula = ~ high + low, + ... + ) + + ## construct indicator + ## from the series + constructed_indicator <- midpoint_price( + x = constructed_series, + cols = rebuild_formula( + names(constructed_series) + ), + timePeriod = timePeriod, + na.bridge = TRUE + ) + + ## add conditional idx + constructed_indicator[["idx"]] <- add_idx( + constructed_series + ) + + ## construct {plotly}-object + ## splice:plotly-assembly:start + traces <- lapply( + setdiff(colnames(constructed_indicator), "idx"), + function(col) { + list( + y = stats::as.formula( + paste0("~", col) + ), + name = col + ) + } + ) + name <- "MIDPRICE" + ## splice:plotly-assembly:end + + state <- .chart_state() + plotly_object <- build_plotly( + init = state[["main"]], + traces = traces, + decorators = list(), + name = get0( + x = "name", + ifnotfound = NULL + ), + data = constructed_indicator + ) + state[["main"]] <- plotly_object + + plotly_object +} + +#' @usage NULL +#' @aliases midpoint_price +#' +#' @export +midpoint_price.ggplot <- function( + x, + cols, + timePeriod = 14, + na.bridge = FALSE, + ## splice:optional-ggplot:start + ## splice:optional-ggplot:end + ... +) { + ## check ggplot2 availability + assert_ggplot2() + + ## check that input value + ## 'cols' is a -objet + if (!missing(cols)) { + assert_formula(cols) + } + + ## construct series from + ## {ggplot}-object + constructed_series <- series( + x = x, + formula = cols, + default_formula = ~ high + low, + ... + ) + + ## construct indicator + ## from the series + constructed_indicator <- midpoint_price( + x = constructed_series, + cols = rebuild_formula( + names(constructed_series) + ), + timePeriod = timePeriod, + na.bridge = TRUE + ) + + ## add conditional idx + constructed_indicator[["idx"]] <- add_idx( + constructed_series + ) + + ## construct {ggplot2}-object + ## splice:ggplot-assembly:start + layers <- lapply( + setdiff(colnames(constructed_indicator), "idx"), + function(col) list(y = col) + ) + name <- "MIDPRICE" + ## splice:ggplot-assembly:end + + state <- .chart_state() + ggplot_object <- build_ggplot( + init = state[["main"]], + layers = layers, + decorators = list(), + name = get0( + x = "name", + ifnotfound = NULL + ), + data = constructed_indicator + ) + state[["main"]] <- ggplot_object + + ggplot_object +} diff --git a/R/ta_MINUS_DI.R b/R/ta_MINUS_DI.R index e57a6e8f7..8af967266 100644 --- a/R/ta_MINUS_DI.R +++ b/R/ta_MINUS_DI.R @@ -12,6 +12,7 @@ ## splice:documentation:end #' #' @template description +#' #' @template returns minus_directional_indicator <- function( x, diff --git a/R/ta_MINUS_DM.R b/R/ta_MINUS_DM.R index f0cde79db..2fd6a5997 100644 --- a/R/ta_MINUS_DM.R +++ b/R/ta_MINUS_DM.R @@ -12,6 +12,7 @@ ## splice:documentation:end #' #' @template description +#' #' @template returns minus_directional_movement <- function( x, diff --git a/R/ta_MOM.R b/R/ta_MOM.R index 0534aece0..5ddfeef3e 100644 --- a/R/ta_MOM.R +++ b/R/ta_MOM.R @@ -12,6 +12,7 @@ ## splice:documentation:end #' #' @template description +#' #' @template returns momentum <- function( x, diff --git a/R/ta_NATR.R b/R/ta_NATR.R index 57e8a1420..f594c997e 100644 --- a/R/ta_NATR.R +++ b/R/ta_NATR.R @@ -12,6 +12,7 @@ ## splice:documentation:end #' #' @template description +#' #' @template returns normalized_average_true_range <- function( x, diff --git a/R/ta_OBV.R b/R/ta_OBV.R index b7debfc0a..9f95a0b7c 100644 --- a/R/ta_OBV.R +++ b/R/ta_OBV.R @@ -12,6 +12,7 @@ ## splice:documentation:end #' #' @template description +#' #' @template returns on_balance_volume <- function( x, diff --git a/R/ta_PLUS_DI.R b/R/ta_PLUS_DI.R index 69310bf2c..7a9f0cc0f 100644 --- a/R/ta_PLUS_DI.R +++ b/R/ta_PLUS_DI.R @@ -12,6 +12,7 @@ ## splice:documentation:end #' #' @template description +#' #' @template returns plus_directional_indicator <- function( x, diff --git a/R/ta_PLUS_DM.R b/R/ta_PLUS_DM.R index e11625493..7f432541e 100644 --- a/R/ta_PLUS_DM.R +++ b/R/ta_PLUS_DM.R @@ -12,6 +12,7 @@ ## splice:documentation:end #' #' @template description +#' #' @template returns plus_directional_movement <- function( x, diff --git a/R/ta_PPO.R b/R/ta_PPO.R index 13c9fc52d..d2b90190f 100644 --- a/R/ta_PPO.R +++ b/R/ta_PPO.R @@ -12,6 +12,9 @@ ## splice:documentation:end #' #' @template description +#' @param fastPeriod ([integer]). Number of period for the fast MA. Defaults to `12`. +#' @param slowPeriod ([integer]). Number of period for the slow MA. Defaults to `26`. +#' @param maType ([integer]). Type of Moving Average. Defaults to `0` ([SMA]). Can also be passed as talib::SMA. #' @template returns percentage_price_oscillator <- function( x, @@ -71,7 +74,7 @@ percentage_price_oscillator.default <- function( constructed_series[[1]], as.integer(fastPeriod), as.integer(slowPeriod), - as.integer(maType), + as.maType(maType), as.logical(na.bridge) ) @@ -146,7 +149,7 @@ percentage_price_oscillator_lookback <- function( C_impl_ta_PPO_lookback, as.integer(fastPeriod), as.integer(slowPeriod), - as.integer(maType) + as.maType(maType) ) } #' @usage NULL @@ -177,7 +180,7 @@ percentage_price_oscillator.numeric <- function( as.double(x), as.integer(fastPeriod), as.integer(slowPeriod), - as.integer(maType), + as.maType(maType), as.logical(na.bridge) ) diff --git a/R/ta_RSI.R b/R/ta_RSI.R index 39151f6dd..b32429c4a 100644 --- a/R/ta_RSI.R +++ b/R/ta_RSI.R @@ -12,6 +12,7 @@ ## splice:documentation:end #' #' @template description +#' #' @template returns relative_strength_index <- function( x, diff --git a/R/ta_SAR.R b/R/ta_SAR.R index 465124901..b2a6b4460 100644 --- a/R/ta_SAR.R +++ b/R/ta_SAR.R @@ -12,6 +12,8 @@ ## splice:documentation:end #' #' @template description +#' @param accelerationFactor ([double]). Acceleration Factor used up to the Maximum value. Defaults to `0.02`. +#' @param afMaximum ([double]). Acceleration Factor Maximum value. Defaults to `0.2`. #' @template returns parabolic_stop_and_reverse <- function( x, diff --git a/R/ta_SAREXT.R b/R/ta_SAREXT.R index 6a137ba54..79e4ccf30 100644 --- a/R/ta_SAREXT.R +++ b/R/ta_SAREXT.R @@ -12,6 +12,14 @@ ## splice:documentation:end #' #' @template description +#' @param startValue ([double]). Start value and direction. 0 for Auto, >0 for Long, <0 for Short. Defaults to `0`. +#' @param offsetOnReverse ([double]). Percent offset added/removed to initial stop on short/long reversal. Defaults to `0`. +#' @param afInitLong ([double]). Acceleration Factor initial value for the Long direction. Defaults to `0.02`. +#' @param afLong ([double]). Acceleration Factor for the Long direction. Defaults to `0.02`. +#' @param afMaxLong ([double]). Acceleration Factor maximum value for the Long direction. Defaults to `0.2`. +#' @param afInitShort ([double]). Acceleration Factor initial value for the Short direction. Defaults to `0.02`. +#' @param afShort ([double]). Acceleration Factor for the Short direction. Defaults to `0.02`. +#' @param afMaxShort ([double]). Acceleration Factor maximum value for the Short direction. Defaults to `0.2`. #' @template returns extended_parabolic_stop_and_reverse <- function( x, diff --git a/R/ta_SMA.R b/R/ta_SMA.R index 57ecbcfc1..854894288 100644 --- a/R/ta_SMA.R +++ b/R/ta_SMA.R @@ -18,6 +18,7 @@ #' indicators that supports various Moving Average specifications. #' #' @template description +#' #' @template returns simple_moving_average <- function( x, @@ -39,7 +40,8 @@ simple_moving_average <- function( as.integer(timePeriod) }, maType = 0L - ) + ), + class = "maType" ) return(x) diff --git a/R/ta_STDDEV.R b/R/ta_STDDEV.R index 838912306..d4f2ba609 100644 --- a/R/ta_STDDEV.R +++ b/R/ta_STDDEV.R @@ -10,6 +10,7 @@ ## splice:documentation:end #' #' @template rolling_description +#' @param deviations ([double]). Number of deviations. Defaults to `1`. #' @template rolling_returns rolling_standard_deviation <- function( x, diff --git a/R/ta_STOCH.R b/R/ta_STOCH.R index 0c9bfd3b5..ca23a996f 100644 --- a/R/ta_STOCH.R +++ b/R/ta_STOCH.R @@ -12,6 +12,11 @@ ## splice:documentation:end #' #' @template description +#' @param fastKPeriod ([integer]). Time period for building the Fast-K line. Defaults to `5`. +#' @param slowKPeriod ([integer]). Smoothing for making the Slow-K line. Usually set to 3. Defaults to `3`. +#' @param slowKMa ([integer]). Type of Moving Average for Slow-K. Defaults to `0` ([SMA]). Can also be passed as talib::SMA. +#' @param slowDPeriod ([integer]). Smoothing for making the Slow-D line. Defaults to `3`. +#' @param slowDMa ([integer]). Type of Moving Average for Slow-D. Defaults to `0` ([SMA]). Can also be passed as talib::SMA. #' @template returns stochastic <- function( x, @@ -77,9 +82,9 @@ stochastic.default <- function( constructed_series[[3]], as.integer(fastKPeriod), as.integer(slowKPeriod), - as.integer(slowKMa), + as.maType(slowKMa), as.integer(slowDPeriod), - as.integer(slowDMa), + as.maType(slowDMa), as.logical(na.bridge) ) @@ -164,9 +169,9 @@ stochastic_lookback <- function( C_impl_ta_STOCH_lookback, as.integer(fastKPeriod), as.integer(slowKPeriod), - as.integer(slowKMa), + as.maType(slowKMa), as.integer(slowDPeriod), - as.integer(slowDMa) + as.maType(slowDMa) ) } #' @usage NULL diff --git a/R/ta_STOCHF.R b/R/ta_STOCHF.R index e27d3adac..3bcb4cd89 100644 --- a/R/ta_STOCHF.R +++ b/R/ta_STOCHF.R @@ -12,6 +12,9 @@ ## splice:documentation:end #' #' @template description +#' @param fastKPeriod ([integer]). Time period for building the Fast-K line. Defaults to `5`. +#' @param fastDPeriod ([integer]). Smoothing for making the Fast-D line. Usually set to 3. Defaults to `3`. +#' @param fastDMa ([integer]). Type of Moving Average for Fast-D. Defaults to `0` ([SMA]). Can also be passed as talib::SMA. #' @template returns fast_stochastic <- function( x, @@ -73,7 +76,7 @@ fast_stochastic.default <- function( constructed_series[[3]], as.integer(fastKPeriod), as.integer(fastDPeriod), - as.integer(fastDMa), + as.maType(fastDMa), as.logical(na.bridge) ) @@ -148,7 +151,7 @@ fast_stochastic_lookback <- function( C_impl_ta_STOCHF_lookback, as.integer(fastKPeriod), as.integer(fastDPeriod), - as.integer(fastDMa) + as.maType(fastDMa) ) } #' @usage NULL diff --git a/R/ta_STOCHRSI.R b/R/ta_STOCHRSI.R index d25886bef..f263f3380 100644 --- a/R/ta_STOCHRSI.R +++ b/R/ta_STOCHRSI.R @@ -12,6 +12,9 @@ ## splice:documentation:end #' #' @template description +#' @param fastKPeriod ([integer]). Time period for building the Fast-K line. Defaults to `5`. +#' @param fastDPeriod ([integer]). Smoothing for making the Fast-D line. Usually set to 3. Defaults to `3`. +#' @param fastDMa ([integer]). Type of Moving Average for Fast-D. Defaults to `0` ([SMA]). Can also be passed as talib::SMA. #' @template returns stochastic_relative_strength_index <- function( x, @@ -74,7 +77,7 @@ stochastic_relative_strength_index.default <- function( as.integer(timePeriod), as.integer(fastKPeriod), as.integer(fastDPeriod), - as.integer(fastDMa), + as.maType(fastDMa), as.logical(na.bridge) ) @@ -155,7 +158,7 @@ stochastic_relative_strength_index_lookback <- function( as.integer(timePeriod), as.integer(fastKPeriod), as.integer(fastDPeriod), - as.integer(fastDMa) + as.maType(fastDMa) ) } #' @usage NULL @@ -188,7 +191,7 @@ stochastic_relative_strength_index.numeric <- function( as.integer(timePeriod), as.integer(fastKPeriod), as.integer(fastDPeriod), - as.integer(fastDMa), + as.maType(fastDMa), as.logical(na.bridge) ) diff --git a/R/ta_T3.R b/R/ta_T3.R index 5ba155506..2f1456e85 100644 --- a/R/ta_T3.R +++ b/R/ta_T3.R @@ -18,6 +18,7 @@ #' indicators that supports various Moving Average specifications. #' #' @template description +#' @param volumeFactor ([double]). Volume Factor. Defaults to `0.7`. #' @template returns t3_exponential_moving_average <- function( x, @@ -45,7 +46,8 @@ t3_exponential_moving_average <- function( as.double(volumeFactor) }, maType = 8L - ) + ), + class = "maType" ) return(x) diff --git a/R/ta_TEMA.R b/R/ta_TEMA.R index e5cc430ce..4b8162da6 100644 --- a/R/ta_TEMA.R +++ b/R/ta_TEMA.R @@ -18,6 +18,7 @@ #' indicators that supports various Moving Average specifications. #' #' @template description +#' #' @template returns triple_exponential_moving_average <- function( x, @@ -39,7 +40,8 @@ triple_exponential_moving_average <- function( as.integer(timePeriod) }, maType = 4L - ) + ), + class = "maType" ) return(x) diff --git a/R/ta_TRANGE.R b/R/ta_TRANGE.R index 8d50cde8b..a748605c1 100644 --- a/R/ta_TRANGE.R +++ b/R/ta_TRANGE.R @@ -12,6 +12,7 @@ ## splice:documentation:end #' #' @template description +#' #' @template returns true_range <- function( x, diff --git a/R/ta_TRIMA.R b/R/ta_TRIMA.R index 0d0eca7ce..eda56f033 100644 --- a/R/ta_TRIMA.R +++ b/R/ta_TRIMA.R @@ -18,6 +18,7 @@ #' indicators that supports various Moving Average specifications. #' #' @template description +#' #' @template returns triangular_moving_average <- function( x, @@ -39,7 +40,8 @@ triangular_moving_average <- function( as.integer(timePeriod) }, maType = 5L - ) + ), + class = "maType" ) return(x) diff --git a/R/ta_TRIX.R b/R/ta_TRIX.R index a6c4a8fca..660e1e29f 100644 --- a/R/ta_TRIX.R +++ b/R/ta_TRIX.R @@ -12,6 +12,7 @@ ## splice:documentation:end #' #' @template description +#' #' @template returns triple_exponential_average <- function( x, diff --git a/R/ta_TSF.R b/R/ta_TSF.R deleted file mode 100644 index 186b3e83f..000000000 --- a/R/ta_TSF.R +++ /dev/null @@ -1,82 +0,0 @@ -#' @export -#' @family Statistic Functions -#' -#' @title Time Series Forecast -#' @templateVar .title Time Series Forecast -#' @templateVar .author Serkan Korkmaz -#' @templateVar .fun TSF -#' -## splice:documentation:start -## splice:documentation:end -#' -#' @template rolling_description -#' @template rolling_returns -TSF <- function( - x, - timePeriod = 14, - na.bridge = FALSE -) { - UseMethod("TSF") -} - -#' @export -#' @usage NULL -#' @rdname TSF -#' -#' @aliases TSF -TSF <- TSF - -#' @usage NULL -#' @aliases TSF -#' -#' @export -TSF.default <- function( - x, - timePeriod = 14, - na.bridge = FALSE -) { - ## calculate indicator and - ## return as data.frame - x <- .Call( - C_impl_ta_TSF, - ## splice:call:start - as.double(x), - as.integer(timePeriod), - ## splice:call:end - as.logical(na.bridge) - ) - - ## strip dimensions - ## while preserving - ## attributes - dim(x) <- NULL - - ## return indicator - x -} - -#' @usage NULL -#' @aliases TSF -#' -#' @export -TSF.numeric <- function( - x, - timePeriod = 14, - na.bridge = FALSE -) { - ## calculate indicator and - ## return as data.frame - x <- TSF.default( - x = x, - timePeriod = timePeriod, - na.bridge = na.bridge - ) - - ## strip dimensions - ## while preserving - ## attributes - dim(x) <- NULL - - ## return indicator - x -} diff --git a/R/ta_TYPPRICE.R b/R/ta_TYPPRICE.R index f090a98a8..79180f84f 100644 --- a/R/ta_TYPPRICE.R +++ b/R/ta_TYPPRICE.R @@ -12,6 +12,7 @@ ## splice:documentation:end #' #' @template description +#' #' @template returns typical_price <- function( x, diff --git a/R/ta_ULTOSC.R b/R/ta_ULTOSC.R index 7032ef124..7e2f185ed 100644 --- a/R/ta_ULTOSC.R +++ b/R/ta_ULTOSC.R @@ -12,6 +12,9 @@ ## splice:documentation:end #' #' @template description +#' @param firstPeriod ([integer]). Number of bars for 1st period. Defaults to `7`. +#' @param secondPeriod ([integer]). Number of bars for 2nd period. Defaults to `14`. +#' @param thirdPeriod ([integer]). Number of bars for 3rd period. Defaults to `28`. #' @template returns ultimate_oscillator <- function( x, diff --git a/R/ta_VAR.R b/R/ta_VAR.R index 21ad4d1ab..5415b3941 100644 --- a/R/ta_VAR.R +++ b/R/ta_VAR.R @@ -10,6 +10,7 @@ ## splice:documentation:end #' #' @template rolling_description +#' @param deviations ([double]). Number of deviations. Defaults to `1`. #' @template rolling_returns rolling_variance <- function( x, diff --git a/R/ta_VOLUME.R b/R/ta_VOLUME.R index 2ffa45799..c9c04d255 100644 --- a/R/ta_VOLUME.R +++ b/R/ta_VOLUME.R @@ -9,7 +9,7 @@ #' @templateVar .formula ~volume + open + close #' ## splice:documentation:start -#' @param ma A list of MA specifications. +#' @param maType A [list] of maType specifications on the form SMA(timePeriod = 7). ## splice:documentation:end #' #' @template description @@ -17,7 +17,7 @@ trading_volume <- function( x, cols, - ma = list(SMA(n = 7), SMA(n = 15)), + maType = list(SMA(timePeriod = 7), SMA(timePeriod = 15)), na.bridge = FALSE, ... ) { @@ -38,7 +38,7 @@ VOLUME <- trading_volume trading_volume.default <- function( x, cols, - ma = list(SMA(n = 7), SMA(n = 15)), + maType = list(SMA(timePeriod = 7), SMA(timePeriod = 15)), na.bridge = FALSE, ... ) { @@ -68,10 +68,10 @@ trading_volume.default <- function( ## splice:call:start as.double(constructed_series[[1]]), lapply( - ma, + maType, function(x) { as.integer( - unlist(x, use.names = FALSE) + x ) } ), @@ -93,7 +93,7 @@ trading_volume.default <- function( trading_volume.data.frame <- function( x, cols, - ma = list(SMA(n = 7), SMA(n = 15)), + maType = list(SMA(timePeriod = 7), SMA(timePeriod = 15)), na.bridge = FALSE, ... ) { @@ -101,7 +101,7 @@ trading_volume.data.frame <- function( trading_volume.default( x = x, cols = cols, - ma = ma, + maType = maType, na.bridge = na.bridge, ... ) @@ -115,14 +115,14 @@ trading_volume.data.frame <- function( trading_volume.matrix <- function( x, cols, - ma = list(SMA(n = 7), SMA(n = 15)), + maType = list(SMA(timePeriod = 7), SMA(timePeriod = 15)), na.bridge = FALSE, ... ) { trading_volume.default( x = x, cols = cols, - ma = ma, + maType = maType, na.bridge = na.bridge, ... ) @@ -136,7 +136,7 @@ trading_volume.matrix <- function( trading_volume.numeric <- function( x, cols, - ma = list(SMA(n = 7), SMA(n = 15)), + maType = list(SMA(timePeriod = 7), SMA(timePeriod = 15)), na.bridge = FALSE, ... ) { @@ -154,7 +154,7 @@ trading_volume.numeric <- function( C_impl_ta_VOLUME, ## splice:numeric:start as.double(x), - ma, + maType, ## splice:numeric:end as.logical(na.bridge) ) @@ -174,7 +174,7 @@ trading_volume.numeric <- function( trading_volume.plotly <- function( x, cols, - ma = list(SMA(n = 7), SMA(n = 15)), + maType = list(SMA(timePeriod = 7), SMA(timePeriod = 15)), na.bridge = FALSE, ## splice:optional-plotly:start ## splice:optional-plotly:end @@ -207,7 +207,7 @@ trading_volume.plotly <- function( cols = rebuild_formula( names(constructed_series) ), - ma = ma, + maType = maType, na.bridge = TRUE ) @@ -311,7 +311,7 @@ trading_volume.plotly <- function( trading_volume.ggplot <- function( x, cols, - ma = list(SMA(n = 7), SMA(n = 15)), + maType = list(SMA(timePeriod = 7), SMA(timePeriod = 15)), na.bridge = FALSE, ## splice:optional-ggplot:start ## splice:optional-ggplot:end @@ -343,7 +343,7 @@ trading_volume.ggplot <- function( cols = rebuild_formula( names(constructed_series) ), - ma = ma, + maType = maType, na.bridge = TRUE ) @@ -370,7 +370,7 @@ trading_volume.ggplot <- function( list( y = trace_cols[1], geom = "bar", - direction = "direction" + directiotimePeriod = "direction" ) ) if (length(trace_cols) > 1L) { diff --git a/R/ta_WCLPRICE.R b/R/ta_WCLPRICE.R index a77e712f1..a73b7c7c8 100644 --- a/R/ta_WCLPRICE.R +++ b/R/ta_WCLPRICE.R @@ -12,6 +12,7 @@ ## splice:documentation:end #' #' @template description +#' #' @template returns weighted_close_price <- function( x, diff --git a/R/ta_WILLR.R b/R/ta_WILLR.R index 11c4759b0..d56282696 100644 --- a/R/ta_WILLR.R +++ b/R/ta_WILLR.R @@ -12,6 +12,7 @@ ## splice:documentation:end #' #' @template description +#' #' @template returns williams_oscillator <- function( x, diff --git a/R/ta_WMA.R b/R/ta_WMA.R index 23da0abf8..bbdef9897 100644 --- a/R/ta_WMA.R +++ b/R/ta_WMA.R @@ -18,6 +18,7 @@ #' indicators that supports various Moving Average specifications. #' #' @template description +#' #' @template returns weighted_moving_average <- function( x, @@ -39,7 +40,8 @@ weighted_moving_average <- function( as.integer(timePeriod) }, maType = 2L - ) + ), + class = "maType" ) return(x) diff --git a/R/utils.R b/R/utils.R index d31333a58..d34714723 100644 --- a/R/utils.R +++ b/R/utils.R @@ -362,3 +362,42 @@ map_dfr.integer <- function(x) { x } + +mapMaType <- function(x) { + switch( + as.character(x + 1), + `1` = "SMA", + `2` = "EMA", + `3` = "WMA", + `4` = "DEMA", + `5` = "TEMA", + `6` = "TRIMA", + `7` = "KAMA", + `8` = "MAMA", + `9` = "T3", + "Unkown" + ) +} + +## maType +## +## Description: +## A small S3 that helps mapping +## maTypes to and +## +as.maType <- function(x, ...) { + UseMethod("as.maType") +} + +#' @export +as.maType.maType <- function(x, ...) { + as.integer(x$maType) +} + +#' @export +as.maType.double <- function(x, ...) { + as.integer(x) +} + +#' @export +as.maType.integer <- as.maType.double diff --git a/R/zzz.R b/R/zzz.R index 8e81e80f6..8cd72e4a3 100644 --- a/R/zzz.R +++ b/R/zzz.R @@ -149,7 +149,7 @@ ) { ## reset candles on ## unload - .Call(C_reset_candle_setting) + .Call(C_reset_candle_setting, NULL) ## shutdown TA-Lib ## on unload diff --git a/README.md b/README.md index e69809f15..49faa21c4 100644 --- a/README.md +++ b/README.md @@ -36,14 +36,14 @@ including 61 candlestick pattern detectors.
-| Need | {talib} | -|:---------------------|:----------------------------------------------------------------------------------------| +| Need | {talib} | +|:---|:---| | Technical indicators | TA-Lib-backed moving averages, momentum, volatility, volume, cycle, and overlap studies | -| Candlestick patterns | Built-in Japanese candlestick pattern recognition | -| OHLCV workflows | Works directly with open, high, low, close, and volume columns | -| Performance | Computation delegated to C routines through `.Call()` | -| Dependencies | Minimal required R dependencies; plotting packages are optional | -| Charts | Composable financial charts with optional `{plotly}` and `{ggplot2}` support | +| Candlestick patterns | Built-in Japanese candlestick pattern recognition | +| OHLCV workflows | Works directly with open, high, low, close, and volume columns | +| Performance | Computation delegated to C routines through `.Call()` | +| Dependencies | Minimal required R dependencies; plotting packages are optional | +| Charts | Composable financial charts with optional `{plotly}` and `{ggplot2}` support |
@@ -101,10 +101,10 @@ features <- cbind( tail(features) #> RSI UpperBand MiddleBand LowerBand CDLENGULFING -#> 2024-12-26 01:00:00 46.48851 100487.38 96698.61 92909.83 -1 +#> 2024-12-26 01:00:00 46.48851 100487.38 96698.61 92909.83 -100 #> 2024-12-27 01:00:00 43.85488 100670.65 96512.96 92355.27 0 #> 2024-12-28 01:00:00 45.93888 100632.13 96581.91 92531.69 0 -#> 2024-12-29 01:00:00 43.12301 99628.77 95576.60 91524.43 -1 +#> 2024-12-29 01:00:00 43.12301 99628.77 95576.60 91524.43 -100 #> 2024-12-30 01:00:00 41.47686 96403.53 94231.31 92059.09 0 #> 2024-12-31 01:00:00 43.37358 95441.13 93774.23 92107.34 0 ``` @@ -173,15 +173,15 @@ compatibility with the broader ecosystem:
-| Category | TA-Lib (C) | {talib} | {talib} alias | -|:----------------------|:---------------------|:----------------------------|:------------------| -| Overlap Studies | `TA_BBANDS()` | `bollinger_bands()` | `BBANDS()` | -| Momentum Indicators | `TA_CCI()` | `commodity_channel_index()` | `CCI()` | -| Volume Indicators | `TA_OBV()` | `on_balance_volume()` | `OBV()` | -| Volatility Indicators | `TA_ATR()` | `average_true_range()` | `ATR()` | -| Price Transform | `TA_AVGPRICE()` | `average_price()` | `AVGPRICE()` | -| Cycle Indicators | `TA_HT_SINE()` | `sine_wave()` | `HT_SINE()` | -| Pattern Recognition | `TA_CDLHANGINGMAN()` | `hanging_man()` | `CDLHANGINGMAN()` | +| Category | TA-Lib (C) | {talib} | {talib} alias | +|:---|:---|:---|:---| +| Overlap Studies | `TA_BBANDS()` | `bollinger_bands()` | `BBANDS()` | +| Momentum Indicators | `TA_CCI()` | `commodity_channel_index()` | `CCI()` | +| Volume Indicators | `TA_OBV()` | `on_balance_volume()` | `OBV()` | +| Volatility Indicators | `TA_ATR()` | `average_true_range()` | `ATR()` | +| Price Transform | `TA_AVGPRICE()` | `average_price()` | `AVGPRICE()` | +| Cycle Indicators | `TA_HT_SINE()` | `sine_wave()` | `HT_SINE()` | +| Pattern Recognition | `TA_CDLHANGINGMAN()` | `hanging_man()` | `CDLHANGINGMAN()` |
diff --git a/codegen/README.md b/codegen/README.md index 2b8e20dfd..81acb2344 100644 --- a/codegen/README.md +++ b/codegen/README.md @@ -172,6 +172,7 @@ both variants in one file; `render_backend()` renders it once per backend | `${TITLE}` | `` | `Bollinger Bands` | | `${FAMILY}` | `` | `Overlap Studies` | | `${FORMULA}` | Default column formula | `~close` | +| `${PARAM_DOCS}` | `@param` roxygen lines for the optional inputs (`timePeriod`/`penetration` excluded — the man-roxygen templates document those; MAType formals carry the TA_MAType → `[SMA]`…`[T3]` legend) | `#' @param fastPeriod ([integer]). Number of period for the fast MA. Defaults to \`12\`. Can also be passed as talib::SMA().` | | `${ARGS}` | Formals, one per line, trailing comma per entry | `timePeriod = 5,` | | `${PARGS}` | Named forwarding, trailing comma per entry | `timePeriod = timePeriod,` | | `${C_SIGNATURE}` | `.Call()` args: series columns + coerced formals | `constructed_series[[1]],\n as.integer(timePeriod), ...` | diff --git a/codegen/src/metadata.rs b/codegen/src/metadata.rs index 5e1d34d5c..7f8523987 100644 --- a/codegen/src/metadata.rs +++ b/codegen/src/metadata.rs @@ -47,12 +47,14 @@ impl MetaData { /// An reduced to what the /// R signature needs: an argument name, its type -/// (for the .Call() coercion) and its default value +/// (for the .Call() coercion), its default value and +/// its one-line description (for the @param docs) #[derive(Clone, Debug, PartialEq)] pub struct OptionalArg { pub name: String, pub kind: OptionalType, pub default: String, + pub description: String, } #[derive(Clone, Debug, PartialEq)] @@ -137,10 +139,18 @@ pub fn parse_api(xml: &str) -> Vec { // map to their OHLCV column while plain double arrays // (inReal) default to the 'close' column for arg in tag_blocks(block, "RequiredInputArgument") { - let input_type = tag_text(arg, "Type").expect("input Type"); + let input_type = tag_text(arg, "Name").expect("input Type"); f.input.push(match input_type { - "Open" | "High" | "Low" | "Close" | "Volume" => input_type.to_lowercase(), + "Open" | "High" | "Low" | "Close" | "Volume" => { + input_type.to_lowercase().replace("in", "") + } + "inReal" => "close".to_string(), + "inPeriods" => "periods".to_string(), + // inReal0: x and inReal1: y + // is affiliated with Price Transforms, Statistics Functions and Math Transforms + "inReal0" => "x".to_string(), + "inReal1" => "y".to_string(), _ => "close".to_string(), }); } @@ -152,6 +162,19 @@ pub fn parse_api(xml: &str) -> Vec { let name = camel_case(tag_text(arg, "Name").expect("optional Name")); let default = tag_text(arg, "DefaultValue").expect("DefaultValue"); + // the ShortDescription becomes the @param text; the + // XML is entity-escaped and carries a few wording + // slips ('fro', 'Nb of') worth fixing at the source + let description = tag_text(arg, "ShortDescription") + .expect("optional ShortDescription") + .replace(">", ">") + .replace("<", "<") + .replace("&", "&") + .replace(" fro ", " for ") + .replace("Nb of", "Number of") + .trim_end_matches('.') + .to_string(); + let (kind, default) = match tag_text(arg, "Type").expect("optional Type") { "Integer" => (OptionalType::Integer, default.to_string()), "MA Type" => (OptionalType::MAType, default.to_string()), @@ -166,6 +189,7 @@ pub fn parse_api(xml: &str) -> Vec { name, kind, default, + description, }); } @@ -193,6 +217,7 @@ pub(crate) const SAMPLE: &str = r#" Time Period + Number of period Integer 2 @@ -202,11 +227,13 @@ pub(crate) const SAMPLE: &str = r#" Deviations up + Deviation multiplier for upper band Double 2.000000e+0 MA Type + Type of Moving Average MA Type 0 @@ -273,17 +300,20 @@ mod tests { OptionalArg { name: "timePeriod".to_string(), kind: OptionalType::Integer, - default: "5".to_string() + default: "5".to_string(), + description: "Number of period".to_string() }, OptionalArg { name: "deviationsUp".to_string(), kind: OptionalType::Double, - default: "2".to_string() + default: "2".to_string(), + description: "Deviation multiplier for upper band".to_string() }, OptionalArg { name: "maType".to_string(), kind: OptionalType::MAType, - default: "0".to_string() + default: "0".to_string(), + description: "Type of Moving Average".to_string() } ] ); diff --git a/codegen/src/render.rs b/codegen/src/render.rs index 246994e3b..9c5a58de8 100644 --- a/codegen/src/render.rs +++ b/codegen/src/render.rs @@ -15,7 +15,9 @@ //! on disk are preserved by preserve_regions() (see main.rs). use crate::metadata::{MetaData, OptionalArg, OptionalType}; -use crate::tables::{ChartType, agnostic, chart_type, function_name, is_candlestick, ma_type}; +use crate::tables::{ + ChartType, MOVING_AVERAGES, agnostic, chart_type, function_name, is_candlestick, ma_type, +}; /// The template files rendered by render_indicator(), /// loaded once from 'codegen/templates/' @@ -164,10 +166,60 @@ pub fn render_indicator(f: &MetaData, t: &Templates) -> String { name: "timePeriod".to_string(), kind: OptionalType::Integer, default: "30".to_string(), + description: "Number of period".to_string(), }, ); } + // the ${PARAM_DOCS} roxygen lines of the generic, one @param + // per optional input with its type, XML description and default, + // e.g. #' @param fastPeriod ([integer]). Number of period for + // the fast MA. Defaults to `12`. MAType formals additionally + // carry the index legend with the default's moving average + // spelled out inline. + // + // timePeriod and penetration are skipped: they are documented + // by the man-roxygen templates ('description.R' and + // 'rolling_description.R'). An empty result renders a blank + // roxygen line so the block stays contiguous + let param_docs = { + let docs = formals + .iter() + .filter(|a| a.name != "timePeriod" && a.name != "penetration") + .map(|a| match a.kind { + OptionalType::MAType => { + let default_ma = MOVING_AVERAGES + .iter() + .find(|(_, index)| index.trim_end_matches('L') == a.default) + .map(|(ma, _)| *ma) + .unwrap_or_else(|| panic!("no moving average for TA_MAType {}", a.default)); + + format!( + "#' @param {} ([integer]). {}. Defaults to `{}` ([{}]). Can also be passed as talib::{}.", + a.name, a.description, a.default, default_ma, default_ma + ) + } + _ => format!( + "#' @param {} ([{}]). {}. Defaults to `{}`.", + a.name, + match a.kind { + OptionalType::Double => "double", + _ => "integer", + }, + a.description, + a.default + ), + }) + .collect::>() + .join("\n"); + + if docs.is_empty() { + "#'".to_string() + } else { + docs + } + }; + // formals and pass-through arguments, // e.g. 'timePeriod = 14,' and 'timePeriod = timePeriod,' let args = formals @@ -216,6 +268,7 @@ pub fn render_indicator(f: &MetaData, t: &Templates) -> String { .iter() .map(|a| match a.kind { OptionalType::Double => format!("as.double({})", a.name), + OptionalType::MAType => format!("as.maType({})", a.name), _ => format!("as.integer({})", a.name), }) .collect(); @@ -229,7 +282,32 @@ pub fn render_indicator(f: &MetaData, t: &Templates) -> String { .collect::>() .join(",\n\t\t"); - let c_numeric = std::iter::once("as.double(x)".to_string()) + // the raw-vector series of the numeric/rolling path: the + // bivariate rolling statistics (BETA, CORREL: inReal0/inReal1, + // mined as x/y) take a pair of vectors, everything else a + // single 'x'. ${SERIES}/${PSERIES} render the formals and the + // forwarded arguments, one per line with a trailing comma + let series: &[&str] = if f.input == ["x", "y"] { + &["x", "y"] + } else { + &["x"] + }; + + let series_args = series + .iter() + .map(|s| format!("{s},")) + .collect::>() + .join("\n\t"); + + let pseries = series + .iter() + .map(|s| format!("{s} = {s},")) + .collect::>() + .join("\n\t\t"); + + let c_numeric = series + .iter() + .map(|s| format!("as.double({s})")) .chain(coercions.iter().cloned()) .collect::>() .join(",\n\t\t"); @@ -251,11 +329,14 @@ pub fn render_indicator(f: &MetaData, t: &Templates) -> String { .replace("${TITLE}", &f.title) .replace("${FAMILY}", &f.family) .replace("${FORMULA}", &formula) + .replace("${PARAM_DOCS}", ¶m_docs) .replace("${ARGS}", &args) .replace("${PARGS}", &pargs) .replace("${C_SIGNATURE_LOOKBACK}", &c_lookback) .replace("${C_SIGNATURE}", &c_signature) .replace("${C_NUMERIC}", &c_numeric) + .replace("${SERIES}", &series_args) + .replace("${PSERIES}", &pseries) .replace("${SPEC_FIELDS}", &spec_fields) .replace("${MA_TYPE}", ma_index.unwrap_or("-1L")) .replace("${CARGS}", &cargs) @@ -535,6 +616,19 @@ tail assert!(rendered.contains("as.double(deviationsUp)")); assert!(!rendered.contains("${"), "unreplaced placeholder"); + // the optional inputs are documented with type, description + // and default; timePeriod stays with the man-roxygen template + assert!(rendered.contains( + "#' @param deviationsUp ([double]). Deviation multiplier for upper band. Defaults to `2`." + )); + assert!(!rendered.contains("#' @param timePeriod")); + + // MAType formals carry the TA_MAType legend with the + // default's moving average spelled out inline + assert!(rendered.contains( + "#' @param maType ([integer]). Type of Moving Average. Defaults to `0` ([SMA]). Can also be passed as talib::SMA." + )); + // univariate: the numeric method is appended // after the matrix method with the raw vector assert!(rendered.contains("bollinger_bands.numeric <- function(")); @@ -544,7 +638,7 @@ tail // coerced optional inputs to C assert!(rendered.contains("bollinger_bands_lookback <- function(")); assert!(rendered.contains( - "C_impl_ta_BBANDS_lookback,\n\t\tas.integer(timePeriod),\n\t\tas.double(deviationsUp),\n\t\tas.integer(maType)\n\t)" + "C_impl_ta_BBANDS_lookback,\n\t\tas.integer(timePeriod),\n\t\tas.double(deviationsUp),\n\t\tas.maType(maType)\n\t)" )); // BBANDS is a Main chart indicator: the plotly and ggplot @@ -642,16 +736,40 @@ tail "constructed_series[[1]],\n\t\tas.double(fastLimit),\n\t\tas.double(slowLimit)" )); + // every optional input beyond timePeriod/penetration is + // documented with type, description and default + let macd = render("MACD"); + assert!(macd.contains( + "#' @param fastPeriod ([integer]). Number of period for the fast MA. Defaults to `12`." + )); + assert!(macd.contains( + "#' @param signalPeriod ([integer]). Smoothing for the signal line (nb of period). Defaults to `9`." + )); + // rolling statistics: protected .Call() region, // no chart methods let stddev = render("STDDEV"); assert!(stddev.contains("rolling_standard_deviation <- function(")); + assert!( + stddev.contains( + "#' @param deviations ([double]). Number of deviations. Defaults to `1`." + ) + ); + assert!(!stddev.contains("#' @param timePeriod")); assert!(stddev.contains("@template rolling_returns")); assert!(stddev.contains("## splice:call:start")); assert!(stddev.contains("as.double(x),")); + assert!(!stddev.contains("as.double(y)")); assert!(!stddev.contains(".plotly")); assert!(!stddev.contains(".ggplot")); + // the bivariate rolling statistics (inReal0/inReal1) take + // the pair x/y and pass both series to the C routine + let correl = render("CORREL"); + assert!(correl.contains("rolling_correlation <- function(\n\tx,\n\ty,")); + assert!(correl.contains("as.double(x),\n\t\tas.double(y),\n\t\tas.integer(timePeriod)")); + assert!(correl.contains("x = x,\n\t\ty = y,")); + // candlesticks: the agnostic flag comes // from AGNOSTIC_PATTERNS assert!(render("CDLDOJI").contains("agnostic = TRUE")); diff --git a/codegen/src/tables.rs b/codegen/src/tables.rs index 8dd60fcf2..80b74860a 100644 --- a/codegen/src/tables.rs +++ b/codegen/src/tables.rs @@ -21,6 +21,7 @@ pub const FUNCTION_NAMES: &[(&str, &str)] = &[ ("AROONOSC", "aroon_oscillator"), ("ATR", "average_true_range"), ("AVGPRICE", "average_price"), + ("AVGDEV", "average_deviation"), ("BBANDS", "bollinger_bands"), ("BETA", "rolling_beta"), ("BOP", "balance_of_power"), @@ -99,6 +100,7 @@ pub const FUNCTION_NAMES: &[(&str, &str)] = &[ ("HT_TRENDMODE", "trend_cycle_mode"), ("IMI", "intraday_movement_index"), ("KAMA", "kaufman_adaptive_moving_average"), + ("MAVP", "variable_moving_average_period"), ("MACD", "moving_average_convergence_divergence"), ("MACDEXT", "extended_moving_average_convergence_divergence"), ("MACDFIX", "fixed_moving_average_convergence_divergence"), @@ -107,6 +109,7 @@ pub const FUNCTION_NAMES: &[(&str, &str)] = &[ ("MEDPRICE", "median_price"), ("MFI", "money_flow_index"), ("MIDPRICE", "midpoint_price"), + ("MIDPOINT", "midpoint_period"), ("MIN", "rolling_min"), ("MINUS_DI", "minus_directional_indicator"), ("MINUS_DM", "minus_directional_movement"), @@ -182,10 +185,13 @@ pub const CHART_TYPES: &[(&str, ChartType)] = &[ ("HT_TRENDLINE", ChartType::Main), ("HT_TRENDMODE", ChartType::Sub), ("IMI", ChartType::Sub), + ("MAVP", ChartType::Main), ("MACD", ChartType::Sub), ("MACDEXT", ChartType::Sub), ("MACDFIX", ChartType::Sub), ("MFI", ChartType::Sub), + ("MIDPOINT", ChartType::Main), + ("MIDPRICE", ChartType::Main), ("MINUS_DI", ChartType::Sub), ("MINUS_DM", ChartType::Sub), ("MOM", ChartType::Sub), @@ -285,6 +291,7 @@ pub const EXCLUDED_INDICATORS: &[&str] = &[ "LINEARREG_ANGLE", "LINEARREG_SLOPE", "LINEARREG_INTERCEPT", + "TSF", ]; /// GroupIds excluded from generation altogether; diff --git a/codegen/templates/indicator_template.R b/codegen/templates/indicator_template.R index 1d1e5fe71..69e16a45e 100644 --- a/codegen/templates/indicator_template.R +++ b/codegen/templates/indicator_template.R @@ -12,6 +12,7 @@ ## splice:documentation:end #' #' @template description +${PARAM_DOCS} #' @template returns ${FUN} <- function( x, diff --git a/codegen/templates/moving_average_template.R b/codegen/templates/moving_average_template.R index 44fb5e40e..40b6f35bb 100644 --- a/codegen/templates/moving_average_template.R +++ b/codegen/templates/moving_average_template.R @@ -18,6 +18,7 @@ #' indicators that supports various Moving Average specifications. #' #' @template description +${PARAM_DOCS} #' @template returns ${FUN} <- function( x, @@ -35,7 +36,8 @@ ${FUN} <- function( list( ${SPEC_FIELDS}, maType = ${MA_TYPE} - ) + ), + class = "maType" ) return(x) diff --git a/codegen/templates/rolling_template.R b/codegen/templates/rolling_template.R index f1858d845..469554a99 100644 --- a/codegen/templates/rolling_template.R +++ b/codegen/templates/rolling_template.R @@ -10,9 +10,10 @@ ## splice:documentation:end #' #' @template rolling_description +${PARAM_DOCS} #' @template rolling_returns ${FUN} <- function( - x, + ${SERIES} ${ARGS} na.bridge = FALSE) { UseMethod("${FUN}") @@ -30,7 +31,7 @@ ${ALIAS} <- ${FUN} #' #' @export ${FUN}.default <- function( - x, + ${SERIES} ${ARGS} na.bridge = FALSE) { @@ -58,14 +59,14 @@ ${FUN}.default <- function( #' #' @export ${FUN}.numeric <- function( - x, + ${SERIES} ${ARGS} na.bridge = FALSE) { ## calculate indicator and ## return as data.frame x <- ${FUN}.default( - x = x, + ${PSERIES} ${PARGS} na.bridge = na.bridge ) diff --git a/man-roxygen/description.R b/man-roxygen/description.R index 7b7426fb6..06f0babbc 100644 --- a/man-roxygen/description.R +++ b/man-roxygen/description.R @@ -62,12 +62,12 @@ <% } %> #' <% fun_args <- names(formals(.fun)) %> -<% if ("n" %in% fun_args) { %> -#' @param n ([integer]). Lookback period (window size). A positive [integer] +<% if ("timePeriod" %in% fun_args) { %> +#' @param timePeriod ([integer]). Lookback period (window size). A positive [integer] #' of [length] 1. <% } %> -<% if ("eps" %in% fun_args) { %> -#' @param eps ([double]). Penetration threshold for candlestick pattern +<% if ("penetration" %in% fun_args) { %> +#' @param penetration ([double]). Penetration threshold for candlestick pattern #' recognition, expressed as a fraction of the candle body. A [double] of #' [length] 1. <% } %> @@ -92,6 +92,7 @@ #' @concept trading #' @concept algorithmic trading #' +<% if (!exists(".custom_example")) { %> <% if (grepl(pattern = "Price Transform", x = .family)) { %> #' @examples #' ## load Bitcoin (BTC) @@ -136,3 +137,4 @@ #' } <% } %> +<% } %> diff --git a/man-roxygen/returns.R b/man-roxygen/returns.R index 603b7d9d2..30917c672 100644 --- a/man-roxygen/returns.R +++ b/man-roxygen/returns.R @@ -2,4 +2,4 @@ #' #' An object of same [class] and [length] of `x`: #' -#' `r generate_returns_section(<%= tolower(.fun) %>(talib::BTC))` +#' `r BTC <- talib::BTC; BTC$periods <- runif(nrow(BTC), 10, 20); generate_returns_section(<%= tolower(.fun) %>(BTC))` diff --git a/man-roxygen/rolling_description.R b/man-roxygen/rolling_description.R index a9dda6307..5143c256e 100644 --- a/man-roxygen/rolling_description.R +++ b/man-roxygen/rolling_description.R @@ -30,8 +30,8 @@ #' <% } %> <% fun_args <- names(formals(.fun)) %> -<% if ("n" %in% fun_args) { %> -#' @param n ([integer]). Lookback period (window size). A positive [integer] +<% if ("timePeriod" %in% fun_args) { %> +#' @param timePeriod ([integer]). Lookback period (window size). A positive [integer] #' of [length] 1. <% } %> <% if ("na.bridge" %in% fun_args) { %> diff --git a/man/examples/MAVP-example.R b/man/examples/MAVP-example.R new file mode 100644 index 000000000..99b52b831 --- /dev/null +++ b/man/examples/MAVP-example.R @@ -0,0 +1,37 @@ +## load Bitcoin (BTC) +## series +data(BTC, package = "talib") + +## define a random series +## of periods to evaluate +## each candle +BTC$periods <- runif( + n = nrow(BTC), + min = 5, + max = 10 +) + +## calculate the indicator +## for Bitcoin (BTC) +utils::tail( + talib::variable_moving_average_period( + BTC + ) +) + +## visualize the indicator +## with talib::chart() +## +## see ?talib::chart or ?talib::indicator +## for more details +{ + ## chart OHLC-V + ## series with talib::chart() + talib::chart(BTC) + + ## chart indicator + ## with default values + talib::indicator( + talib::variable_moving_average_period + ) +} diff --git a/src/TA-Lib.h b/src/TA-Lib.h index 3e5fb4138..33b0c6acc 100644 --- a/src/TA-Lib.h +++ b/src/TA-Lib.h @@ -128,7 +128,6 @@ TA_INDICATOR(TEMA, TA_DOUBLE, TA_INPUT(inReal), TA_OPTIONS(OPTIONAL_INTEGER(optI TA_INDICATOR(TRANGE, TA_DOUBLE, TA_INPUT(inHigh, inLow, inClose), TA_OPTIONS(), TA_OUTPUT(outReal), TA_OUTPUT_NAME(TRANGE), NOT_CANDLESTICK) TA_INDICATOR(TRIMA, TA_DOUBLE, TA_INPUT(inReal), TA_OPTIONS(OPTIONAL_INTEGER(optInTimePeriod)), TA_OUTPUT(outReal), TA_OUTPUT_NAME(TRIMA), NOT_CANDLESTICK) TA_INDICATOR(TRIX, TA_DOUBLE, TA_INPUT(inReal), TA_OPTIONS(OPTIONAL_INTEGER(optInTimePeriod)), TA_OUTPUT(outReal), TA_OUTPUT_NAME(TRIX), NOT_CANDLESTICK) -TA_INDICATOR(TSF, TA_DOUBLE, TA_INPUT(inReal), TA_OPTIONS(OPTIONAL_INTEGER(optInTimePeriod)), TA_OUTPUT(outReal), TA_OUTPUT_NAME(TSF), NOT_CANDLESTICK) TA_INDICATOR(TYPPRICE, TA_DOUBLE, TA_INPUT(inHigh, inLow, inClose), TA_OPTIONS(), TA_OUTPUT(outReal), TA_OUTPUT_NAME(TYPPRICE), NOT_CANDLESTICK) TA_INDICATOR(ULTOSC, TA_DOUBLE, TA_INPUT(inHigh, inLow, inClose), TA_OPTIONS(OPTIONAL_INTEGER(optInTimePeriod1), OPTIONAL_INTEGER(optInTimePeriod2), OPTIONAL_INTEGER(optInTimePeriod3)), TA_OUTPUT(outReal), TA_OUTPUT_NAME(ULTOSC), NOT_CANDLESTICK) TA_INDICATOR(VAR, TA_DOUBLE, TA_INPUT(inReal), TA_OPTIONS(OPTIONAL_INTEGER(optInTimePeriod), OPTIONAL_DOUBLE(optInNbDev)), TA_OUTPUT(outReal), TA_OUTPUT_NAME(VAR), NOT_CANDLESTICK) @@ -256,7 +255,6 @@ TA_LOOKBACK(TEMA, TA_OPTIONS(OPTIONAL_INTEGER(optInTimePeriod))) TA_LOOKBACK(TRANGE, TA_OPTIONS()) TA_LOOKBACK(TRIMA, TA_OPTIONS(OPTIONAL_INTEGER(optInTimePeriod))) TA_LOOKBACK(TRIX, TA_OPTIONS(OPTIONAL_INTEGER(optInTimePeriod))) -TA_LOOKBACK(TSF, TA_OPTIONS(OPTIONAL_INTEGER(optInTimePeriod))) TA_LOOKBACK(TYPPRICE, TA_OPTIONS()) TA_LOOKBACK(ULTOSC, TA_OPTIONS(OPTIONAL_INTEGER(optInTimePeriod1), OPTIONAL_INTEGER(optInTimePeriod2), OPTIONAL_INTEGER(optInTimePeriod3))) TA_LOOKBACK(VAR, TA_OPTIONS(OPTIONAL_INTEGER(optInTimePeriod), OPTIONAL_DOUBLE(optInNbDev))) diff --git a/tests/testthat/helper-data.R b/tests/testthat/helper-data.R new file mode 100644 index 000000000..88abd10a5 --- /dev/null +++ b/tests/testthat/helper-data.R @@ -0,0 +1,29 @@ +## testthat helper - sourced automatically before the test files +## +## MAVP (variable_moving_average_period) consumes a 'periods' column +## alongside 'close' (default formula ~close + periods), but the +## built-in datasets ship without one. The autogenerated tests call +## every indicator on the bare datasets, so shadow them here with +## copies carrying a deterministic 'periods' column. Harmless for the +## remaining indicators - they select their columns via formula and +## ignore the extra column. +.add_periods <- function(x) { + periods <- rep_len( + x = 5:10, + length.out = nrow(x) + ) + + if (is.matrix(x)) { + return( + cbind(x, periods = periods) + ) + } + + x$periods <- periods + + x +} + +BTC <- .add_periods(talib::BTC) +SPY <- .add_periods(talib::SPY) +ATOM <- .add_periods(talib::ATOM) diff --git a/tests/testthat/test-moving_average_spec.R b/tests/testthat/test-moving_average_spec.R deleted file mode 100644 index 3707b873d..000000000 --- a/tests/testthat/test-moving_average_spec.R +++ /dev/null @@ -1,319 +0,0 @@ -## Moving-Average specification mode -## -## When an MA function is called without 'x' it returns a named list -## ("spec") consumed by indicators that support multiple MA types -## (APO, PPO, BBANDS, MACDEXT, STOCH, STOCHF, STOCHRSI). -## -## Hand-written (not generated via codegen/) because the contract is -## cross-cutting across the nine MA functions and would otherwise -## require threading maType through generate_unit-tests.sh. - -## ---- expected spec per MA ------------------------------------------------- -## Each row: function, alias, maType code, default-field values. -## Simple MAs take n only; MAMA takes fast/slow; T3 takes n/vfactor. -.ma_specs <- list( - list( - name = "SMA", - fun = simple_moving_average, - alias = SMA, - maType = 0L, - n = 30L - ), - list( - name = "EMA", - fun = exponential_moving_average, - alias = EMA, - maType = 1L, - n = 30L - ), - list( - name = "WMA", - fun = weighted_moving_average, - alias = WMA, - maType = 2L, - n = 30L - ), - list( - name = "DEMA", - fun = double_exponential_moving_average, - alias = DEMA, - maType = 3L, - n = 30L - ), - list( - name = "TEMA", - fun = triple_exponential_moving_average, - alias = TEMA, - maType = 4L, - n = 30L - ), - list( - name = "TRIMA", - fun = triangular_moving_average, - alias = TRIMA, - maType = 5L, - n = 30L - ), - list( - name = "KAMA", - fun = kaufman_adaptive_moving_average, - alias = KAMA, - maType = 6L, - n = 30L - ) -) - -## ---- shape and default contract (simple n-only MAs) ------------------------ -for (.spec in .ma_specs) { - local({ - spec <- .spec - - testthat::test_that( - sprintf("%s() without x returns list(n, maType)", spec$name), - { - out <- spec$fun() - - testthat::expect_type(out, "list") - testthat::expect_named(out, c("n", "maType")) - testthat::expect_identical(out$maType, spec$maType) - testthat::expect_identical(out$n, spec$n) - } - ) - - testthat::test_that( - sprintf( - "%s() alias returns identical spec to long-form", - spec$name - ), - { - testthat::expect_identical(spec$alias(), spec$fun()) - testthat::expect_identical(spec$alias(n = 7), spec$fun(n = 7)) - } - ) - - testthat::test_that( - sprintf( - "%s() coerces n to integer and honours explicit value", - spec$name - ), - { - ## explicit integer round-trips - testthat::expect_identical(spec$fun(n = 14)$n, 14L) - ## double is truncated to integer (matches as.integer semantics) - testthat::expect_identical(spec$fun(n = 14.7)$n, 14L) - ## storage type is integer even when caller passes double - testthat::expect_identical(typeof(spec$fun(n = 5)$n), "integer") - } - ) - }) -} - -## ---- MAMA (n + fast / slow) ------------------------------------------------ -## MAMA carries 'n' as a spec-only field so downstream consumers -## (BBANDS, STOCH, MACDEXT, ...) can read ma$n uniformly even though -## MAMA's own standalone calculation is adaptive and does not use n. -testthat::test_that("MAMA() without x returns list(n, fast, slow, maType = 7L)", { - out <- mesa_adaptive_moving_average() - - testthat::expect_type(out, "list") - testthat::expect_named(out, c("n", "fast", "slow", "maType")) - testthat::expect_identical(out$maType, 7L) - testthat::expect_identical(out$n, 30L) - testthat::expect_identical(out$fast, 0.5) - testthat::expect_identical(out$slow, 0.05) -}) - -testthat::test_that("MAMA() alias matches long-form and coerces types", { - testthat::expect_identical(MAMA(), mesa_adaptive_moving_average()) - testthat::expect_identical( - MAMA(n = 14, fast = 0.3, slow = 0.1), - mesa_adaptive_moving_average(n = 14, fast = 0.3, slow = 0.1) - ) - - out <- MAMA(n = 14.7, fast = 1L, slow = 0L) - testthat::expect_identical(out$n, 14L) - testthat::expect_identical(typeof(out$n), "integer") - testthat::expect_identical(out$fast, 1) - testthat::expect_identical(out$slow, 0) - testthat::expect_identical(typeof(out$fast), "double") - testthat::expect_identical(typeof(out$slow), "double") -}) - -## 'n' is spec-only: MAMA's standalone output must NOT depend on it. -## Regression guard against someone wiring 'n' into the C call. -testthat::test_that("MAMA(x, n) ignores n in standalone calculation", { - default_out <- mesa_adaptive_moving_average(BTC) - n14_out <- mesa_adaptive_moving_average(BTC, n = 14) - n50_out <- mesa_adaptive_moving_average(BTC, n = 50) - - testthat::expect_equal(default_out, n14_out) - testthat::expect_equal(default_out, n50_out) -}) - -## ---- T3 (n + vfactor) ------------------------------------------------------ -testthat::test_that("T3() without x returns list(n, vfactor, maType = 8L)", { - out <- t3_exponential_moving_average() - - testthat::expect_type(out, "list") - testthat::expect_named(out, c("n", "vfactor", "maType")) - testthat::expect_identical(out$maType, 8L) - testthat::expect_identical(out$n, 5L) - testthat::expect_identical(out$vfactor, 0.7) -}) - -testthat::test_that("T3() alias matches long-form and coerces types", { - testthat::expect_identical(T3(), t3_exponential_moving_average()) - testthat::expect_identical( - T3(n = 10, vfactor = 0.5), - t3_exponential_moving_average(n = 10, vfactor = 0.5) - ) - - out <- T3(n = 9.9, vfactor = 1L) - testthat::expect_identical(out$n, 9L) - testthat::expect_identical(out$vfactor, 1) - testthat::expect_identical(typeof(out$n), "integer") - testthat::expect_identical(typeof(out$vfactor), "double") -}) - -## ---- downstream integration ------------------------------------------------ -## Every MA spec carries 'n' + 'maType' and must drive every consumer: -## APO / PPO (read maType only) and -## BBANDS / stochastic / STOCHF / STOCHRSI / MACDEXT (read both n and maType). - -.all_specs <- list( - SMA = SMA, - EMA = EMA, - WMA = WMA, - DEMA = DEMA, - TEMA = TEMA, - TRIMA = TRIMA, - KAMA = KAMA, - MAMA = MAMA, - T3 = T3 -) - -for (.nm in names(.all_specs)) { - local({ - nm <- .nm - mk <- .all_specs[[nm]] - - testthat::test_that( - sprintf("APO() accepts %s() spec", nm), - { - out <- absolute_price_oscillator(BTC, ma = mk()) - testthat::expect_s3_class(out, "data.frame") - testthat::expect_equal(nrow(out), nrow(BTC)) - testthat::expect_true(any(is.finite(out[[1]]))) - } - ) - - testthat::test_that( - sprintf("PPO() accepts %s() spec", nm), - { - out <- percentage_price_oscillator(BTC, ma = mk()) - testthat::expect_s3_class(out, "data.frame") - testthat::expect_equal(nrow(out), nrow(BTC)) - testthat::expect_true(any(is.finite(out[[1]]))) - } - ) - - testthat::test_that( - sprintf("BBANDS() accepts %s() spec with explicit n", nm), - { - out <- bollinger_bands(BTC, ma = mk(n = 10)) - testthat::expect_s3_class(out, "data.frame") - testthat::expect_equal(nrow(out), nrow(BTC)) - testthat::expect_true(any(is.finite(out[[1]]))) - } - ) - - testthat::test_that( - sprintf("stochastic() accepts %s() spec for slowk/slowd", nm), - { - out <- stochastic(BTC, slowk = mk(n = 3), slowd = mk(n = 3)) - testthat::expect_s3_class(out, "data.frame") - testthat::expect_equal(nrow(out), nrow(BTC)) - testthat::expect_true(any(is.finite(out[[1]]))) - } - ) - - testthat::test_that( - sprintf("STOCHF() accepts %s() spec for fastd", nm), - { - out <- STOCHF(BTC, fastd = mk(n = 3)) - testthat::expect_s3_class(out, "data.frame") - testthat::expect_equal(nrow(out), nrow(BTC)) - testthat::expect_true(any(is.finite(out[[1]]))) - } - ) - - testthat::test_that( - sprintf("STOCHRSI() accepts %s() spec for fastd", nm), - { - out <- STOCHRSI(BTC, fastd = mk(n = 3)) - testthat::expect_s3_class(out, "data.frame") - testthat::expect_equal(nrow(out), nrow(BTC)) - testthat::expect_true(any(is.finite(out[[1]]))) - } - ) - - testthat::test_that( - sprintf("MACDEXT() accepts %s() spec for fast/slow/signal", nm), - { - out <- extended_moving_average_convergence_divergence( - BTC, - fast = mk(n = 5), - slow = mk(n = 10), - signal = mk(n = 3) - ) - testthat::expect_s3_class(out, "data.frame") - testthat::expect_equal(nrow(out), nrow(BTC)) - testthat::expect_true(any(is.finite(out[[1]]))) - } - ) - }) -} - -## ---- maType drives the consumer's output ----------------------------------- -## Swap EMA for SMA in APO: output must differ. Protects against consumers -## silently ignoring the spec (the bug shape that shape tests alone miss). -testthat::test_that("APO output depends on the MA spec (EMA != SMA)", { - out_sma <- absolute_price_oscillator(BTC, ma = SMA(n = 9)) - out_ema <- absolute_price_oscillator(BTC, ma = EMA(n = 9)) - - testthat::expect_equal(dim(out_sma), dim(out_ema)) - ## require an actual numerical difference somewhere in the overlap - testthat::expect_false( - isTRUE(all.equal(out_sma[[1]], out_ema[[1]])) - ) -}) - -testthat::test_that("BBANDS output depends on the MA spec (WMA != SMA)", { - out_sma <- bollinger_bands(BTC, ma = SMA(n = 10)) - out_wma <- bollinger_bands(BTC, ma = WMA(n = 10)) - - testthat::expect_equal(dim(out_sma), dim(out_wma)) - testthat::expect_false( - isTRUE(all.equal(out_sma[[1]], out_wma[[1]])) - ) -}) - -## 'n' must drive the consumer's window. If a future change drops ma$n -## lookup in favour of a hard-coded default, these tests catch it. -testthat::test_that("BBANDS consumes ma$n (different n => different output)", { - out_10 <- bollinger_bands(BTC, ma = SMA(n = 10)) - out_30 <- bollinger_bands(BTC, ma = SMA(n = 30)) - - testthat::expect_false( - isTRUE(all.equal(out_10[[1]], out_30[[1]])) - ) -}) - -testthat::test_that("stochastic() consumes slowk$n (different n => different output)", { - out_3 <- stochastic(BTC, slowk = SMA(n = 3), slowd = SMA(n = 3)) - out_7 <- stochastic(BTC, slowk = SMA(n = 7), slowd = SMA(n = 7)) - - testthat::expect_false( - isTRUE(all.equal(out_3[[1]], out_7[[1]])) - ) -}) diff --git a/tests/testthat/test-ta_AVGDEV.R b/tests/testthat/test-ta_AVGDEV.R index 1da02754b..983f86e55 100644 --- a/tests/testthat/test-ta_AVGDEV.R +++ b/tests/testthat/test-ta_AVGDEV.R @@ -6,11 +6,11 @@ ## alias and function similarity ## checks this ensures that -## AVGDEV and AVGDEV produces the same results +## average_deviation and AVGDEV produces the same results testthat::test_that(desc = 'Alias and function similarity', code = { ## 1) test that the alias and ## function returns the same values - output <- AVGDEV(SPY) + output <- average_deviation(SPY) alias <- AVGDEV(SPY) ## 1.1) check if the values @@ -29,7 +29,7 @@ testthat::test_that(desc = 'Class in, class out ()', code = { ## 1) check that the output class ## matches the input class testthat::expect_true( - inherits(AVGDEV(SPY), class(SPY)) + inherits(average_deviation(SPY), class(SPY)) ) }) @@ -38,7 +38,7 @@ testthat::test_that(desc = 'Class in, class out ()', code = { ## 1) check that the output class ## matches the input class testthat::expect_true( - inherits(AVGDEV(BTC), class(BTC)) + inherits(average_deviation(BTC), class(BTC)) ) }) @@ -50,10 +50,10 @@ testthat::test_that(desc = 'Class in, class out ()', code = { ## series() function testthat::test_that(desc = 'Default calls', code = { testthat::expect_equal( - object = AVGDEV( + object = average_deviation( BTC ), - expected = AVGDEV( + expected = average_deviation( BTC, cols = ~close ) @@ -66,7 +66,7 @@ testthat::test_that(desc = 'Default calls', code = { ## object testthat::test_that(desc = 'Equal length of input and output for with na.bridge = TRUE', code = { testthat::expect_equal( - object = nrow(AVGDEV( + object = nrow(average_deviation( ATOM, na.bridge = TRUE )), @@ -81,7 +81,7 @@ testthat::test_that(desc = 'Row names are respected for , na.bridge x_names <- row.names(ATOM) ## calculate indicator - indicator <- AVGDEV(ATOM, na.bridge = TRUE) + indicator <- average_deviation(ATOM, na.bridge = TRUE) testthat::expect_equal( object = x_names, @@ -95,7 +95,7 @@ testthat::test_that(desc = 'Row names are respected for , na.bridge ## object testthat::test_that(desc = 'Equal length of input and output for ', code = { testthat::expect_equal( - object = nrow(AVGDEV( + object = nrow(average_deviation( BTC )), expected = nrow(BTC) @@ -109,7 +109,7 @@ testthat::test_that(desc = 'Row names are respected for ', code = { x_names <- row.names(BTC) ## calculate indicator - indicator <- AVGDEV(BTC) + indicator <- average_deviation(BTC) testthat::expect_equal( object = x_names, @@ -120,7 +120,7 @@ testthat::test_that(desc = 'Row names are respected for ', code = { ## object testthat::test_that(desc = 'Equal length of input and output for ', code = { testthat::expect_equal( - object = nrow(AVGDEV( + object = nrow(average_deviation( SPY )), expected = nrow(SPY) @@ -136,7 +136,7 @@ testthat::test_that(desc = 'Row names are respected for ', code = { rownames(SPY) <- paste0("row", 1:nrow(SPY)) ## calculate indicator - indicator <- AVGDEV(SPY) + indicator <- average_deviation(SPY) testthat::expect_equal( object = paste0("row", 1:nrow(SPY)), @@ -150,7 +150,7 @@ testthat::test_that(desc = ' methods', code = { ## check that the method ## runs x <- testthat::expect_no_condition( - AVGDEV(BTC[[1]]) + average_deviation(BTC[[1]]) ) target_length <- length(BTC[[1]]) diff --git a/tests/testthat/test-ta_MAVP.R b/tests/testthat/test-ta_MAVP.R index 15b77dc14..c7a7746a1 100644 --- a/tests/testthat/test-ta_MAVP.R +++ b/tests/testthat/test-ta_MAVP.R @@ -6,11 +6,11 @@ ## alias and function similarity ## checks this ensures that -## MAVP and MAVP produces the same results +## variable_moving_average_period and MAVP produces the same results testthat::test_that(desc = 'Alias and function similarity', code = { ## 1) test that the alias and ## function returns the same values - output <- MAVP(SPY) + output <- variable_moving_average_period(SPY) alias <- MAVP(SPY) ## 1.1) check if the values @@ -29,7 +29,7 @@ testthat::test_that(desc = 'Class in, class out ()', code = { ## 1) check that the output class ## matches the input class testthat::expect_true( - inherits(MAVP(SPY), class(SPY)) + inherits(variable_moving_average_period(SPY), class(SPY)) ) }) @@ -38,7 +38,7 @@ testthat::test_that(desc = 'Class in, class out ()', code = { ## 1) check that the output class ## matches the input class testthat::expect_true( - inherits(MAVP(BTC), class(BTC)) + inherits(variable_moving_average_period(BTC), class(BTC)) ) }) @@ -50,12 +50,12 @@ testthat::test_that(desc = 'Class in, class out ()', code = { ## series() function testthat::test_that(desc = 'Default calls', code = { testthat::expect_equal( - object = MAVP( + object = variable_moving_average_period( BTC ), - expected = MAVP( + expected = variable_moving_average_period( BTC, - cols = ~close + cols = ~ close + periods ) ) }) @@ -66,7 +66,7 @@ testthat::test_that(desc = 'Default calls', code = { ## object testthat::test_that(desc = 'Equal length of input and output for with na.bridge = TRUE', code = { testthat::expect_equal( - object = nrow(MAVP( + object = nrow(variable_moving_average_period( ATOM, na.bridge = TRUE )), @@ -81,7 +81,7 @@ testthat::test_that(desc = 'Row names are respected for , na.bridge x_names <- row.names(ATOM) ## calculate indicator - indicator <- MAVP(ATOM, na.bridge = TRUE) + indicator <- variable_moving_average_period(ATOM, na.bridge = TRUE) testthat::expect_equal( object = x_names, @@ -95,7 +95,7 @@ testthat::test_that(desc = 'Row names are respected for , na.bridge ## object testthat::test_that(desc = 'Equal length of input and output for ', code = { testthat::expect_equal( - object = nrow(MAVP( + object = nrow(variable_moving_average_period( BTC )), expected = nrow(BTC) @@ -109,7 +109,7 @@ testthat::test_that(desc = 'Row names are respected for ', code = { x_names <- row.names(BTC) ## calculate indicator - indicator <- MAVP(BTC) + indicator <- variable_moving_average_period(BTC) testthat::expect_equal( object = x_names, @@ -120,7 +120,7 @@ testthat::test_that(desc = 'Row names are respected for ', code = { ## object testthat::test_that(desc = 'Equal length of input and output for ', code = { testthat::expect_equal( - object = nrow(MAVP( + object = nrow(variable_moving_average_period( SPY )), expected = nrow(SPY) @@ -136,10 +136,96 @@ testthat::test_that(desc = 'Row names are respected for ', code = { rownames(SPY) <- paste0("row", 1:nrow(SPY)) ## calculate indicator - indicator <- MAVP(SPY) + indicator <- variable_moving_average_period(SPY) testthat::expect_equal( object = paste0("row", 1:nrow(SPY)), expected = rownames(indicator) ) }) + +## -method checks for +## and +## +## checks +testthat::test_that(desc = '-methods for ', code = { + ## check that variable_moving_average_period can + ## use without any issues + output <- testthat::expect_no_error( + { + chart(BTC) + indicator(variable_moving_average_period) + } + ) + + ## check that the output + ## is a -object + testthat::expect_true( + inherits(output, "plotly") + ) +}) + +## checks +testthat::test_that(desc = '-methods for ', code = { + ## check that variable_moving_average_period can + ## use without any issues + output <- testthat::expect_no_error( + { + chart(SPY) + indicator(variable_moving_average_period) + } + ) + + ## check that the output + ## is a -object + testthat::expect_true( + inherits(output, "plotly") + ) +}) + +## -method checks for +## and +## +## checks +testthat::test_that(desc = '-methods for ', code = { + testthat::skip_if_not_installed("ggplot2") + + ## check that variable_moving_average_period can + ## use without any issues + output <- testthat::expect_no_error( + { + options(talib.chart.backend = "ggplot2") + on.exit(options(talib.chart.backend = "plotly")) + chart(BTC) + indicator(variable_moving_average_period) + } + ) + + ## check that the output + ## is a -object or + testthat::expect_true( + inherits(output, "gg") || inherits(output, "talib_chart") + ) +}) + +## checks +testthat::test_that(desc = '-methods for ', code = { + testthat::skip_if_not_installed("ggplot2") + + ## check that variable_moving_average_period can + ## use without any issues + output <- testthat::expect_no_error( + { + options(talib.chart.backend = "ggplot2") + on.exit(options(talib.chart.backend = "plotly")) + chart(SPY) + indicator(variable_moving_average_period) + } + ) + + ## check that the output + ## is a -object or + testthat::expect_true( + inherits(output, "gg") || inherits(output, "talib_chart") + ) +}) diff --git a/tests/testthat/test-ta_MIDPOINT.R b/tests/testthat/test-ta_MIDPOINT.R index 3fff2596c..e650df52f 100644 --- a/tests/testthat/test-ta_MIDPOINT.R +++ b/tests/testthat/test-ta_MIDPOINT.R @@ -6,11 +6,11 @@ ## alias and function similarity ## checks this ensures that -## MIDPOINT and MIDPOINT produces the same results +## midpoint_period and MIDPOINT produces the same results testthat::test_that(desc = 'Alias and function similarity', code = { ## 1) test that the alias and ## function returns the same values - output <- MIDPOINT(SPY) + output <- midpoint_period(SPY) alias <- MIDPOINT(SPY) ## 1.1) check if the values @@ -29,7 +29,7 @@ testthat::test_that(desc = 'Class in, class out ()', code = { ## 1) check that the output class ## matches the input class testthat::expect_true( - inherits(MIDPOINT(SPY), class(SPY)) + inherits(midpoint_period(SPY), class(SPY)) ) }) @@ -38,7 +38,7 @@ testthat::test_that(desc = 'Class in, class out ()', code = { ## 1) check that the output class ## matches the input class testthat::expect_true( - inherits(MIDPOINT(BTC), class(BTC)) + inherits(midpoint_period(BTC), class(BTC)) ) }) @@ -50,10 +50,10 @@ testthat::test_that(desc = 'Class in, class out ()', code = { ## series() function testthat::test_that(desc = 'Default calls', code = { testthat::expect_equal( - object = MIDPOINT( + object = midpoint_period( BTC ), - expected = MIDPOINT( + expected = midpoint_period( BTC, cols = ~close ) @@ -66,7 +66,7 @@ testthat::test_that(desc = 'Default calls', code = { ## object testthat::test_that(desc = 'Equal length of input and output for with na.bridge = TRUE', code = { testthat::expect_equal( - object = nrow(MIDPOINT( + object = nrow(midpoint_period( ATOM, na.bridge = TRUE )), @@ -81,7 +81,7 @@ testthat::test_that(desc = 'Row names are respected for , na.bridge x_names <- row.names(ATOM) ## calculate indicator - indicator <- MIDPOINT(ATOM, na.bridge = TRUE) + indicator <- midpoint_period(ATOM, na.bridge = TRUE) testthat::expect_equal( object = x_names, @@ -95,7 +95,7 @@ testthat::test_that(desc = 'Row names are respected for , na.bridge ## object testthat::test_that(desc = 'Equal length of input and output for ', code = { testthat::expect_equal( - object = nrow(MIDPOINT( + object = nrow(midpoint_period( BTC )), expected = nrow(BTC) @@ -109,7 +109,7 @@ testthat::test_that(desc = 'Row names are respected for ', code = { x_names <- row.names(BTC) ## calculate indicator - indicator <- MIDPOINT(BTC) + indicator <- midpoint_period(BTC) testthat::expect_equal( object = x_names, @@ -120,7 +120,7 @@ testthat::test_that(desc = 'Row names are respected for ', code = { ## object testthat::test_that(desc = 'Equal length of input and output for ', code = { testthat::expect_equal( - object = nrow(MIDPOINT( + object = nrow(midpoint_period( SPY )), expected = nrow(SPY) @@ -136,7 +136,7 @@ testthat::test_that(desc = 'Row names are respected for ', code = { rownames(SPY) <- paste0("row", 1:nrow(SPY)) ## calculate indicator - indicator <- MIDPOINT(SPY) + indicator <- midpoint_period(SPY) testthat::expect_equal( object = paste0("row", 1:nrow(SPY)), @@ -144,13 +144,99 @@ testthat::test_that(desc = 'Row names are respected for ', code = { ) }) +## -method checks for +## and +## +## checks +testthat::test_that(desc = '-methods for ', code = { + ## check that midpoint_period can + ## use without any issues + output <- testthat::expect_no_error( + { + chart(BTC) + indicator(midpoint_period) + } + ) + + ## check that the output + ## is a -object + testthat::expect_true( + inherits(output, "plotly") + ) +}) + +## checks +testthat::test_that(desc = '-methods for ', code = { + ## check that midpoint_period can + ## use without any issues + output <- testthat::expect_no_error( + { + chart(SPY) + indicator(midpoint_period) + } + ) + + ## check that the output + ## is a -object + testthat::expect_true( + inherits(output, "plotly") + ) +}) + +## -method checks for +## and +## +## checks +testthat::test_that(desc = '-methods for ', code = { + testthat::skip_if_not_installed("ggplot2") + + ## check that midpoint_period can + ## use without any issues + output <- testthat::expect_no_error( + { + options(talib.chart.backend = "ggplot2") + on.exit(options(talib.chart.backend = "plotly")) + chart(BTC) + indicator(midpoint_period) + } + ) + + ## check that the output + ## is a -object or + testthat::expect_true( + inherits(output, "gg") || inherits(output, "talib_chart") + ) +}) + +## checks +testthat::test_that(desc = '-methods for ', code = { + testthat::skip_if_not_installed("ggplot2") + + ## check that midpoint_period can + ## use without any issues + output <- testthat::expect_no_error( + { + options(talib.chart.backend = "ggplot2") + on.exit(options(talib.chart.backend = "plotly")) + chart(SPY) + indicator(midpoint_period) + } + ) + + ## check that the output + ## is a -object or + testthat::expect_true( + inherits(output, "gg") || inherits(output, "talib_chart") + ) +}) + ## check that methods runs without ## issues and returns proper lengths testthat::test_that(desc = ' methods', code = { ## check that the method ## runs x <- testthat::expect_no_condition( - MIDPOINT(BTC[[1]]) + midpoint_period(BTC[[1]]) ) target_length <- length(BTC[[1]]) diff --git a/tests/testthat/test-ta_MIDPRICE.R b/tests/testthat/test-ta_MIDPRICE.R index 7c78aa970..336da6fe2 100644 --- a/tests/testthat/test-ta_MIDPRICE.R +++ b/tests/testthat/test-ta_MIDPRICE.R @@ -143,3 +143,89 @@ testthat::test_that(desc = 'Row names are respected for ', code = { expected = rownames(indicator) ) }) + +## -method checks for +## and +## +## checks +testthat::test_that(desc = '-methods for ', code = { + ## check that midpoint_price can + ## use without any issues + output <- testthat::expect_no_error( + { + chart(BTC) + indicator(midpoint_price) + } + ) + + ## check that the output + ## is a -object + testthat::expect_true( + inherits(output, "plotly") + ) +}) + +## checks +testthat::test_that(desc = '-methods for ', code = { + ## check that midpoint_price can + ## use without any issues + output <- testthat::expect_no_error( + { + chart(SPY) + indicator(midpoint_price) + } + ) + + ## check that the output + ## is a -object + testthat::expect_true( + inherits(output, "plotly") + ) +}) + +## -method checks for +## and +## +## checks +testthat::test_that(desc = '-methods for ', code = { + testthat::skip_if_not_installed("ggplot2") + + ## check that midpoint_price can + ## use without any issues + output <- testthat::expect_no_error( + { + options(talib.chart.backend = "ggplot2") + on.exit(options(talib.chart.backend = "plotly")) + chart(BTC) + indicator(midpoint_price) + } + ) + + ## check that the output + ## is a -object or + testthat::expect_true( + inherits(output, "gg") || inherits(output, "talib_chart") + ) +}) + +## checks +testthat::test_that(desc = '-methods for ', code = { + testthat::skip_if_not_installed("ggplot2") + + ## check that midpoint_price can + ## use without any issues + output <- testthat::expect_no_error( + { + options(talib.chart.backend = "ggplot2") + on.exit(options(talib.chart.backend = "plotly")) + chart(SPY) + indicator(midpoint_price) + } + ) + + ## check that the output + ## is a -object or + testthat::expect_true( + inherits(output, "gg") || inherits(output, "talib_chart") + ) +}) diff --git a/tests/testthat/test-ta_TSF.R b/tests/testthat/test-ta_TSF.R deleted file mode 100644 index a15ae6fe9..000000000 --- a/tests/testthat/test-ta_TSF.R +++ /dev/null @@ -1,45 +0,0 @@ -## autogenerated unit-test -## from codegen/ -## the file will be overwritten in the next iteration -## -## author: Serkan Korkmaz - -## test that the function runs without -## any conditions -testthat::test_that(desc = 'Runs without *any* conditions', code = { - output <- testthat::expect_no_condition( - { - TSF( - x = SPY[, 1] - ) - } - ) -}) - -## test that the length of the input -## matches the output -testthat::test_that(desc = 'Length in, length out', code = { - testthat::expect_equal( - object = length( - TSF( - x = SPY[, 1] - ) - ), - expected = length(SPY[, 1]) - ) -}) - -## test that the output is a vector -testthat::test_that(desc = 'Output type', code = { - output <- TSF( - x = SPY[, 1] - ) - - testthat::expect_true( - typeof(output) == "double" || typeof(output) == "integer" - ) - - testthat::expect_true( - is.null(dim(output)) - ) -}) diff --git a/vignettes/articles/tidyverse.Rmd b/vignettes/articles/tidyverse.Rmd deleted file mode 100644 index 7fbe52085..000000000 --- a/vignettes/articles/tidyverse.Rmd +++ /dev/null @@ -1,184 +0,0 @@ ---- -title: "Tidyverse Workflows" -author: Serkan Korkmaz ---- - -```{r, include = FALSE} -options(talib.chart.dark = FALSE) - -knitr::opts_chunk$set( - collapse = TRUE, - comment = "#>", - out.width = "100%", - fig.align = "center" -) -``` - -`{talib}` indicators accept a `data.frame` and return a `data.frame`---but the returned data frame contains **only the indicator columns**, not the original data. This is by design: it keeps the core API minimal and composable. In a tidyverse pipeline, however, you usually want the indicator columns attached to your existing data so you can keep piping. - -This article builds a thin wrapper called `tidy_ta()` that bridges that gap, then puts it to work in increasingly realistic scenarios. - -```{r} -library(talib) -library(dplyr) -library(tidyr) -``` - -## The gap - -Piping into a `{talib}` indicator works---`x` is the first argument: - -```{r} -BTC %>% - RSI(n = 14) %>% - tail() -``` - -The result is a one-column data frame with just the RSI values. The original price data is gone. To keep both, you need to bind the indicator output back to the input. - -## Building `tidy_ta()` - -The simplest version takes a data frame, passes it to an indicator, and column-binds the result: - -```{r} -tidy_ta <- function(.data, .f, ...) { - dplyr::bind_cols(.data, .f(.data, ...)) -} -``` - -Three lines, and every indicator in `{talib}` is now pipe-friendly: - -```{r} -BTC %>% - tidy_ta(RSI, n = 14) %>% - tail() -``` - -Multi-column indicators work the same way---Bollinger Bands returns three columns, and all three get bound: - -```{r} -BTC %>% - tidy_ta(bollinger_bands) %>% - tail() -``` - -Chaining multiple indicators composes naturally: - -```{r} -BTC %>% - tidy_ta(RSI, n = 14) %>% - tidy_ta(bollinger_bands) %>% - tidy_ta(MACD) %>% - tail() -``` - -### Handling column-name collisions - -If you add two SMAs with different periods, both return a column named `SMA` and `bind_cols()` disambiguates with ugly suffixes like `SMA...6`. A `.suffix` parameter fixes this: - -```{r} -tidy_ta <- function(.data, .f, ..., .suffix = NULL) { - result <- .f(.data, ...) - - if (!is.null(.suffix)) { - colnames(result) <- paste(colnames(result), .suffix, sep = "_") - } - - dplyr::bind_cols(.data, result) -} -``` - -Now each indicator gets a clear name: - -```{r} -BTC %>% - tidy_ta(SMA, n = 10, .suffix = "10") %>% - tidy_ta(SMA, n = 20, .suffix = "20") %>% - tail() -``` - -The `cols` argument is forwarded through `...`, so column remapping still works: - -```{r} -BTC %>% - tidy_ta(RSI, cols = ~high, n = 14) %>% - tail() -``` - -This is the complete wrapper. The rest of the article uses it as-is. - -## Grouped operations across assets - -A common task is computing the same indicator across multiple tickers. Stack the data, `nest()` by ticker, apply `tidy_ta()` inside each group, and `unnest()`: - -```{r} -assets <- bind_rows( - BTC %>% as_tibble(rownames = "date") %>% mutate(ticker = "BTC"), - SPY %>% as_tibble(rownames = "date") %>% mutate(ticker = "SPY"), - NVDA %>% as_tibble(rownames = "date") %>% mutate(ticker = "NVDA") -) - -assets %>% - nest(.by = ticker) %>% - mutate(data = lapply(data, tidy_ta, RSI, n = 14)) %>% - unnest(data) %>% - select(ticker, date, close, RSI) %>% - filter(!is.na(RSI)) %>% - slice_tail(n = 3, by = ticker) -``` - -Because `tidy_ta()` returns the full enriched data frame, `unnest()` restores everything in one step. This scales to multiple indicators by chaining inside the `lapply()`: - -```{r} -assets %>% - nest(.by = ticker) %>% - mutate(data = lapply(data, function(d) { - d %>% - tidy_ta(RSI, n = 14) %>% - tidy_ta(bollinger_bands) - })) %>% - unnest(data) %>% - select(ticker, date, close, RSI, UpperBand, MiddleBand, LowerBand) %>% - filter(!is.na(RSI)) %>% - slice_tail(n = 3, by = ticker) -``` - -## Putting it all together - -A complete pipeline: enrich a multi-asset dataset, flag RSI signals, and find the most recent event per asset. - -```{r} -assets %>% - nest(.by = ticker) %>% - mutate(data = lapply(data, tidy_ta, RSI, n = 14)) %>% - unnest(data) %>% - filter(!is.na(RSI)) %>% - mutate( - signal = case_when( - RSI > 70 ~ "overbought", - RSI < 30 ~ "oversold" - ) - ) %>% - filter(!is.na(signal)) %>% - slice_tail(n = 1, by = c(ticker, signal)) %>% - select(ticker, date, close, RSI, signal) %>% - arrange(ticker, signal) -``` - -## Summary - -The entire wrapper is six lines: - -```r -tidy_ta <- function(.data, .f, ..., .suffix = NULL) { - result <- .f(.data, ...) - if (!is.null(.suffix)) { - colnames(result) <- paste(colnames(result), .suffix, sep = "_") - } - dplyr::bind_cols(.data, result) -} -``` - -It works because `{talib}` indicators already follow the key convention: data frame in, data frame out, with row counts and row names preserved. `tidy_ta()` just bridges the last mile---binding the result back to the input so the pipeline keeps flowing. - -The pattern is not specific to `{talib}`. Any function that takes a data frame and returns a same-length data frame can be wrapped the same way. diff --git a/vignettes/candlestick.Rmd b/vignettes/candlestick.Rmd index 0e4c630c1..62ea19932 100644 --- a/vignettes/candlestick.Rmd +++ b/vignettes/candlestick.Rmd @@ -184,13 +184,13 @@ table(talib::engulfing(talib::BTC)) options(talib.normalize = TRUE) ``` -### The `eps` parameter +### The `penetration` parameter -Seven patterns accept an `eps` (penetration) parameter that controls how far one candle must intrude into the body of another. These are `morning_star()`, `evening_star()`, `morning_doji_star()`, `evening_doji_star()`, `abandoned_baby()`, `dark_cloud_cover()`, and `mat_hold()`. The default is `eps = 0` for all of them. +Seven patterns accept an `penetration` (penetration) parameter that controls how far one candle must intrude into the body of another. These are `morning_star()`, `evening_star()`, `morning_doji_star()`, `evening_doji_star()`, `abandoned_baby()`, `dark_cloud_cover()`, and `mat_hold()`. The default is `penetration = 0` for all of them. ```{r} ## Evening Star with 30% penetration -x <- talib::evening_star(talib::BTC, eps = 0.3) +x <- talib::evening_star(talib::BTC, penetration = 0.3) sum(abs(x), na.rm = TRUE) ``` diff --git a/vignettes/charting.Rmd b/vignettes/charting.Rmd index 28e771232..06d3aad2b 100644 --- a/vignettes/charting.Rmd +++ b/vignettes/charting.Rmd @@ -57,8 +57,8 @@ Use `indicator()` to attach technical indicators to the most recent `chart()`. I ```{r} { talib::chart(talib::BTC) - talib::indicator(talib::SMA, n = 7) - talib::indicator(talib::SMA, n = 14) + talib::indicator(talib::SMA, timePeriod = 7) + talib::indicator(talib::SMA, timePeriod = 14) talib::indicator(talib::RSI) } ``` @@ -82,9 +82,9 @@ By default, each sub-panel indicator (RSI, MACD, etc.) gets its own panel. To me { talib::chart(talib::BTC) talib::indicator( - talib::RSI(n = 10), - talib::RSI(n = 14), - talib::RSI(n = 21) + talib::RSI(timePeriod = 10), + talib::RSI(timePeriod = 14), + talib::RSI(timePeriod = 21) ) } ``` @@ -95,7 +95,7 @@ Each indicator keeps its own arguments and receives a distinct color from the ac { talib::chart(talib::BTC) talib::indicator( - talib::RSI(n = 14), + talib::RSI(timePeriod = 14), talib::MACD() ) } @@ -108,15 +108,15 @@ Combined panels and regular panels can be freely mixed in the same chart: talib::chart(talib::BTC) talib::indicator(talib::BBANDS) talib::indicator( - talib::RSI(n = 10), - talib::RSI(n = 14), - talib::RSI(n = 21) + talib::RSI(timePeriod = 10), + talib::RSI(timePeriod = 14), + talib::RSI(timePeriod = 21) ) talib::indicator(talib::MACD) } ``` -> **Note:** the syntax matters. `indicator(RSI, n = 14)` passes a function and arguments separately (single indicator). `indicator(RSI(n = 14), RSI(n = 21))` passes _calls_ (combined panel). A single call like `indicator(RSI(n = 14))` also works and is equivalent to the single-indicator form. +> **Note:** the syntax matters. `indicator(RSI, timePeriod = 14)` passes a function and arguments separately (single indicator). `indicator(RSI(timePeriod = 14), RSI(timePeriod = 21))` passes _calls_ (combined panel). A single call like `indicator(RSI(timePeriod = 14))` also works and is equivalent to the single-indicator form. ### Standalone indicators @@ -221,10 +221,10 @@ The default theme uses a dark background with cyan and blue candles. ```{r} { talib::chart(talib::BTC) - talib::indicator(talib::SMA, n = 7) - talib::indicator(talib::SMA, n = 14) - talib::indicator(talib::SMA, n = 21) - talib::indicator(talib::SMA, n = 28) + talib::indicator(talib::SMA, timePeriod = 7) + talib::indicator(talib::SMA, timePeriod = 14) + talib::indicator(talib::SMA, timePeriod = 21) + talib::indicator(talib::SMA, timePeriod = 28) talib::indicator(talib::MACD) talib::indicator(talib::trading_volume) } @@ -238,10 +238,10 @@ A light theme with neutral grays. { talib::set_theme$hawks_and_doves talib::chart(talib::BTC) - talib::indicator(talib::SMA, n = 7) - talib::indicator(talib::SMA, n = 14) - talib::indicator(talib::SMA, n = 21) - talib::indicator(talib::SMA, n = 28) + talib::indicator(talib::SMA, timePeriod = 7) + talib::indicator(talib::SMA, timePeriod = 14) + talib::indicator(talib::SMA, timePeriod = 21) + talib::indicator(talib::SMA, timePeriod = 28) talib::indicator(talib::MACD) talib::indicator(talib::trading_volume) } @@ -255,10 +255,10 @@ A dark theme with teal and orange accents. { talib::set_theme$payout talib::chart(talib::BTC) - talib::indicator(talib::SMA, n = 7) - talib::indicator(talib::SMA, n = 14) - talib::indicator(talib::SMA, n = 21) - talib::indicator(talib::SMA, n = 28) + talib::indicator(talib::SMA, timePeriod = 7) + talib::indicator(talib::SMA, timePeriod = 14) + talib::indicator(talib::SMA, timePeriod = 21) + talib::indicator(talib::SMA, timePeriod = 28) talib::indicator(talib::MACD) talib::indicator(talib::trading_volume) } @@ -272,10 +272,10 @@ A bright theme with teal and red candles on a light background. { talib::set_theme$tp_slapped talib::chart(talib::BTC) - talib::indicator(talib::SMA, n = 7) - talib::indicator(talib::SMA, n = 14) - talib::indicator(talib::SMA, n = 21) - talib::indicator(talib::SMA, n = 28) + talib::indicator(talib::SMA, timePeriod = 7) + talib::indicator(talib::SMA, timePeriod = 14) + talib::indicator(talib::SMA, timePeriod = 21) + talib::indicator(talib::SMA, timePeriod = 28) talib::indicator(talib::MACD) talib::indicator(talib::trading_volume) } @@ -289,10 +289,10 @@ A light, muted theme with earthy tones. { talib::set_theme$trust_the_process talib::chart(talib::BTC) - talib::indicator(talib::SMA, n = 7) - talib::indicator(talib::SMA, n = 14) - talib::indicator(talib::SMA, n = 21) - talib::indicator(talib::SMA, n = 28) + talib::indicator(talib::SMA, timePeriod = 7) + talib::indicator(talib::SMA, timePeriod = 14) + talib::indicator(talib::SMA, timePeriod = 21) + talib::indicator(talib::SMA, timePeriod = 28) talib::indicator(talib::MACD) talib::indicator(talib::trading_volume) } @@ -306,10 +306,10 @@ A dark theme inspired by the Bloomberg Terminal interface. Orange bullish candle { talib::set_theme$bloomberg_terminal talib::chart(talib::BTC) - talib::indicator(talib::SMA, n = 7) - talib::indicator(talib::SMA, n = 14) - talib::indicator(talib::SMA, n = 21) - talib::indicator(talib::SMA, n = 28) + talib::indicator(talib::SMA, timePeriod = 7) + talib::indicator(talib::SMA, timePeriod = 14) + talib::indicator(talib::SMA, timePeriod = 21) + talib::indicator(talib::SMA, timePeriod = 28) talib::indicator(talib::MACD) talib::indicator(talib::trading_volume) } @@ -323,10 +323,10 @@ A dark monochrome theme that encodes direction with luminance only. Light-gray b { talib::set_theme$limit_up talib::chart(talib::BTC) - talib::indicator(talib::SMA, n = 7) - talib::indicator(talib::SMA, n = 14) - talib::indicator(talib::SMA, n = 21) - talib::indicator(talib::SMA, n = 28) + talib::indicator(talib::SMA, timePeriod = 7) + talib::indicator(talib::SMA, timePeriod = 14) + talib::indicator(talib::SMA, timePeriod = 21) + talib::indicator(talib::SMA, timePeriod = 28) talib::indicator(talib::MACD) talib::indicator(talib::trading_volume) } @@ -340,10 +340,10 @@ A light theme with the classic blue-vs-red trading pair. Steel-blue bullish and { talib::set_theme$bid_n_ask talib::chart(talib::BTC) - talib::indicator(talib::SMA, n = 7) - talib::indicator(talib::SMA, n = 14) - talib::indicator(talib::SMA, n = 21) - talib::indicator(talib::SMA, n = 28) + talib::indicator(talib::SMA, timePeriod = 7) + talib::indicator(talib::SMA, timePeriod = 14) + talib::indicator(talib::SMA, timePeriod = 21) + talib::indicator(talib::SMA, timePeriod = 28) talib::indicator(talib::MACD) talib::indicator(talib::trading_volume) } @@ -416,7 +416,7 @@ Set `talib.chart.backend` to `"ggplot2"` for static charts. This requires the ** talib::set_theme$hawks_and_doves talib::chart(talib::BTC) - talib::indicator(talib::SMA, n = 14) + talib::indicator(talib::SMA, timePeriod = 14) talib::indicator(talib::RSI) } ``` diff --git a/vignettes/talib.Rmd b/vignettes/talib.Rmd index 0a1a8eb83..9de1503eb 100644 --- a/vignettes/talib.Rmd +++ b/vignettes/talib.Rmd @@ -16,7 +16,7 @@ knitr::opts_chunk$set( comment = "#>", out.width = "100%", out.height = "680", - fig.align = "center" + fig.aligtimePeriod = "center" ) ``` @@ -99,8 +99,8 @@ Every indicator has a descriptive snake_case name and an uppercase alias that mi ```{r} ## these are equivalent identical( - talib::relative_strength_index(talib::BTC, n = 14), - talib::RSI(talib::BTC, n = 14) + talib::relative_strength_index(talib::BTC, timePeriod = 14), + talib::RSI(talib::BTC, timePeriod = 14) ) ``` @@ -109,17 +109,17 @@ identical( Most indicators require a minimum number of observations before they can produce a value. This is called the **lookback period**. The first `lookback` rows of the result will be `NA`: ```{r} -## SMA with n = 5 has a lookback of 4 +## SMA with timePeriod = 5 has a lookback of 4 head( - talib::SMA(talib::BTC, n = 5), - n = 7 + talib::SMA(talib::BTC, timePeriod = 5), + timePeriod = 7 ) ``` The lookback is stored as an attribute on the result: ```{r} -x <- talib::SMA(talib::BTC, n = 20) +x <- talib::SMA(talib::BTC, timePeriod = 20) attr(x, "lookback") ``` @@ -209,7 +209,7 @@ Several indicators accept a **Moving Average specification** for their smoothing ```{r} ## SMA as a specification str( - talib::SMA(n = 20) + talib::SMA(timePeriod = 20) ) ``` @@ -220,7 +220,8 @@ This specification can be passed to indicators like `bollinger_bands()` or `stoc tail( talib::bollinger_bands( talib::BTC, - ma = talib::EMA(n = 20) + timePeriod = 20, + maType = talib::EMA() ) ) ``` @@ -230,8 +231,10 @@ tail( tail( talib::stochastic( talib::BTC, - slowk = talib::WMA(n = 5), - slowd = talib::EMA(n = 3) + slowKPeriod = 5, + slowKMa = talib::EMA(), + slowDPeriod = 5, + slowDMa = talib::EMA() ) ) ```