Skip to contents

Why these exist alongside datestamp()

datestamp() applies PSY’s own rule to a radf() result: a bubble runs from the first point the recursive statistic crosses its critical value to the first point it drops back below. That rule is simple and well understood, but it is not the only way to date a bubble once you already believe one is there. The dating_*() family instead fits an explicit regime model – unit-root, then explosive, then unit-root again – directly to the raw series by minimizing residual sum of squares (SSR), and picks the break dates that fit that model best. They take no critical value at all: given a window believed to contain (at most) one bubble, they answer “where exactly does it start and end,” not “is there one.”

Function Paper Idea
dating_hls() Harvey, Leybourne & Sollis (2017) Fits 4 candidate regime-dummy models (with/without a distinct collapse regime, with/without recovery) via closed-form segment SSR; BIC picks among them.
dating_knp() Kejriwal, Nguyen & Perron (2025) Same model as HLS’s Model 2, but proves the plain SSR minimizer is inconsistent – it converges to the collapse date, not the origination date – and fixes it by omitting one squared residual from the objective.
dating_pdc() Pang, Du & Chong (2021); Kurozumi & Skrobotov (2023) Assumes a fixed 3- or 4-regime structure and finds each breakpoint sequentially in closed form (collapse first, since it is stochastically dominant), with no BIC step.
dating_hlw() Harvey, Leybourne & Whitehouse (2020) A wrapper: runs radf()/datestamp() first to find how many episodes and roughly where, then applies HLS-style fitting independently within each detected window.

All four take no radf_cv, so they don’t plug into summary()/tidy()/ autoplot() – see vignette("naming-and-analysis") for the full pipeline picture. Each prints its own dating table instead.

A single bubble, four verdicts

One simulated series: a unit-root run-up, a genuine explosive regime (rho = 1.03, true origination at 51), then a volatile collapse back to a unit root (true collapse at 80).

set.seed(11)
n1 <- 50; n2 <- 30; n3 <- 20
expansion <- 100 * 1.03^(1:n2) + cumsum(rnorm(n2, sd = 1))
y <- c(cumsum(rnorm(n1)), expansion, expansion[n2] + cumsum(rnorm(n3, sd = 1.5)))

True origination is 51, true collapse is 80. Running all four:

dating_hls(y, trim = 0.05)
#> 
#> ── dating_hls (n = 100, trim = 0.05) ───────────────────────────────────────────
#> 
#>    series  model  origination  collapse  recovery
#>   series1      2           50        55      <NA>
dating_knp(y, trim = 0.05)
#> 
#> ── dating_knp (n = 100, trim = 0.05, omit = TRUE) ──────────────────────────────
#> 
#>    series  origination  collapse   delta
#>   series1           28        50  0.1126
dating_pdc(y, regimes = 3, trim = 0.05)
#>         origination collapse
#> series1          50       79
dating_hlw(y, trim = 0.1, nboot = 199, seed = 1)
#> 
#> ── dating_hlw (n = 100, trim = 0.1) ────────────────────────────────────────────
#> 
#> series1:
#>  model origination collapse recovery
#>      2          50       59     <NA>
#>      2          64       80     <NA>

On this draw, dating_pdc() lands closest to both true dates (50, 79). dating_hls() gets the origination right (50) but its BIC-selected model puts the collapse far too early (55) – a real, visible failure mode, not a contrived one. dating_hlw()’s preliminary datestamp() step splits the episode into two separate windows here, and its second window’s collapse (80) is exact even though the first window is spurious. dating_knp()’s origination (28) is the least accurate of the four on this particular draw.

The point of running all four side by side isn’t that one is “correct” – it’s that these are genuinely different estimators with different failure modes, and disagreement between them on real data is informative, not a bug to be resolved by picking a favorite.

Which to reach for

  • Believe there is exactly one bubble in the window and want the best-fitting regime model (with or without a distinct collapse/recovery regime): dating_hls().
  • Same setting, but origination-date accuracy matters more than collapse-date accuracy (KNP’s own finding: the naive estimator is biased toward the collapse date): dating_knp().
  • Want closed-form, no BIC model search, and are willing to fix the number of regimes up front: dating_pdc() (add weights for the volatility-corrected variant).
  • Don’t know how many episodes there are, or want dating anchored to an actual radf()/datestamp() detection first: dating_hlw().