exuber 2.0.0
New methodologies from the docs/ research programme, each independently validated against a published number (formula-exact check, table lookup, or a direct Monte Carlo reproduction of the source paper’s own theorem) — see docs/README.md for the full record of what was checked and how.
Critical values
- Default critical values now come from a shared precomputed store instead of the bundled
radf_critdataset: lag 0-4, everynfrom the smallest the PSY window allows up to 4000, 2000 replications each.radf()followed bysummary()/datestamp()/autoplot()fetches the one(n, lag)table it needs on first use and caches it on disk (tools::R_user_dir("exuber", "cache")), so a lagged specification no longer requires simulating your owncv. The tables are nested (everynof a lag from the same seeded paths); values differ from the old bundled ones by Monte Carlo noise. Needs network access the first time a given(n, lag)is used;radf_mc_cv()/radf_wb_cv()remain the offline route. -
radf_crit(the bundled lag-0, n <= 600 table) is removed, along with itsprint.critmethod anddata-raw/sim-crit.R; the simulation now lives in the siblingexubercritrepository.
Volatility-robust tests
-
radf_sbz()/radf_sbz_cv()/radf_sbz_union()— Herwartz & Siedenburg’s WLS/kernel-volatility SBZ test, split into a statistic (radf_sbz()), its bootstrap critical values (radf_sbz_cv(), fulldatestamp()/autoplot()support), and the union-of-rejections test against classicsupDF(radf_sbz_union(), renamed from the original bundledradf_sbz_cv()— seevignette("volatility-robust-radf"). -
radf_kp()— kernel-purge heteroskedasticity test. -
radf_wb_cv(..., dist_skew = TRUE)— Hafner (2020) skewness-corrected wild bootstrap. -
radf_sign()/radf_sign_cv()— Harvey, Leybourne & Zu (2020) sign-based sGSADF, invariant to volatility with no bootstrap needed. -
ssu_test()— Kurozumi & Nishi (2025) stochastic explosive-coefficient test (minimum-viable subset). -
datestamp(..., option = "svadf")— Sarkar & Wells (2026) SV-ADF asymmetric-threshold dating, folded intodatestamp()rather than shipped as a separateradf_svadf()entry point. Caveat: the source is a non-peer-reviewed preprint, flagged at call time and in?datestamp, Caveats section.
Dating and root inference
-
dating_pdc()— PDC/KS sequential sample-splitting dating, plustype = "wls"for Kurozumi & Skrobotov (2023)’s time-varying-volatility correction. -
radf_recovery()/radf_recovery_cv()— Phillips & Shi (2014) reverse-regression crisis-origination/recovery dating. Caveat:f_cand the overall false-detection rate are exploratory pending further validation, flagged at call time and in?radf_recovery. -
dating_hls()— Harvey, Leybourne & Sollis (2017) SSR/BIC single-bubble dating. -
dating_hlw()— Harvey, Leybourne & Whitehouse (2020) SSR/BIC multi-bubble two-step wrapper. -
dating_knp()— Kejriwal, Nguyen & Perron (2025) bias-corrected dating.
Real-time monitoring
-
monitor()— Phillips & Shi (2020) training/monitoring orchestration (Family A), plus Kurozumi (2020) closed-formSADF/GSADF_s0boundaries and Homm & Breitung (2012)’s FLUC boundary. -
monitor_cusum()— Homm & Breitung (2012) CUSUM monitoring, plus Astill et al. (2023)’s volatility-robust CUSUMV kernel variant and HB’s finite-sample boundary. -
lbi_test()/monitor_lbi()— Breitung & Diegel (2025) static LBI test and its sequential mCUSUM/wCUSUM extension.
Multivariate / panel tests
-
radf_common()/radf_common_cv()— Chen, Phillips & Shi common-bubble detection (PCA + PSY). -
cobubble_test()— Evripidou, Harvey, Leybourne & Sollis (2022) co-explosive test. -
contagion_reg()— Greenaway-McGrevy & Phillips (2016) bubble contagion regression (minimum-viable subset).
Alternative paradigms
-
quantile_test()— Wu, Shi & Wu (2025) quantile-based global test. -
monitor_quantile()— Wu, Shi & Wu (2025) QPWY recursive quantile monitoring.
Naming
- 12 of the functions above (
cobubble_test,contagion_reg,monitor_cusum,dating_hls,dating_hlw,dating_knp,lbi_test,monitor_lbi,dating_pdc,monitor_quantile,quantile_test,ssu_test) were namedradf_*in earlier development snapshots of this unreleased version; renamed before release since none of them are actually recursive-ADF-based tests. No deprecated aliases were kept, as the old names never shipped in a CRAN release. -
radf_monitor()renamed tomonitor()(via a brief intermediatemonitor_radf(), never released), the flagship of the real-time monitoring family alongsidemonitor_cusum()/monitor_lbi()/monitor_quantile()— deliberately carrying noradf/sadftoken so it can’t be mistaken for aradf_*()variant, ADF-family internals notwithstanding — seevignette("naming-and-analysis"). -
exuber_functions()added: a queryable registry of every exported function’s family (adf,test,dating,monitor,root,regression), so “what monitoring functions exist” is an actual function call, not a naming convention to memorize. -
radf_wb_cv2()/radf_wb_distr2()renamed toradf_wb_ps_cv()/radf_wb_ps_distr()– the2suffix named nothing (just “the second wild bootstrap added”);_psidentifies it as Phillips & Shi (2020)’s wild bootstrap (fits a null AR model, resamples residuals, supports atbtraining-window boundary), as opposed toradf_wb_cv()’s Harvey et al. (2016) non-parametric multiplier bootstrap, matching this file’s own internal naming (radf_wb_dgp_ps/radf_wb_psvs.radf_wb_dgp_hlst/radf_wb_hlst) and the package-wideradf_<method>_<qualifier>_cvpattern (radf_sign_dm_cv()). Unlike the renames above,radf_wb_cv2()shipped in the 1.0.0 release (the JSS paper), so this one keepsradf_wb_cv2()/radf_wb_distr2()as deprecated aliases (.Deprecated(), warn-and-forward, see?exuber-deprecated) rather than a clean break. -
radf_tt_cv(),radf_sign_cv(), andradf_sign_dm_cv()now all computebadf_cv/bsadf_cv(a time-varying boundary), not just the three scalar critical values —radf_tt()/radf_sign()/radf_sign_dm()results now work with the fullsummary()/datestamp()/tidy()/autoplot()pipeline, not justsummary()/tidy(). Found first inradf_tt_cv()(a user-reporteddatestamp()crash onradf_sign()prompted checking all three GLS-demeaned-family functions), then confirmed the identical fix applies to the other two. Validated per function:badf_cv‘s last row is bit-identical toadf_cv(a hard identity), empirical false-alarm rate at or below nominal (radf_tt3.3%,radf_sign5.5%,radf_sign_dm3.5%, vs. 5% nominal), and detection power on an identical synthetic bubble in the same range as the establishedradf()/radf_mc_cv()baseline (16%):radf_tt18%,radf_sign20%,radf_sign_dm8% (lower power a known, expected property of the sign-based tests’ heteroskedasticity invariance, not a validation concern) — seevignette("naming-and-analysis").
Performance
-
radf()is ~25x faster on typical sample sizes (exubercore v0.3.1): the recursive grid re-formed the full residual vector for every window, an O(n^3) total; it now keeps running cross-products and uses the closed-formSSR = y'y - b'X'y, making it O(n^2). n = 400 drops from ~140 ms to ~6 ms per path, soradf_mc_cv(),radf_wb_cv(),radf_sb_cv(),monitor(),dating_hlw(),radf_recovery()and every other loop overradf()speed up by the same factor. The regression is now parametrised asdyon(1, y_{t-1}, dy lags)(same t-statistic, onbeta - 1), which is also more accurate than before at largenwith large levels: ~3e-11 againstlm()at n = 2000 where the old recursion was ~2e-8. - Parallel runs (
options(exuber.parallel = TRUE)) reuse one worker cluster per session instead of starting and stopping a freshfuture::multisessionon every call – a few seconds of start-up overhead that used to dominate every smallradf_mc_cv()/radf_wb_cv()job. The cluster is sized byexuber.ncoresand stopped when the namespace unloads.exuber.parallelnow defaults tointeractive(): scripts, knitr andR CMD checkrun serially unless they opt in, so a batch job no longer pays worker start-up (nor leaves worker connections open) for a handful of replications. -
radf()’s panel statistic usedapply(bsadf, 1, mean), whose per-call dispatch overhead scales with the number of rows rather than staying fixed – 65x slower than the equivalentrowMeans(bsadf)at n = 100, 289x slower at n = 1000, and the single largest piece ofradf()’s runtime at every sample size tested. Replaced withrowMeans(bsadf)(identical result,tests/testthatunchanged, 879 passing).
API consistency
- One significance-level convention across the whole package: every function that took a
levelargument now takessig_lvlon the 0-100 scale already used bydatestamp()/autoplot()(sig_lvl = 95= a 5% test / 95% confidence). Affected (all unreleased, so no shims):lbi_test(),monitor_lbi()(0.95->95,0.975->97.5, …),ssu_test(),monitor(),monitor_cusum(),rootstamp()(was a0.95-style confidence level),quantile_test()/monitor_quantile()(already 0-100, renamed only), andcobubble_test()(was a size,level = 0.05; nowsig_lvl = 95). A sharedassert_sig_lvl()makessig_lvl = 0.95an immediate error everywhere rather than a silently wrong quantile. -
monitor(adflag = )->lag, matchingradf();monitor_cusum(N = )->h, matching every other kernel-bandwidth argument;cobubble_test(lags = )->lag_grid, solag/lagscan no longer be confused (mirrorsquantile_test()’stau/tau_grid). -
cobubble_test()andradf_sbz_union()now returncobubble_test_obj/radf_sbz_union_obj, the_objsuffix every other standalone class already carried. -
dating_pdc()andradf_sbz()gained their ownprint()methods (the former fell through toprint.data.frame, hiding its attributes; the latter printed as a plainradf). -
scale_exuber_manual(size_values = )is deprecated in favor oflinewidth_values(ggplot2 >= 3.4.0’slinewidthaesthetic replacessizefor lines; the deprecation warning ggplot2 emitted from everyautoplot()is gone).autoplot(include_negative = ), deprecated since 1.0.0, is now actually forwarded tononrejectedinstead of ignored. -
radf_tt()/radf_tt_cv()/monitor_quantile()carry the same experimental badge as the other new methods; the “does not plug intoautoplot” note on every standalone function was wrong (each has had its ownautoplot()method) and now says so.
Other
rootstamp()— confidence interval and doubling time on the explosive root, via S3 dispatch: the default method fits a single sub-sample, theradf_objmethod runs everydatestamp()episode at once (previously three separate functions –explosive_root(),root_ci(),root_ci_datestamp()– consolidated before release).Documentation: every
autoplot()andaugment()method now has its own help page instead of sharing one with the function it plots/tidies (?autoplot.monitor_cusum_obj,?augment.radf_obj, …). The pkgdown reference index is reorganized into per-function subsections so each function is listed next to the methods that consume its output.sim_vol_break()– i.i.d. Gaussian innovations whose standard deviation shifts permanently at a chosen break fraction, the non-stationary volatility DGP (Cavaliere & Taylor 2007) the volatility-robust tests are actually built for, unlike stationary GARCH.sim_ps1()gained the sameeinnovation-injection argumentsim_psy1()already had, and thec/c1/c2arguments ofsim_psy1()/sim_ps1()now accept any positive scalar (as documented), so a fixed explosive root is expressible directly (c = 0.04, alpha = 0).Examples and vignettes now demonstrate each method on the DGP it targets rather than on
sim_datathroughout: volatility-robust tests (radf_tt(),radf_kp(),radf_sign(),radf_sbz(),radf_wb_cv(),monitor_cusum(type = "kernel"),dating_pdc(type = "wls")) on a volatility break,cobubble_test()/contagion_reg()onsim_coexplosive(),radf_common()onsim_common(),ssu_test()on a stochastic root,quantile_test()/monitor_quantile()on heavy-tailed innovations,dating_*()/radf_recovery()onsim_ps1(), and every monitor on a bubble that starts after its training window. Hand-rolledcumsum(rnorm())DGPs in the vignettes are replaced by the package’s own generators.
Bug fixes
radf_sb_cv()/radf_sb_distr()panel critical values were wrong in every release since 0.1.0. The bootstrap loop overwrote the per-series BSADF path instead of summing it, so the panel null distribution was the last series’ BSADF divided by the number of series rather than the cross-sectional mean. The error is invisible for ~5-series panels (the two quantities happen to coincide) but the test was oversized for narrow panels (8% at nominal 5% for 2 series) and had no power for wide ones (0% rejection under H0 and under the alternative for 10 series). Fixed; empirical size is now 6% / 5% / 2.5% for 2 / 5 / 10 series. A regression test pins the identity that a panel of identical copies has the same bootstrap distribution as the single series.datestamp()/autoplot()/autoplot2()’ssig_lvlargument now actually controls whether a series counts as rejecting the null. Previously,diagnostics.radf_obj()(used internally to decide which series get dated/plotted at all) hard-coded the 95% critical value for that decision regardless ofsig_lvl, so e.g.datestamp(x, cv, sig_lvl = 90)could throw"Cannot reject H0 at the 5% significance level"for a series that clearly rejects at the 10% level the caller asked for –sig_lvlonly ever reached the within-series episode threshold curve, never the series-eligibility gate.diagnostics()gained asig_lvlargument (default 95, so default-call behavior is unchanged) anddatestamp()/autoplot()/autoplot2()now thread their ownsig_lvlthrough to it.radf()(and everything built on it) errored withsubscript out of boundson a named numeric vector, e.g. aprcomp()score column – which is exactly whatradf_common()feeds it – because the names leaked into the internal NA-edge bookkeeping.The default-
cvrange check said the store coversn <= 5000; it coversn <= 4000andlag <= 4, and now says so before trying the network.sim_psy1()’sseedargument now also covers a generator passed lazily toe/coef_noise(e.g.sim_psy1(n, seed = 1, e = sim_vol_break(n - 1))); previously those were forced before the seed was set.
exuber 1.1.0
CRAN release: 2025-08-31
- Fixed targets not in the package itself nor in the base packages to use package anchors, i.e., use
exuber 1.0.1
CRAN release: 2023-02-12
Maintenance release to accommodate breaking changes in dplyr 1.1.0.
exuber 1.0.0
CRAN release: 2022-08-19
This first major release accompanies the publication of an article in the Journal of Statistical Software:
Vasilopoulos, K., Pavlidis, E., & Martínez-García, E. (2022). exuber: Recursive Right-Tailed Unit Root Testing with R. Journal of Statistical Software, 103(1), 1–26. https://doi.org/10.18637/jss.v103.i10
augment method for radf_obj and radf_cv
New arg
truncFixed inconsistencies among functions.
Now radf stores the data that are later can be accessed with
mat+-
Advanced features on datestamping: New columns that indicate:
- Signal
- Peak
- Ongoing
- Nonrejected
New datestamping procedure
rev_radfetc.New bootstrap procedure
radf_wb_cv2andradf_wb_distr2New coloring convention for plotting
dsandobjclasses
exuber 0.4.2
CRAN release: 2020-12-18
- Include printing methods for
radf_objandradf_cv. - Removed unused class definitions.
- Using
progresspackage for progress_bar.
exuber 0.4.0
CRAN release: 2020-05-04
Design
We have the following design in mind for future scalability. If you want make inference about radf models, then the estimation can be achieved with radf() function and return an object of class radf_obj, and the critical values can be achieved with radf_*_cv() and return an object of class radf_cv.
Breaking changes
-
autoplot()forradfmodels has been refactored and new features have been added for more flexibility and conformity with the {ggplot} mindset. - Because of the change in
autoplot,ggarrange()is now defunct. -
fortify()methods have been replaced bytidy(),augment(),tidy_join()andglance_join()methods.fortify()methods are now defunct. - Also
glance()is now defunct. The user can usetidy()withpanel=TRUEinstead. - Changed the names of:
-
mc_cv()toradf_mc_cv().mc_cv()is now deprecated. -
mc_distr()toradf_mc_distr().mc_distr()is now deprecated. -
wb_cv()toradf_wb_cv().wb_cv()is now deprecated. -
wb_distr()toradf_wb_distr().wb_distr()is now deprecated. -
sb_cv()toradf_sb_cv().sb_cv()is now deprecated. -
sb_distr()toradf_sb_distr().sb_distr()is now deprecated. -
critdataset toradf_crit. -
col_names()toseries_names().col_names()is now deprecated.
-
exuberdata
- We created a new package called
exuberdatathat accommodates critical values for up to 2000 observations. Critical values can be examined withexuberdata::radf_crit2. The package is created throughdratR archive Template, and can be easily installed withinstall.packages('exuberdata', repos = 'https://kvasilopoulos.github.io/drat/', type = 'source')or throughinstall_exuberdatawrapper function that is provided inexuber.
exuber 0.3.0
CRAN release: 2019-07-15
Breaking changes
- Changed
opt_bsadf = conservativefor the simulated critical values (crit), also reduced the size of thecritfrom 700 to 600 due to package size restrictions. -
sim_dgp1()andsim_dgp2()have been renamed tosim_psy1()andsim_psy2()to better describe the origination of the dgp. -
sim_dgp1()andsim_dgp2()have been soft-deprecated. -
autoplot_radf()arranges automatically multiple graphs, to return to previous behavior we included the optional argumentarrangewhich is set to TRUE by default.
Three new functions have been added to simulate empirical distributions for:
-
mc_dist(): Monte Carlo -
wb_dist(): Wild Bootstrap -
sb_dist(): Sieve Bootstrap
and a function that can calculate the p-values calc_pvalue() given the above distributions as argument.
Also methods tidy() and autoplot() have been added to turn the object into a tidy tibble and draw a particular plot with ggplot2, respectively.
New features
-
tidy()methods for objects of classradf,cv. -
augment()methods for objects of classradfandcv. -
augment_join()to combine objectradfandcvinto a single data.frame. -
glance()method for objects of classradf.
Improvements
- New printing output for the functions
summary(),diagnostics()anddatestamp(). - New improved progressbar with more succinct printing for
wb_cv() -
seedargument to functions that are using rng. Also the option to declare a global seed for reproducibility with theoption(exuber.global_seed = ###)
exuber 0.2.1
CRAN release: 2019-03-01
- Changed DESCRIPTION to include
sb_cvreference. - Renamed boolean to dummy from
datestampanddiagnostics. -
datestampdummy is now an attribute.
exuber 0.2.0
CRAN release: 2019-02-04
Options
Some of the arguments in the functions were included as options, you can set the package options with e.g. options(exuber.show_progress = TRUE).
-
paralleloption boolean, allows for parallel in critical values computation. -
ncoresoption numeric, sets the number of cores, defaults to max - 1. -
show_progressoption boolean, allows you to disable the progress bar, defaults to TRUE.
New features
- Panel estimation in
radf() - Added
sb_cv()function: Panel Sieve Bootstrapped critical values - Default critical values are supplied directly into
summary(),diagnostics,datestamp()andautoplot(), without having to specify argument cv. The critical values have been simulated frommc_cv()function and stored as data. Custom critical values should be provided by the user with the optioncv. - Added
ggarrange()function, that can arrange a list of ggplot objects into a single grob. - Added
fortifyto arrange a data.frame fromradf()function.
