MinnesotaPrior#
- class cultivars.bayes.priors.MinnesotaPrior(*, tightness=0.2, cross_equation=0.5, decay=1.0, exogenous=100.0, sum_of_coefficients=None, persistence=1.0)[source]#
Litterman’s prior: shrink toward independent random walks.
The prior of Litterman (1986) and Doan, Litterman, and Sims (1984) states that, absent evidence, each variable follows its own random walk: the coefficient on a variable’s own first lag is centred at
persistence(one, for levels) and every other coefficient at zero, with variances that say how far the data may pull each one away. For equation \(i\) and the coefficient on variable \(j\) at lag \(l\), with \(s_i\) the residual scale of a univariate autoregression of variable \(i\),\[\begin{split}\operatorname{Var}(A_{l,ij}) = \begin{cases} \left(\dfrac{\lambda_1}{l^{\lambda_3}}\right)^{2} & i = j,\\[1.2em] \left(\dfrac{\lambda_1 \lambda_2}{l^{\lambda_3}}\, \dfrac{s_i}{s_j}\right)^{2} & i \ne j, \end{cases} \qquad \operatorname{Var}(c_i) = (\lambda_1 \lambda_4\, s_i)^{2},\end{split}\]the last for the intercept, any trend, and any exogenous regressor. Five hyperparameters, and each answers a different question.
tightness\(\lambda_1\) is how much the prior is believed at all; it scales every variance and is the one people tune.cross_equation\(\lambda_2\) says how much less a variable’s dependence on other variables is believed than its dependence on itself, which is the prior’s central economic claim – most of what a series does is explained by its own past – and is the hyperparameter a dummy-observation implementation cannot express.decay\(\lambda_3\) tightens longer lags toward zero, encoding that distant history matters less.exogenous\(\lambda_4\) loosens the deterministic and exogenous block and defaults large: those coefficients carry levels and slopes that nobody means to shrink, and a tight default there is a silent and expensive mistake.sum_of_coefficients\(\lambda_5\) is the only one that cannot be a variance: it restricts a sum of coefficients, so it enters as artificial rows throughdummy_observations(). The scale ratio \(s_i / s_j\) is what makes variables measured in different units comparable, so that shrinkage is a statement about dynamics rather than about whether a series is quoted in percent or in levels.With \(\lambda_2 \ne 1\) the prior variance does not factor as \(\Sigma \otimes \Omega\), so there is no Normal-inverse-Wishart closed form, and estimation runs equation by equation conditional on the scales. That is Litterman’s original procedure and it is correct; it is also why this prior and
NormalInverseWishartPriorare different estimation paths rather than the same object with different numbers. This prior is accepted byVARfor a shrunk point estimate and byGibbsBVARfor a sampled posterior; the conjugateBVARaccepts it only withcross_equation=1, where it coincides with the conjugate form.- Variables:
tightness – Overall confidence, \(\lambda_1\). Values near 0.1 to 0.3 are usual for macroeconomic data; large recovers least squares.
cross_equation – How much harder cross-variable coefficients shrink, \(\lambda_2\). One treats them like own lags; smaller is tighter.
decay – Lag decay, \(\lambda_3\).
exogenous – Looseness of the intercept and exogenous block, \(\lambda_4\). Large is flat.
sum_of_coefficients – Confidence that the variables sit at their pre-sample means forever, \(\lambda_5\).
Noneomits the restriction; larger imposes it harder.persistence – Prior mean of each variable’s own first lag. One is the random-walk prior for levels; zero suits differenced data. Getting this wrong shrinks toward the wrong place, so tightening makes the estimate worse rather than better.
- Raises:
SpecificationError – When a hyperparameter is used, not at construction: if
tightness,cross_equation, orexogenousis not positive, ifdecayis negative, or ifsum_of_coefficientsis given and not positive. The dataclass is frozen and does no validation of its own, so a bad value surfaces at the firstcoefficient_mean()orcoefficient_variance()call, which is the model’s fit.DimensionError – If
persistenceis a sequence whose length is not the number of variables, at the same point.
Note
Priors compose with
+:MinnesotaPrior() + SumOfCoefficientsPrior()stacks the moments of the first with the dummy rows of the second, and the label reads accordingly. Thesum_of_coefficientsfield is the same restriction carried inside this prior for callers who prefer one object; a composition withSumOfCoefficientsPrioris the same rows again, so use one or the other.See also
NormalInverseWishartPrior– the conjugate form with the cross-equation weight pinned at one, for the exact posterior.SumOfCoefficientsPriorandDummyInitialObservationPrior– the dummy-observation components, for stacking with+.minnesota_scales()– the univariate residual scales \(s_i\), computed once per sample by the estimator and passed in through the context.
References
Doan, T., Litterman, R., & Sims, C. (1984). Forecasting and conditional projection using realistic prior distributions. Econometric Reviews, 3(1), 1-100.
Litterman, R. B. (1986). Forecasting with Bayesian vector autoregressions: Five years of experience. Journal of Business & Economic Statistics, 4(1), 25-38.
Giannone, D., Lenza, M., & Primiceri, G. E. (2015). Prior selection for vector autoregressions. Review of Economics and Statistics, 97(2), 436-451.
Example
A tight Minnesota prior on a bivariate VAR(2) pulls the point estimate toward independent random walks; the second-lag block, which the prior shrinks hardest, nearly vanishes:
>>> import numpy as np >>> from cultivars.multivariate.reduced_form.vector_autoregression import VAR >>> rng = np.random.default_rng(0) >>> y = np.zeros((120, 2)) >>> for t in range(1, 120): ... y[t] = 0.7 * y[t - 1] + rng.standard_normal(2) >>> ols = VAR(y, order=2).fit() >>> shrunk = VAR(y, order=2, prior=MinnesotaPrior(tightness=0.05)).fit() >>> round(float(np.abs(ols.coefficients[1]).sum()), 3) 0.176 >>> round(float(np.abs(shrunk.coefficients[1]).sum()), 3) 0.038
The prior’s own moments, on a context of two variables at two lags with residual scales one and four: own lags are dimensionless, cross lags carry the scale ratio, and the leading intercept row is loose:
>>> from cultivars.bayes.priors import PriorContext >>> ctx = PriorContext( ... k_endog=2, order=2, scales=np.array([1.0, 4.0]), ... presample_mean=np.array([10.0, 20.0]), ... ) >>> MinnesotaPrior().coefficient_mean(ctx)[1:3] array([[1., 0.], [0., 1.]]) >>> MinnesotaPrior().coefficient_variance(ctx).round(4) array([[4.0e+02, 6.4e+03], [4.0e-02, 1.6e-01], [6.0e-04, 4.0e-02], [1.0e-02, 4.0e-02], [2.0e-04, 1.0e-02]])
Attributes#
Overall confidence \(\lambda_1\); scales every prior variance. |
|
Relative tightness of cross-variable coefficients \(\lambda_2\). |
|
Lag decay \(\lambda_3\); lag \(l\) is shrunk by \(l^{-\lambda_3}\). |
|
Looseness of the deterministic and exogenous block \(\lambda_4\). |
|
Confidence in the sum-of-coefficients restriction \(\lambda_5\), or |
|
a scalar, or one value per variable. |
Methods#
|
Each variable's own first lag at |
|
Litterman's variances, tighter for cross terms and for longer lags. |
|
The sum-of-coefficients rows, when that restriction is asked for. |