*! xtflucbreak 1.0.0 07aug2026

Title

xtflucbreak -- Fluctuation test for a structural change at an unknown
date in heterogeneous panel data models, with or without common correlated effects

Syntax

xtflucbreak [depvar indepvars] [if] [in]
[, options]

With no varlist the command runs as postestimation
and reads the model from the estimation results in memory.

Model
cce filter out unobserved common factors with cross-section averages
(Li-Xiao-Chen section 4); the default is the section-3 branch

nocceconstant omit the constant column from the CCE projection, reproducing
the literal Mw printed on p.1190

ccalags(#) augment the cross-section averages with # lags; default
ccalags(0)

noconstant do not include an intercept among the tested coefficients

Test
level(#) overall significance level in percent; default level(5)

trimming(#) fraction of T trimmed at each end of the search grid;
default trimming(0.10)

asymptotic decide on the literal LXC statistic instead of the
finite-sample standardised one (over-rejects; see Options)

nosigmascale drop the 1/sigmahati scaling, reproducing the literal
section-4 display

cholesky use the Cholesky factor of Qhati instead of the symmetric
square root

Benchmarks
compare also report the Wald 1, Wald 2 and CUSUM tests of Antoch et al.
(2018) with wild-bootstrap critical values

reps(#) bootstrap replications for compare; default reps(1000)

seed(#) random-number seed for the bootstrap

Reporting
graph draw the fluctuation paths, the change-point profile, the panel-level
shifts and (with compare) the benchmark processes

flucname(name) name for the fluctuation-path graph; default xtfb_fluc

breakname(name) name for the change-point graph; default xtfb_break

unitname(name) name for the panel-shift graph; default xtfb_units

compname(name) name for the benchmark graph; default xtfb_compare

showunits list the per-panel shift norms

listunits(#) how many panels to list; default listunits(10)

nowarnings suppress the sample-size / assumption diagnostics

The data must be xtset and balanced on the estimation sample.

Description

xtflucbreak implements the fluctuation test of Li, Xiao and Chen (2024) for a
structural change at an unknown date in the heterogeneous panel regression

yit = xit'({&beta}i + {&delta}i·1{t > k0}) + eit,
i = 1,...,N, t = 1,...,T,

where every panel has its own slope vector {&beta}i. The null is
H0: {&delta}i = 0 for all i; the alternative allows a break in a fraction
c > 0 of the panels.

The idea (Chu and White 1992; Ploberger, Kraemer and Kontrus 1989) is that if the
coefficients are constant, the recursive estimator {&beta}hati(k) computed on the
first k observations should not wander far from the full-sample {&beta}hati.
The command aggregates that wandering across panels,

S(k) = N-1/2 {&Sigma}i (1/sigmahati)(k/{&radic}T)·Qhati1/2({&beta}hati(k) - {&beta}hati),

which converges to a K-dimensional Brownian bridge under H0. The null is rejected
when any component of maxk|S(k)| exceeds the Kolmogorov critical value at a
Sidak-adjusted level. The change point is estimated by the argmax of the same process.

Two branches are implemented in one command:

Section 3 (default) assumes no unobserved common factors: the panels are
cross-sectionally independent and OLS per panel is consistent.

Section 4 (cce) allows the regressors and the errors to load on
unobserved common factors. The factors are projected out with the cross-section averages
of (y, x) following Baltagi, Feng and Kao (2016), and the same statistic is computed on
the filtered data. This branch additionally needs {&radic}T/N {&rarr} 0.

xtflucbreak answers "is there a break, and when?". It does not estimate the
post-break slopes. For that, feed the estimated date to xtbfkbreak, which
implements the Baltagi-Feng-Kao estimator that this paper's CCE branch is built on.

Options

Model

cce switches to the section-4 branch. The T{&times}(K+1) matrix of cross-section
averages Wbar = (ybart, xbart') is formed and the annihilator
Mw = I - Wbar(Wbar'Wbar)-1Wbar' is applied to y and to X before anything else.
Use it whenever a cross-sectional dependence test such as xtcd2 rejects, or when
the regressors are plausibly driven by common shocks. Note that under cce the
intercept is absorbed by Mw and is not among the tested coefficients.

nocceconstant builds Mw from Wbar alone, exactly as printed in the paper.
The default adds a constant column, which is Pesaran's (2006) CCE augmentation and what
xtbfkbreak does, so that the two commands agree numerically. With a slope break
the augmented projection also spans the regime-split factor space, which the bare Wbar
does not. Specify this option only to reproduce the literal display.

ccalags(#) adds # lags of the cross-section averages to the projection, in the
spirit of Chudik and Pesaran's dynamic CCE. The first # periods are dropped, so T
falls by #. The paper uses no lags.

noconstant removes the intercept from xit. The paper's own Model 1 has
xit = (1, x1it)', i.e. the intercept is one of the K tested coefficients,
which is the default here. Under cce this option has no effect.

Test

level(#) sets the overall probability of a false alarm {&alpha}. The per-component
level is the Sidak correction {&alpha}* = 1 - (1-{&alpha})1/K, exactly as in the
paper's Remark 3.5. With {&alpha} = 0.05 and K = 2 this gives {&alpha}* = 0.025321 and a
critical value of 1.4781 (the paper prints 1.4782).

trimming(#) restricts the search to
#·T {&le} k {&le} (1-#)·T. The paper's displays say
max1<k<T with no trimming, but that cannot be what produced its published size
table: with the grid starting at k = K the recursive estimate is fitted on K observations
and the variance of S(k) there is roughly 105 times the Brownian-bridge value, so
the test rejects almost always under H0. Measured empirical size at the nominal 5%
level, LXC Model 1, iid errors, N = T = 50:

trimming(0) {&rarr} 0.674 trimming(0.05) {&rarr} 0.140
trimming(0.10) {&rarr} 0.050 trimming(0.15) {&rarr} 0.050

trimming(0.10) reproduces the paper's own Table 1 (0.050 iid, 0.070 unequal
variances, 0.062 GARCH against their 0.050 / 0.081 / 0.069), which is strong evidence that
this is what they did. It is therefore the default. The grid is additionally floored at the
first k for which Xi(k)'Xi(k) is invertible in every panel; the effective
range is printed in the header.

asymptotic makes the literal LXC statistic the decision rule. By default
xtflucbreak rescales each component of S(k) by
{&radic}(s(1-s) / Var[S(k)]), where Var[S(k)] is the exact conditional variance
(1/N){&Sigma}i(k2/T)·diag(Qhati1/2(Aik-1 -
AiT-1)Qhati1/2) rather than its asymptotic limit s(1-s). The two
coincide as (N,T) {&rarr} {&infin}, so the default is the paper's statistic
asymptotically; it differs only in finite samples, where the literal form is badly
oversized because E[Ak-1] = {&Sigma}-1/(k-K-1), not
{&Sigma}-1/k. The literal statistic and its p-value are printed in every table and
returned in r(stat_lxc) and r(p_lxc), so the paper's number is always
recoverable. Measured size at the nominal 5% level:

literal default

Model 1, N=T=50 0.050 0.038

Model 1, N=50 T=25 0.255 0.028

Model 2 (CCE), N=T=50 0.100 0.062 (LXC Table 6: 0.059)

Model 2, N=200 T=50 0.098 0.045

In the CCE branch the default also matches the paper's power table more closely
(0.870 against LXC Table 7's 0.870 for {&delta} = (0.2,0)', versus 0.912 for the literal
form). Use asymptotic only to reproduce a published number, never for inference on a
short panel.

nosigmascale drops the 1/sigmahati factor. The paper's section-4 statistic as
typeset has no such factor, but Theorem 4.8 claims a standard Brownian bridge limit,
which cannot hold without it unless {&sigma}i {&equiv} 1. The default therefore keeps
the scaling in both branches. See help xtflucbreak methods.

cholesky replaces the symmetric square root of Qhati by its Cholesky factor.
Both satisfy AA' = Qhati and both give an asymptotically standard bridge, but the
components differ. With the symmetric root (the default, and the natural reading of
Qhat1/2) the value of maxj is invariant to the order of the regressors. With
the Cholesky factor it is not, but component 1 then involves only the first regressor,
component 2 only the first two, and so on, which can be easier to interpret.

Benchmarks

compare adds the three tests that Li, Xiao and Chen benchmark against in their
Tables 1-9: two Wald-type statistics and a CUSUM statistic from Antoch et al. (2018),
each with wild-bootstrap critical values. They are computed on the raw, untransformed
data with an intercept, which is exactly the configuration used in the paper's tables --
Antoch et al. have no CCE variant, and their loss of power under common factors is the
point of the comparison.

reps(#) and seed(#) control the wild bootstrap. Antoch et al. use B = 1000.

Reporting

graph draws up to four figures: the fluctuation paths |S(k)(j)| with the
critical band, the ||V(k)|| change-point profile, the panel-level shifts at khat, and
(with compare) the three benchmark detection processes.

showunits adds a table of ||{&delta}hati|| and sigmahati per panel.
The full vectors are always available in r(shift) and r(sigma).

Interpreting the output

Header block. Confirms which branch ran, how the CCE filter was built, which
square root was used, whether the sigma scaling is on, and the effective search grid
k = klo ... khi. It also prints T/N and {&radic}T/N, the two ratios the asymptotics
depend on.

Fluctuation statistic table. One row per component j of
Qhati1/2({&beta}hati(k) - {&beta}hati). The arrow marks the
component attaining the overall maximum.

Statistic is maxk|S(k)(j)| over the admissible grid, on the
finite-sample standardised scale (see asymptotic). The last column,
LXC as printed, is the same maximum on the paper's literal scale.

Crit. value is the same C1({&alpha}*) for every component -- it is a
per-component threshold, and the Sidak adjustment is what keeps the family-wise
error at {&alpha}.

p-value is the Kolmogorov tail probability of that component's statistic. The overall
p-value at the foot of the table is 1 - (1 - minjpj)K, the exact inverse
of the Sidak rule: it is below {&alpha} if and only if some component exceeds
C1({&alpha}*).

A component is not a regressor. Qhati1/2 rotates the coefficient
deviations, and the rotation differs across panels, so "component 2 rejected" does not mean
"the second regressor broke". Use the shift table (below) for statements about individual
coefficients, or cholesky for a partially triangular reading.

Change-point block. khat = argmaxk||V(k)||, where
V(k) = {&radic}(NT)·S(k). Because the model is
{&delta}i1{t > k0}, khat is the last pre-break period; the
regime switches at khat+1. Both dates are printed on the time variable's own scale.
No confidence interval is given: the paper proves consistency (Theorem 3.8/4.12) but
explicitly leaves the limiting distribution of khat to future research.

Panel shift block. For each component, the mean, standard deviation and
percentage positive of
{&delta}hati = {&beta}hati(khat+1..T) - {&beta}hati(1..khat), an
unrestricted regime contrast in the original coefficient space. The sign-concordance
figure matters: S(k) aggregates signed deviations across i, so breaks running in
opposite directions cancel. A near 50/50 split means the test was working against itself,
and a non-rejection in that configuration is weak evidence.

Benchmark block (compare). Wald 1, Wald 2 and CUSUM with bootstrap
critical values and p-values. The three statistics live on very different scales by
construction (different weighting matrices Ci,t); compare each to its own critical
value, never to each other.

Diagnostics. Warnings fire when T < 50 (below the smallest T in the paper's
Monte Carlo), when {&radic}T/N is not small under cce (Theorem 4.8's rate condition),
when N < 20 (the aggregation over i is a CLT), and when khat lands on the edge of the grid.

Remarks and practical guidance

Which branch? Run a cross-sectional dependence test first
(xtcd2, or xttestpanel csd). If it rejects, use cce: the section-3
statistic is not merely inefficient under common factors, it is built on inconsistent
per-panel OLS. If it does not reject, the section-3 branch is the more powerful of the two
-- the paper's Table 7 shows power falling markedly once the factors are there.

Sample size. The paper's designs are N, T {&isin} {50, 100, 200}.
Power at k0 = T/2 is essentially 1 in the no-CCE branch even at N = T = 50, and
0.38-0.87 in the CCE branch depending on where the break sits in the coefficient vector.
Power is lowest for breaks near an endpoint (Table 4) and when only a fraction of panels
break (Tables 5 and 9). The precision of khat improves mainly with N, not T
(Figures 4-6).

Balanced panels only. S(k) sums over i at a common k, so all panels must share
the same time grid. Unbalanced data is refused rather than silently mishandled.

One break. The theory covers a single change point. A rejection is consistent
with several breaks, gradual change, or a break in a subset of panels; the argmax will
then pick the dominant one. Re-running on sub-periods around khat is the practical
diagnostic.

Errors. Assumption 3.1 needs a martingale-difference {&epsilon}it with
constant conditional variance per panel; it does not need homoskedasticity across
panels, and the paper's simulations confirm the test survives panel-specific variances and
GARCH(1,1). It does not allow serial correlation in {&epsilon}it; if you suspect
it, model it (add lags) rather than relying on the test.

What to do after a rejection. Take khat to xtbfkbreak for regime-wise
CCE mean-group slopes and a break-date confidence interval:

. xtflucbreak y x1 x2, cce

. xtbfkbreak y x1 x2, breaks(1)

Departures from the printed paper. Three, all documented with the evidence in
help xtflucbreak methods, and two of them switchable:

(1) The absolute value is taken outside the sum over panels, as Theorem 3.4 requires;
inside, as Remark 3.5 typesets it, the statistic diverges under H0. No switch --
the literal form has no valid critical value.

(2) The 1/sigmahati scaling is retained in the CCE branch, which Theorem 4.8's
standard-bridge claim requires but the printed statistic omits. Switch:
nosigmascale.

(3) Each component is standardised by its exact finite-sample variance rather than by the
asymptotic s(1-s), and the search grid is trimmed 10% by default. Without these the test
has empirical size 0.67 at N = T = 50 and 0.26 at T = 25. Switches: asymptotic and
trimming(). The literal statistic is reported regardless.

Examples

Setup

. webuse grunfeld, clear

. xtset company year

The basic test

. xtflucbreak invest mvalue kstock

With every figure and the per-panel table

. xtflucbreak invest mvalue kstock, graph showunits

Allowing unobserved common factors

. xtflucbreak invest mvalue kstock, cce

Against the Wald and CUSUM benchmarks

. xtflucbreak invest mvalue kstock, compare reps(999) seed(12345)

At the 1% level, trimming 10% of the sample at each end

. xtflucbreak invest mvalue kstock, level(1) trimming(0.1)

As postestimation, reusing the model in memory

. xtreg invest mvalue kstock, fe

. xtflucbreak

Then estimate the regimes at the detected date

. xtflucbreak invest mvalue kstock, cce

. xtbfkbreak invest mvalue kstock, breaks(1)

A full simulated demonstration reproducing both Monte Carlo designs of the paper is
in the ancillary file xtflucbreak_example.do, retrievable with
net get xtflucbreak.

Stored results

xtflucbreak stores the following in r():

Scalars

r(stat) max over components of maxk|S(k)| (decision statistic)

r(stat_lxc) the same maximum on the literal LXC scale

r(p_lxc) its Sidak p-value

r(cv) critical value C1({&alpha}*)

r(p) overall Sidak p-value

r(alphastar) per-component level {&alpha}*

r(level) overall level in percent

r(reject) 1 if H0 is rejected, 0 otherwise

r(khat) estimated change point, as an index 1..T

r(breakdate) last pre-break period on the time variable's scale

r(breakpost) first post-break period

r(kfrac) khat/T

r(N) number of panels

r(T) periods per panel used (net of ccalags())

r(K) number of tested coefficients

r(kmin) , r(kmax) bounds of the admissible search grid

r(jmax) component attaining the maximum

r(fracpos) sign-concordance index of the panel shifts

r(wald1) , r(wald1_cv), r(wald1_p) Wald 1 (compare)

r(wald2) , r(wald2_cv), r(wald2_p) Wald 2 (compare)

r(cusum) , r(cusum_cv), r(cusum_p) CUSUM (compare)

r(reps) bootstrap replications (compare)

Macros

r(cmd) xtflucbreak

r(cmdline) command as typed

r(depvar) , r(indepvars), r(panelvar), r(timevar)

r(transform) none or CCE (...)

r(root) symmetric or cholesky

r(sigmascale) 1 if the 1/sigmahati scaling was applied

Matrices

r(S) grid {&times} (2K+2): k, time value, the K decision-scale components of
S(k), then the K literal-LXC components

r(V) grid {&times} 3: k, time value, ||V(k)||

r(stats) K {&times} 5: statistic, critical value, p-value, reject, literal-LXC
statistic

r(bi) N {&times} (K+1): panel id, full-sample {&beta}hati

r(shift) (N+3) {&times} (K+1): panel id and {&delta}hati; the last three
rows are the mean, sd and percent positive

r(sigma) N {&times} 2: panel id, sigmahati

r(compare) 3 {&times} 3: statistic, bootstrap cv, bootstrap p for Wald 1,
Wald 2, CUSUM (compare)

r(cprofile) grid {&times} 5: t, time value, |Wald 1|, |Wald 2|, |CUSUM|
processes (compare)

References

Antoch, J., J. Hanousek, L. Horvath, M. Huskova, and S. Wang. 2018.
Structural breaks in panel data: Large number of panels and short length time series.
Econometric Reviews 38(7): 828-855.

Baltagi, B. H., Q. Feng, and C. Kao. 2016.
Estimation of heterogeneous panels with structural breaks.
Journal of Econometrics 191: 176-195.

Chu, C.-S. J., and H. White. 1992.
A direct test for changing trend.
Journal of Business and Economic Statistics 10: 289-299.

Leisch, F., K. Hornik, and C.-M. Kuan. 2000.
Monitoring structural changes with the generalized fluctuation test.
Econometric Theory 16: 835-854.

Li, F., Y. Xiao, and Z. Chen. 2024.
A fluctuation test for structural change detection in heterogeneous panel data models.
Journal of Systems Science and Complexity 37(3): 1184-1208.

Pesaran, M. H. 2006.
Estimation and inference in large heterogeneous panels with a multifactor error structure.
Econometrica 74: 967-1012.

Author

Dr Merwan Roudane

merwanroudane920@gmail.com

github.com/merwanroudane

Bug reports and suggestions are welcome.

Also see

Manual: [XT] xtset, [XT] xtreg

Online: xtflucbreak methods,
xtflucbreak postestimation,
xtbfkbreak, xtcombreak, xtreg