| Title: | Mean-Variance Spanning Tests |
|---|---|
| Description: | Provides a comprehensive suite of portfolio spanning tests for asset pricing, such as Huberman and Kandel (1987) <doi:10.1111/j.1540-6261.1987.tb03917.x>, Gibbons et al. (1989) <doi:10.2307/1913625>, Kempf and Memmel (2006) <doi:10.1007/BF03396737>, Pesaran and Yamagata (2024) <doi:10.1093/jjfinec/nbad002>, Gungor and Luger (2016) <doi:10.1080/07350015.2015.1019510>, and the randomized alpha test of Massacci et al. (2026) <doi:10.48550/arXiv.2507.17599>. |
| Authors: | David Ardia [aut, cre, cph, fnd] (ORCID: <https://orcid.org/0000-0003-2823-782X>), Benjamin Seguin [aut], Rosnel Sessinou [ctb], Richard Luger [ctb] |
| Maintainer: | David Ardia <[email protected]> |
| License: | GPL-3 |
| Version: | 1.4-4 |
| Built: | 2026-10-11 09:13:05 UTC |
| Source: | https://github.com/ardiad/spantest |
A character vector of length twelve giving a name for each preset of
span_simulate(). Position in this vector is the preset number, so
DGP_NAMES[9] is the name of dgp = 9.
DGP_NAMESDGP_NAMES
A character vector of length 12.
span_dgp_table(), span_simulate()
Computes robust p-values for linear spanning restrictions using a residual-based
subseries procedure with Cauchy Combination Test (CCT) aggregation. Supports high-dimensional
inference for (variance/spread slopes), (intercepts),
and the joint null .
span_as(bench, test, control = list())span_as(bench, test, control = list())
bench |
Numeric matrix of benchmark returns, dimension |
test |
Numeric matrix of test-asset returns, dimension |
control |
Optional list passed to internal computation:
|
For each k in ks, the sample is partitioned into floor(T^k)
contiguous, NON-overlapping subseries – an exact partition of 1:T, not a
moving block.
Residual perturbations controlled by L generate test statistics robust to serial and
cross-sectional dependence and conditional heteroskedasticity. Resulting sub-p-values are aggregated
by the Cauchy Combination Test (CCT), which remains valid under dependence and retains power in high dimensions.
A named list of global (combined) p-values. Names encode hypothesis and settings:
CCTd_L{L}_k{i} — variance (slope) spanning, ;
CCTa_L{L}_k{i} — alpha spanning, ;
CCTad_L{L}_k{i} — joint mean–variance spanning, .
When L > 0 the score of each statistic is multiplied by its own weights
, ,
drawn independently of the data and of each other across factors, dates, assets
and between the alpha and the delta score of an asset. Since version 1.4-3 this
is the definition of the theory; up to version 1.4-2 one weight vector was
shared by every asset and by both scores. Sharing it leaves the cross-sectional
dependence of the statistics intact, while independent weights shrink it, which
matters when the residuals carry a pervasive common factor: at L = 2,
, = 2 and 10 and = 100 and 1000, the three tests
reject a true null in 6.0–8.5\
2.4–4.8\
At L > 0 a call returns the test for one draw of the weights, from
seed: the alpha weights first, then the delta weights. The test is valid
for any draw — its size is correct — but the p-value it returns on one data
set varies from draw to draw, since a Cauchy average of independent terms does
not concentrate as grows; report the seed with the result. R scrambles
the seed when initialising the Mersenne-Twister, so draws from consecutive
seeds behave as independent draws. Up to version 1.4-2 a control B
merged B draws per asset with the Cauchy rule; it was removed in 1.4-3.
A Monte Carlo study should draw new weights in each replication, for instance
seed = base + r in replication r. With the same seed in every
replication, every cell of the study is computed on one set of weights: the
results are one realisation of the weights, common to all cells, and the Monte
Carlo standard errors understate their uncertainty. The weights enter through
sums over each subseries, so the effect on size is small but not nil (about one
point at , in either direction), and the effect on power can be
larger: in the simulation study of Ardia and Sessinou, the weights of seed 123
overstated the power at L = 2 by 10 to 25 points at moderate
alternatives.
Ardia D, Sessinou R (2025).
“Robust Inference in Large Panels and Markowitz Portfolios.”
doi:10.2139/ssrn.5033399.
Working paper.
Other Alpha Spanning Tests:
span_bj(),
span_f1(),
span_gl_a(),
span_grs(),
span_mstv(),
span_py()
Other Variance Spanning Tests:
span_f2(),
span_km()
Other Joint Mean-Variance Spanning Tests:
span_gl_ad(),
span_hk()
set.seed(123) bench <- matrix(rnorm(300), 100, 3) test <- matrix(rnorm(200), 100, 2) out <- span_as(bench, test) out$CCTa_L0_k1; out$CCTd_L0_k1; out$CCTad_L0_k1set.seed(123) bench <- matrix(rnorm(300), 100, 3) test <- matrix(rnorm(200), 100, 2) out <- span_as(bench, test) out$CCTa_L0_k1; out$CCTd_L0_k1; out$CCTad_L0_k1
Tests whether the tangency (maximum Sharpe) portfolio of the augmented
universe (benchmarks + test assets) is spanned by the benchmark assets
alone. Following Britten–Jones (1999), the statistic arises from a
regression of a constant on the raw asset returns (without an intercept)
and yields an test of the tangency-spanning restriction.
span_bj(R1, R2)span_bj(R1, R2)
R1 |
Numeric matrix of benchmark returns, dimension |
R2 |
Numeric matrix of test-asset returns, dimension |
Following Britten–Jones (1999), the (unnormalized) tangency-portfolio weights
are obtained by regressing a vector of ones on the raw asset returns without an
intercept. The null that the benchmarks span the tangency portfolio is the
restriction that the test assets carry zero weight, tested by comparing the
residual sums of squares of the full and benchmark-only regressions. The
statistic has an reference distribution; finite-sample
feasibility requires .
Equivalence with span_grs(). Although derived from a regression of
ones on raw returns rather than from intercepts, this statistic is
numerically identical to the Gibbons–Ross–Shanken statistic returned by
span_grs(), to machine precision and for every . The two are
the same test of computed by different routes; agreement
between them validates the implementation but is not independent evidence.
span_f1() is close but genuinely distinct.
A named list with components:
pvalP-value for the -statistic under the null.
statBritten–Jones -statistic.
H0Null hypothesis description, "alpha = 0" (the test
assets do not expand the tangency portfolio spanned by the benchmarks).
Britten-Jones M (1999).
“The Sampling Error in Estimates of Mean-Variance Efficient Portfolio Weights.”
The Journal of Finance, 54(2), 655–671.
Other Alpha Spanning Tests:
span_as(),
span_f1(),
span_gl_a(),
span_grs(),
span_mstv(),
span_py()
set.seed(321) R1 <- matrix(rnorm(300), 100, 3) # benchmarks: T=100, K=3 R2 <- matrix(rnorm(200), 100, 2) # tests: T=100, N=2 out <- span_bj(R1, R2) out$stat; out$pval; out$H0set.seed(321) R1 <- matrix(rnorm(300), 100, 3) # benchmarks: T=100, K=3 R2 <- matrix(rnorm(200), 100, 2) # tests: T=100, N=2 out <- span_bj(R1, R2) out$stat; out$pval; out$H0
Returns the correspondence between the names accepted by span_simulate()'s
dgp argument, the preset numbers, and the process each one denotes. Useful
when reading code or output that refers to a process by number.
span_dgp_table()span_dgp_table()
A data frame with one row per process and columns preset (integer),
name (character), innovation, dynamics, df, standardize and
scale. Together they are enough to rebuild any preset by hand with
span_simulate()'s explicit arguments; a table that described a process
only partly is how a discrepancy between the two once went unnoticed.
The twelve are a 3 x 4 factorial of innovation law by dynamics, so a column
varies the dynamics alone: every heavy-tailed preset is standardised, and
scale = "process" gives each one unit unconditional variance whatever its
dynamics.
span_dgp_table() # the same process, two ways set.seed(1); a <- span_simulate(100, 2, 3, dgp = "AR-GARCH-SKST") set.seed(1); b <- span_simulate(100, 2, 3, dgp = 9) identical(a, b)span_dgp_table() # the same process, two ways set.seed(1); a <- span_simulate(100, 2, 3, dgp = "AR-GARCH-SKST") set.seed(1); b <- span_simulate(100, 2, 3, dgp = 9) identical(a, b)
Tests the null that the intercepts of the test assets
are jointly zero when regressed on the benchmark assets, i.e., benchmarks
span the mean of the test assets. This is the F1 test of Kan & Zhou (2012).
span_f1(R1, R2)span_f1(R1, R2)
R1 |
Numeric matrix of benchmark returns, dimension |
R2 |
Numeric matrix of test-asset returns, dimension |
Under standard assumptions (i.i.d. returns, full-rank covariances), the
reference distribution is . Finite-sample feasibility
requires . The covariance matrices are the maximum-likelihood
ones (divisor ), as in Kan and Zhou (2012); with them F1, the first step
of their step-down test, is the GRS statistic, so span_f1() returns the
same value as span_grs() and span_bj(). Up to version 1.4-2 it
used the divisor .
A named list with components:
pvalP-value for the -statistic under the null.
statF1 -statistic.
H0Null hypothesis description, "alpha = 0".
Kan R, Zhou G (2012). “Tests of Mean-Variance Spanning.” Annals of Economics and Finance, 13(1), 145–193.
Other Alpha Spanning Tests:
span_as(),
span_bj(),
span_gl_a(),
span_grs(),
span_mstv(),
span_py()
set.seed(123) R1 <- matrix(rnorm(300), 100, 3) # benchmarks: T=100, K=3 R2 <- matrix(rnorm(200), 100, 2) # tests: T=100, N=2 out <- span_f1(R1, R2) out$stat; out$pval; out$H0set.seed(123) R1 <- matrix(rnorm(300), 100, 3) # benchmarks: T=100, K=3 R2 <- matrix(rnorm(200), 100, 2) # tests: T=100, N=2 out <- span_f1(R1, R2) out$stat; out$pval; out$H0
Tests the null that adding test assets does not
improve the minimum-variance frontier spanned by the benchmarks (variance
spanning). The statistic compares frontier-defining quantities of the
augmented (benchmark + test) universe to those of the benchmark subset.
span_f2(R1, R2)span_f2(R1, R2)
R1 |
Numeric matrix of benchmark returns, dimension |
R2 |
Numeric matrix of test-asset returns, dimension |
Under standard conditions (i.i.d. returns, full-rank covariances), the reference
distribution is . Finite-sample feasibility requires
. F2 is the second step of the step-down test of Kan and
Zhou (2012): it tests conditional on , so it is
a test of alone only when the intercepts are zero; for
with free intercepts, use span_km(). The covariance
matrices are the maximum-likelihood ones (divisor ); up to version 1.4-2
they used .
A named list with components:
pvalP-value for the -statistic under the null.
statF2 -statistic.
H0Null hypothesis description, "delta = 0".
Kan R, Zhou G (2012). “Tests of Mean-Variance Spanning.” Annals of Economics and Finance, 13(1), 145–193.
Other Variance Spanning Tests:
span_as(),
span_km()
set.seed(123) R1 <- matrix(rnorm(300), 100, 3) # benchmarks: T=100, K=3 R2 <- matrix(rnorm(200), 100, 2) # tests: T=100, N=2 out <- span_f2(R1, R2) out$stat; out$pval; out$H0set.seed(123) R1 <- matrix(rnorm(300), 100, 3) # benchmarks: T=100, K=3 R2 <- matrix(rnorm(200), 100, 2) # tests: T=100, N=2 out <- span_f2(R1, R2) out$stat; out$pval; out$H0
Tests the null that benchmark assets span the mean
(intercepts) of the test assets. Following Gungor & Luger (2016), the
procedure uses a Monte Carlo (MC) test based on an statistic
with residual sign-flip simulations, yielding local (LMC) and
bounds (BMC) Monte Carlo p-values and a three-way decision rule.
span_gl_a(R1, R2, control = list())span_gl_a(R1, R2, control = list())
R1 |
Numeric matrix of benchmark returns, dimension |
R2 |
Numeric matrix of test-asset returns, dimension |
control |
List of options:
|
Accept if pval_LMC > alpha; Reject if pval_BMC <= alpha;
otherwise Inconclusive. The subseries sign-flip MC approach is robust to
heteroskedasticity, serial dependence, and heavy tails, making it suitable
for high-dimensional settings where classical alpha tests (e.g., GRS) may
suffer from size distortions.
A list with components:
pval_LMCLocal Monte Carlo p-value, the liberal one: an acceptance is read off it.
pval_BMCBounds Monte Carlo p-value, the conservative one: a rejection is read off it.
statObserved statistic: the largest asset-level ratio
. The usual F form multiplies it by a positive constant,
which leaves the ranks, the p-values and the decisions unchanged.
DecisionsDecision code: 1 = Accept, 0 = Reject, NA = Inconclusive.
Decisions_stringText label: "Accept", "Reject", or "Inconclusive".
H0Null hypothesis description, "alpha = 0".
Gungor S, Luger R (2016).
“Multivariate Tests of Mean-Variance Efficiency and Spanning With a Large Number of Assets and Time-Varying Covariances.”
Journal of Business & Economic Statistics, 34(2), 161–175.
Other Alpha Spanning Tests:
span_as(),
span_bj(),
span_f1(),
span_grs(),
span_mstv(),
span_py()
set.seed(1234) R1 <- matrix(rnorm(300), 100, 3) R2 <- matrix(rnorm(200), 100, 2) out <- span_gl_a(R1, R2, control = list(totsim = 100, do_trace = FALSE)) out$Decisions_string; out$pval_LMC; out$pval_BMCset.seed(1234) R1 <- matrix(rnorm(300), 100, 3) R2 <- matrix(rnorm(200), 100, 2) out <- span_gl_a(R1, R2, control = list(totsim = 100, do_trace = FALSE)) out$Decisions_string; out$pval_LMC; out$pval_BMC
Tests the joint null that benchmark assets
span both intercepts and slopes of the test assets, allowing for
heteroskedasticity, serial dependence, and time-varying covariances.
Following Gungor & Luger (2016), the procedure uses a Monte Carlo (MC)
test based on an statistic with residual sign-flip simulations,
yielding local (LMC) and bounds (BMC) Monte Carlo p-values and a
three-way decision rule.
span_gl_ad(R1, R2, control = list())span_gl_ad(R1, R2, control = list())
R1 |
Numeric matrix of benchmark returns, dimension |
R2 |
Numeric matrix of test-asset returns, dimension |
control |
List of options:
|
LMC/BMC follow Gungor & Luger’s MC framework with residual sign-flip draws under
the null. The rule is: Accept if pval_LMC > alpha; Reject if
pval_BMC <= alpha; otherwise Inconclusive. This approach is robust in
high-dimensional and time-varying volatility settings where classical joint
spanning tests can be unreliable.
A list with components:
pval_LMCLocal Monte Carlo p-value, the liberal one: an acceptance is read off it.
pval_BMCBounds Monte Carlo p-value, the conservative one: a rejection is read off it.
statObserved statistic: the largest asset-level ratio
. The usual F form multiplies it by a positive constant,
which leaves the ranks, the p-values and the decisions unchanged.
DecisionsDecision code: 1 = Accept, 0 = Reject, NA = Inconclusive.
Decisions_stringText label: "Accept", "Reject", or "Inconclusive".
H0Null hypothesis description, "alpha = 0 and delta = 0".
Gungor S, Luger R (2016).
“Multivariate Tests of Mean-Variance Efficiency and Spanning With a Large Number of Assets and Time-Varying Covariances.”
Journal of Business & Economic Statistics, 34(2), 161–175.
Other Joint Mean-Variance Spanning Tests:
span_as(),
span_hk()
set.seed(123) R1 <- matrix(rnorm(300), 100, 3) R2 <- matrix(rnorm(200), 100, 2) out <- span_gl_ad(R1, R2, control = list(totsim = 100, do_trace = FALSE)) out$Decisions_string; out$pval_LMC; out$pval_BMCset.seed(123) R1 <- matrix(rnorm(300), 100, 3) R2 <- matrix(rnorm(200), 100, 2) out <- span_gl_ad(R1, R2, control = list(totsim = 100, do_trace = FALSE)) out$Decisions_string; out$pval_LMC; out$pval_BMC
Implements the GRS test of the joint null in the
multivariate regression of test-asset returns on benchmark portfolios
(with an intercept). The statistic assumes homoskedastic, normally
distributed errors and is most reliable when is large relative
to (benchmarks) and (test assets).
span_grs(R1, R2)span_grs(R1, R2)
R1 |
Numeric matrix of benchmark returns, dimension |
R2 |
Numeric matrix of test-asset returns, dimension |
Under standard conditions, the reference distribution is
. Finite-sample feasibility requires .
The residual covariance enters in its maximum-likelihood form (divisor
), which is what pairs with the scaling to give an
exact test.
Equivalence with span_bj(). This statistic is numerically
identical to the Britten–Jones (1999) statistic returned by span_bj(),
to machine precision and for every : the two are different
computational routes to the same test of , not independent
procedures. Reporting both is therefore a check on the implementation, not
corroborating evidence from two tests. span_f1() is close but genuinely
distinct.
A named list with components:
pvalP-value for the -statistic under the null.
statGRS -statistic.
H0Null hypothesis description, "alpha = 0".
Gibbons MR, Ross SA, Shanken J (1989).
“A Test of the Efficiency of a Given Portfolio.”
Econometrica, 57(5), 1121–1152.
Other Alpha Spanning Tests:
span_as(),
span_bj(),
span_f1(),
span_gl_a(),
span_mstv(),
span_py()
set.seed(42) R1 <- matrix(rnorm(300), 100, 3) # benchmarks: T=100, K=3 R2 <- matrix(rnorm(200), 100, 2) # tests: T=100, N=2 out <- span_grs(R1, R2) out$stat; out$pval; out$H0set.seed(42) R1 <- matrix(rnorm(300), 100, 3) # benchmarks: T=100, K=3 R2 <- matrix(rnorm(200), 100, 2) # tests: T=100, N=2 out <- span_grs(R1, R2) out$stat; out$pval; out$H0
Tests the joint null that the benchmark
assets span the mean–variance frontier of the augmented (benchmark + test)
universe. Following Huberman & Kandel (1987), the statistic compares the
frontiers with and without the additional assets.
span_hk(R1, R2)span_hk(R1, R2)
R1 |
Numeric matrix of benchmark returns, dimension |
R2 |
Numeric matrix of test-asset returns, dimension |
The test evaluates whether adding the test assets changes the efficient
frontier implied by the benchmarks. Under standard regularity conditions,
the statistic has an reference with degrees of
freedom. Finite-sample feasibility requires . The covariance
matrices are the maximum-likelihood ones (divisor ), which makes the
statistic the likelihood-ratio form; up to version 1.4-2 they used .
A named list with components:
pvalP-value for the -statistic under the null.
stat-statistic value.
H0Null hypothesis description, "alpha = 0 and delta = 0".
Huberman G, Kandel S (1987).
“Mean-Variance Spanning.”
The Journal of Finance, 42(4), 873–888.
Other Joint Mean-Variance Spanning Tests:
span_as(),
span_gl_ad()
set.seed(123) R1 <- matrix(rnorm(300), 100, 3) # benchmarks: T=100, K=3 R2 <- matrix(rnorm(200), 100, 2) # tests: T=100, N=2 out <- span_hk(R1, R2) out$stat; out$pval; out$H0set.seed(123) R1 <- matrix(rnorm(300), 100, 3) # benchmarks: T=100, K=3 R2 <- matrix(rnorm(200), 100, 2) # tests: T=100, N=2 out <- span_hk(R1, R2) out$stat; out$pval; out$H0
Tests whether the Global Minimum Variance Portfolio (GMVP) of the combined (benchmark + test) universe equals the GMVP of the benchmark assets alone. Following Kempf & Memmel (2006), the null assesses whether adding new assets improves the minimum-variance frontier.
span_km(R1, R2)span_km(R1, R2)
R1 |
Numeric matrix of benchmark returns, dimension |
R2 |
Numeric matrix of test-asset returns, dimension |
The null hypothesis is that augmenting the benchmark set with the
test assets does not change the GMVP weights (), i.e.,
the GMVP of the full universe coincides with that of the benchmark subset.
The test uses the Kempf–Memmel regression
whose slopes are the global-minimum-variance portfolio weights
( for and ).
The test assets leave the GMVP unimproved exactly when their
block is zero, so is an -dimensional linear restriction,
tested by an statistic with reference distribution
. Finite-sample feasibility requires .
Note that regressing a vector of ones on the returns instead—the
Britten–Jones tangency construction used by span_bj()—produces a test of
, not of . The two are easily confused: they
share the difference-of-returns design matrix and differ only in the
dependent variable and the intercept.
A named list with components:
pvalP-value for the F-statistic under the null.
statF-statistic value.
H0Null hypothesis description, "delta = 0" (equivalently,
GMVP of the benchmark set equals GMVP of the full universe).
Kempf A, Memmel C (2006).
“Estimating the Global Minimum Variance Portfolio.”
Schmalenbach Business Review, 58(4), 332–348.
Other Variance Spanning Tests:
span_as(),
span_f2()
set.seed(123) R1 <- matrix(rnorm(300), 100, 3) # benchmarks: T=100, K=3 R2 <- matrix(rnorm(200), 100, 2) # test assets: T=100, N=2 ans <- span_km(R1, R2) ans$pval; ans$stat; ans$H0set.seed(123) R1 <- matrix(rnorm(300), 100, 3) # benchmarks: T=100, K=3 R2 <- matrix(rnorm(200), 100, 2) # test assets: T=100, N=2 ans <- span_km(R1, R2) ans$pval; ans$stat; ans$H0
Implements the randomized test of Massacci et al. (2026)
for the joint null that every intercept is zero in a linear factor pricing
model. The test is built from equation-by-equation estimation, needs no
covariance matrix, and allows to grow faster than . Both the
one-shot test of their Theorem 3.1 and the derandomized decision rule of their
Section 3.2 are returned.
span_mstv(R1, R2, control = list())span_mstv(R1, R2, control = list())
R1 |
Numeric matrix of benchmark returns, dimension |
R2 |
Numeric matrix of test-asset returns, dimension |
control |
Optional list:
|
Write for the OLS intercept of test asset on the
benchmarks and for the pooled root mean squared residual
over the whole panel. The test rescales the intercepts into
which drifts to zero under the null and diverges under the alternative,
perturbs them with independent standard normals, and takes the largest,
. Under the null is
the maximum of standard normals, so
is asymptotically Gumbel with the usual
extreme-value constants, giving both crit and pval. Only rates
of convergence are used, which is what frees the procedure from estimating a
covariance matrix and lets outgrow .
Because the perturbation does not vanish asymptotically, two researchers
applying the one-shot test to the same data can reach different conclusions.
Their remedy is to repeat the perturbation times and record the share
of replications that do not reject, then decide against
the null when that share falls below with
. As in the authors' replication code, the share is
simulated with draws by default, so it is
a multiple of and the decision depends on the draws. Up to version
1.4-2 the share was evaluated in closed form, as its limit
; that limit is a different rule in
practice, since with between 15 and 50 the simulated share is coarse.
The Gumbel limit is an approximation in , and a poor one when
is small: at the one-shot test rejects about a quarter of the time
at a nominal 5%. The function therefore returns NA below
, and in the hundreds is where the procedure is meant to
operate.
A named list with components:
pvalP-value of the one-shot test, from the Gumbel limit.
statThe one-shot statistic .
H0Null hypothesis description, "alpha = 0".
critCritical value of the one-shot test.
QDerandomized quantity : the share of the
B perturbation draws whose statistic does not exceed crit.
rejectDecision of the derandomized rule: TRUE when
Q < (1 - tau) - B^(-1/4).
nu, B
The settings used.
All components except H0 are NA when the test does not apply.
is the number of finite moments assumed, entering only through the
exponent ; the theory needs and the admissible
growth of relative to widens with it. It can be estimated with
a tail-index estimator, or bounded below by testing
for a candidate . The default here is , the value of
the authors' simulations; their empirical application reports and
. A smaller makes the test more liberal. Up to version
1.4-2 the default was 4.
The size of the one-shot test is a probability over the data and the
perturbation together, so a Monte Carlo study must draw a new perturbation in
each replication, for instance seed = base + r in replication r.
With the same seed in every replication, all replications share one vector
and the rejection rate measures the test conditionally on that
draw. Under the null at small the are close to zero and
the statistic is essentially , so that rate is near 0 or
near 1 whatever the data: with the default seed and ,
against a critical value of 3.98, and the
rejection rate is 0 instead of about 4%. The derandomized rule draws its
B perturbations from the same seed, so the same holds for it.
Massacci D, Sarno L, Trapani L, Vallarino P (2026). “A General Randomized Test for Alpha.” Journal of the American Statistical Association. doi:10.48550/arXiv.2507.17599. Forthcoming.
Other Alpha Spanning Tests:
span_as(),
span_bj(),
span_f1(),
span_gl_a(),
span_grs(),
span_py()
set.seed(123) R1 <- matrix(rnorm(3 * 250), 250, 3) # benchmarks: T=250, K=3 R2 <- matrix(rnorm(100 * 250), 250, 100) # tests: T=250, N=100 out <- span_mstv(R1, R2) out$pval; out$Q; out$rejectset.seed(123) R1 <- matrix(rnorm(3 * 250), 250, 3) # benchmarks: T=250, K=3 R2 <- matrix(rnorm(100 * 250), 250, 100) # tests: T=250, N=100 out <- span_mstv(R1, R2) out$pval; out$Q; out$reject
Implements the Pesaran–Yamagata test for the joint null that all intercepts are zero in a multi-factor spanning regression with possible cross-sectional dependence across test assets.
span_py(R1, R2)span_py(R1, R2)
R1 |
Numeric matrix of benchmark returns, dimension |
R2 |
Numeric matrix of test-asset returns, dimension |
The null hypothesis is that all intercepts are zero (), meaning
the benchmark assets span the expected returns of the test assets. The statistic
adjusts for cross-sectional dependence via the residual covariance and has an
asymptotic reference under large . It uses the
asset-level t-statistics and the pairwise residual correlations only, never an
inverse of the residual covariance, so it is defined when
exceeds . Finite-sample safeguards require and at
least two test assets (); otherwise pval and stat are
returned as NA. Up to version 1.4-2 it also required .
A named list with components:
pvalP-value under the standard normal reference distribution.
statStandardized test statistic.
H0Null hypothesis description, "alpha = 0".
Pesaran MH, Yamagata T (2024). “Testing for alpha in linear factor pricing models with a large number of securities.” Journal of Financial Econometrics, 22(2), 407–460.
Other Alpha Spanning Tests:
span_as(),
span_bj(),
span_f1(),
span_gl_a(),
span_grs(),
span_mstv()
set.seed(123) R1 <- matrix(rnorm(300), 100, 3) # benchmarks: T=100, K=3 R2 <- matrix(rnorm(200), 100, 2) # tests: T=100, N=2 out <- span_py(R1, R2) out$pval; out$stat; out$H0set.seed(123) R1 <- matrix(rnorm(300), 100, 3) # benchmarks: T=100, K=3 R2 <- matrix(rnorm(200), 100, 2) # tests: T=100, N=2 out <- span_py(R1, R2) out$pval; out$stat; out$H0
Generates a panel of benchmark returns () and
test-asset returns () from a factor model with a
controllable mean-variance spanning violation, matching the data-generating
processes used in the size/power study of Ardia and Sessinou (2025). It is a
fast, self-contained replacement for ad-hoc fGarch::garchSim() loops:
the innovations are drawn in one vectorised call and the AR / GARCH dynamics
are applied with a lightweight recursion.
span_simulate( n, K, N, ncp = 0, rho_factor = 0.8, rho_error = 0.5, innovation = c("normal", "t", "skew-t"), dynamics = c("iid", "ar", "garch", "ar-garch"), sparse = FALSE, df = 5, xi = 0.9, ar = 0.2, garch = c(omega = 0.1, alpha = 0.1, beta = 0.8), standardize = TRUE, scale = c("innovation", "process"), dgp = NULL, gamma = NULL, burnin = 500L )span_simulate( n, K, N, ncp = 0, rho_factor = 0.8, rho_error = 0.5, innovation = c("normal", "t", "skew-t"), dynamics = c("iid", "ar", "garch", "ar-garch"), sparse = FALSE, df = 5, xi = 0.9, ar = 0.2, garch = c(omega = 0.1, alpha = 0.1, beta = 0.8), standardize = TRUE, scale = c("innovation", "process"), dgp = NULL, gamma = NULL, burnin = 500L )
n |
Integer, number of time periods (sample size). |
K |
Integer, number of benchmark assets. |
N |
Integer, number of test assets. |
ncp |
Non-centrality controlling the spanning violation (0 = null). |
rho_factor, rho_error
|
Toeplitz correlation decays for the factors and
the idiosyncratic terms, in |
innovation |
Marginal law of the innovations: |
dynamics |
Serial structure: |
sparse |
Logical; if |
df |
Degrees of freedom for the Student-/skew- |
xi |
Skewness parameter for the skew- |
ar |
AR(1) coefficient (used by |
garch |
Named numeric vector |
standardize |
Logical; standardise |
scale |
Where unit variance is imposed. |
dgp |
Optional preset process, given either as a name — one of
|
gamma |
Optional pervasive common component in the idiosyncratic terms:
a single number, or a vector of |
burnin |
Integer, number of initial observations discarded to remove the AR/GARCH transient. |
The latent factors are innovation series, cross-sectionally
correlated through the Cholesky factor of a Toeplitz matrix with decay
rho_factor; the idiosyncratic terms are innovation series with
Toeplitz decay rho_error. The benchmarks are these series themselves,
, and the test assets are
so that . Under ncp = 0
the benchmarks span the test assets (, ); larger
|ncp| moves both restrictions away from the null, to
ncp and to -ncp. Up to version 1.4-2 the benchmarks
were with loadings on : the
same loadings on the benchmarks, but benchmarks with correlations near 0.9 and
unequal variances rather than the Toeplitz structure of the process.
With sparse = TRUE the first intercepts are
set to zero (a sparse alternative); is added column-wise, i.e.
asset receives .
Each innovation series is defined by innovation (marginal law) and
dynamics (serial structure): "iid", an AR(1) with coefficient
ar, a GARCH(1,1) with parameters garch, or their
"ar-garch" combination. Student- innovations are standardised to
unit variance when standardize = TRUE; skew- innovations
(standardised Fernandez-Steel skew-) are always unit-variance, so
standardize has no effect on them.
dgp is a convenience argument selecting one of the twelve processes of
Ardia and Sessinou (2025); when supplied it overrides innovation,
dynamics, df, xi and standardize:
| 1 | normal, iid | 7 | normal, AR-GARCH |
| 2 | Student-t(5), iid | 8 | Student-t(5), AR-GARCH |
| 3 | skew-t(4, 0.9), iid | 9 | skew-t(4, 0.9), AR-GARCH |
| 4 | normal, GARCH | 10 | normal, AR |
| 5 | Student-t(5), GARCH | 11 | Student-t(5), AR |
| 6 | skew-t(4, 0.9), GARCH | 12 | skew-t(4, 0.9), AR |
The twelve are a 3 x 4 factorial: one innovation law per family — normal,
standardised , standardised skew- with 4 df and
— repeated across the four dynamics, so a column varies the dynamics alone.
A list with components R1 ( benchmark returns)
and R2 ( test-asset returns), suitable as the
R1/R2 arguments of the spanning tests in this package.
Set the RNG state with set.seed beforehand for
reproducibility.
Ardia D, Sessinou R (2025).
“Robust Inference in Large Panels and Markowitz Portfolios.”
doi:10.2139/ssrn.5033399.
Working paper.
Gungor S, Luger R (2016).
“Multivariate Tests of Mean-Variance Efficiency and Spanning With a Large Number of Assets and Time-Varying Covariances.”
Journal of Business & Economic Statistics, 34(2), 161–175.
span_grs(), span_gl_ad(), span_as()
set.seed(1) sim <- span_simulate(n = 250, K = 3, N = 10, ncp = 0) # spanning null span_grs(sim$R1, sim$R2)$pval sim2 <- span_simulate(n = 250, K = 3, N = 10, ncp = 0.2) # alternative span_grs(sim2$R1, sim2$R2)$pvalset.seed(1) sim <- span_simulate(n = 250, K = 3, N = 10, ncp = 0) # spanning null span_grs(sim$R1, sim$R2)$pval sim2 <- span_simulate(n = 250, K = 3, N = 10, ncp = 0.2) # alternative span_grs(sim2$R1, sim2$R2)$pval