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 from sim_ps1(), Phillips & Shi
(2018)’s own single-bubble DGP: a unit-root run-up, a genuine explosive
regime (true origination at 40), a mildly-integrated collapse regime
(starting at 61) and a return to a unit root (recovery at 70) – exactly
the regime structure these estimators are built for.
y <- sim_ps1(n = 100, seed = 1)True origination is 40, true collapse is 61. Running all four:
dating_hls(y, trim = 0.05)
#>
#> ── dating_hls (n = 100, trim = 0.05) ───────────────────────────────────────────
#>
#> series model origination collapse recovery
#> series1 4 39 60 70
dating_knp(y, trim = 0.05)
#>
#> ── dating_knp (n = 100, trim = 0.05, omit = TRUE) ──────────────────────────────
#>
#> series origination collapse delta
#> series1 60 70 0.9178
dating_pdc(y, regimes = 3, trim = 0.05)
#> origination collapse
#> series1 38 59
dating_hlw(y, trim = 0.1, nboot = 199, seed = 1)
#>
#> ── dating_hlw (n = 100, trim = 0.1) ────────────────────────────────────────────
#>
#> series1:
#> model origination collapse recovery
#> 4 39 60 70On this draw, dating_hls() and dating_hlw()
both select the 4-regime model and land within a point of every true
date (39/60/70 against 40/61/70); dating_pdc() is a point
or two early on both (38/59). dating_knp() is the visible
failure mode here: its model assumes an instantaneous collapse
(unit root resuming from a shifted level), so sim_ps1()’s
ten-period mildly-integrated crash (61-70) is a regime it has no room
for, and it dates that crash as the episode instead, reporting 60/70 – a
real, not contrived, consequence of a model mismatch, not of the
estimator’s bias correction.
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()(addweightsfor 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().
