QVARResult#

class cultivars.multivariate.nonlinear.quantile.QVARResult(*, endog, names, order, trend, quantiles, coefficient_stacks, deterministics, fittedvalues, resid, loss, loss_location, nobs)[source]#

A fitted quantile vector autoregression, all requested levels together.

For each level \(\tau\) in quantiles and each equation \(i\), the coefficients solve the check-loss program

\[\min_{c_i(\tau),\, a_i(\tau)} \sum_t \rho_\tau\!\Bigl(y_{i,t} - c_i(\tau)' d_t - \sum_{l=1}^{p} a_{i,l}(\tau)' y_{t-l}\Bigr), \qquad \rho_\tau(e) = e\,(\tau - \mathbf{1}\{e < 0\}),\]

so \(Q_\tau(y_{i,t} \mid y_{t-1}, \ldots)\) is linear in the system’s lags with its own coefficient stack at every level. The programs separate across equations and across levels, and each is solved exactly; the stacks are collected level-major, ascending in \(\tau\).

Deliberately absent: llf, information criteria, orthogonalized responses, and multi-step forecasts. The module docstring states why each refusal is structural rather than an omission.

Note

Every accessor that takes a quantile resolves it against the levels actually estimated (to within 1e-9) and refuses any other – a new level is a new fit, not an interpolation. The fitted paths at a level are exact in-sample quantiles in the sense of the program: the share of observations below the fitted \(\tau\)-path is \(\tau\) up to the discreteness of the sample. Levels are fitted independently, so the paths may cross; crossing_share measures how often, and the result does not rearrange them. resid is y - fitted at each level, a quantile residual whose sign pattern, not its magnitude, carries the fit.

Variables:
  • endog – The observed panel.

  • names – Variable labels, in column order.

  • order – Autoregressive order.

  • trend – Deterministic specification.

  • quantiles – The estimated levels, ascending.

  • coefficient_stacks – (Q, p, k, k) lag stacks, one per level.

  • deterministics – (Q, n_det, k) deterministic coefficients.

  • fittedvalues – (Q, nobs, k) in-sample conditional quantile paths.

  • resid – (Q, nobs, k) quantile residuals y - fitted.

  • loss – (Q, k) total check loss per equation at the optimum.

  • loss_location – (Q, k) check loss of the unconditional quantile, the benchmark pseudo_r_squared() is read against.

  • nobs – Effective sample size.

See also

  • QVAR – the model that produces this record.

  • VARResult – the conditional-mean system whose single stack these levels fan out from.

  • GARCHResult – the other account of a moving tail, through the variance.

References

Koenker, R., & Bassett, G. (1978). Regression quantiles. Econometrica, 46(1), 33-50.

Koenker, R., & Machado, J. A. F. (1999). Goodness of fit and related inference processes for quantile regression. Journal of the American Statistical Association, 94(448), 1296-1310.

White, H., Kim, T.-H., & Manganelli, S. (2015). VAR for VaR: Measuring tail dependence using multivariate regression quantiles. Journal of Econometrics, 187(1), 169-188.

Example

A growth-at-risk system: growth depends on lagged financial conditions fci at the median, and its downside depends on them more, because the negative shocks are scaled by fci. The lower-tail coefficient is roughly three times the median one, the 10th-percentile path sits below a tenth of the observations, and the paths barely cross:

>>> import numpy as np
>>> rng = np.random.default_rng(0)
>>> y = np.zeros((400, 2))
>>> for t in range(1, 400):
...     fci = 0.6 * y[t - 1, 1] + rng.standard_normal()
...     shock = rng.standard_normal()
...     if shock < 0.0:
...         shock *= np.exp(0.4 * y[t - 1, 1])
...     y[t] = [0.3 * y[t - 1, 0] - 0.2 * y[t - 1, 1] + shock, fci]
>>> res = QVAR(y, order=1, names=("growth", "fci")).fit(quantiles=(0.1, 0.5, 0.9))
>>> res.quantiles, res.n_quantiles, res.k_endog, res.nobs
((0.1, 0.5, 0.9), 3, 2, 399)
>>> res.coefficient_stacks.shape, res.fittedvalues.shape, res.loss.shape
((3, 1, 2, 2), (3, 399, 2), (3, 2))
>>> tail, median = res.coefficients(0.1)[0, 0, 1], res.coefficients(0.5)[0, 0, 1]
>>> bool(tail < 2.5 * median < 0.0)
True
>>> below = y[1:, 0] < res.quantile_path("growth", 0.1)
>>> bool(abs(below.mean() - 0.1) < 0.01)
True
>>> bool(res.crossing_share < 0.01)
True
>>> bool(np.all(np.diff([res.forecast(q)[0] for q in res.quantiles]) > 0.0))
True

Attributes#

endog

The observed panel, shape (nobs_total, k).

names

Variable labels, in column order.

order

The autoregressive order p.

trend

"n", "c" or "ct".

quantiles

The estimated levels, ascending, each strictly inside (0, 1).

coefficient_stacks

(Q, p, k, k) lag stacks, level-major.

deterministics

(Q, n_det, k) deterministic coefficients, level-major.

fittedvalues

(Q, nobs, k) in-sample conditional quantile paths.

resid

(Q, nobs, k) quantile residuals y - fitted.

loss

(Q, k) total check loss per equation at the optimum.

loss_location

(Q, k) check loss of the unconditional sample quantile.

nobs

Effective sample size after the p presample rows.

crossing_share

Share of fitted points where adjacent quantile paths cross.

k_endog

Number of endogenous variables.

n_quantiles

Number of estimated levels.

Methods#

coefficients(quantile)

One level's lag stack A_1(tau), ..., A_p(tau).

deterministic(quantile)

One level's deterministic coefficients, design-major.

forecast(quantile)

The one-step-ahead conditional quantile of every variable.

irf([horizon, cumulative])

Reduced-form propagation of one level's coefficients.

ma_representation([horizon])

Moving-average matrices of one level's coefficient stack.

pseudo_r_squared(quantile)

Koenker-Machado goodness of fit per equation at one level.

quantile_path(name, quantile)

One variable's in-sample conditional quantile path.

stability_profile()

Largest companion-root modulus of each level's system, ascending in tau.

summary()

The estimation summary, renderable as text, HTML, or a dataframe.