pyvallocation.bayesian module

Bayesian utilities complement the shrinkage estimators by providing posterior updates for both means and covariances. Highlights include:

Robust-Bayesian uncertainty

The NIW posterior implies a closed-form covariance of the mean \(S_{\\mu}\) (see [Meucci, 2005]):

\[\begin{split}S_{\\mu} = \\frac{\\nu_1}{T_1 (\\nu_1 - 2)} \\Sigma_1.\end{split}\]

Use pyvallocation.bayesian.RobustBayesPosterior to access S_mu and its horizon-scaled variants (log or simple returns) for robust optimisation.

class pyvallocation.bayesian.NIWParams(T1, mu1, nu1, sigma1)[source]

Bases: object

A container for the parameters of a Normal-Inverse-Wishart (NIW) posterior distribution. :no-inheritance:

These parameters are the result of a Bayesian update, combining an NIW prior with market data, as detailed in Meucci (2005). The formulas for these posterior parameters are given in Eqs. (11)-(14).

Parameters:
  • T1 (int)

  • mu1 (numpy.typing.NDArray.numpy.floating | pandas.Series.numpy.floating)

  • nu1 (int)

  • sigma1 (numpy.typing.NDArray.numpy.floating | pandas.DataFrame.numpy.floating)

T1: int
mu1: numpy.typing.NDArray.numpy.floating | pandas.Series.numpy.floating
nu1: int
sigma1: numpy.typing.NDArray.numpy.floating | pandas.DataFrame.numpy.floating
class pyvallocation.bayesian.NIWPosterior(prior_mu, prior_sigma, t0, nu0)[source]

Bases: object

Computes and manages Normal-Inverse-Wishart (NIW) posterior parameters.

This class implements the Bayesian update rules for an NIW distribution, which is the conjugate prior for a multivariate normal likelihood with unknown mean and covariance. The methodology follows Section 3 of Meucci (2005). It provides methods to calculate posterior parameters, classical-equivalent estimators, and factors used in robust Bayesian asset allocation.

The model assumes that asset returns are independently and identically distributed according to a normal distribution. The investor’s prior knowledge is modeled as an NIW distribution.

How to Use:

  1. Initialize the NIWPosterior object with prior parameters:

    • prior_mu (\(\mu_0\)): The prior estimate for the mean vector.

    • prior_sigma (\(\Sigma_0\)): The prior scale matrix for the covariance.

    • t0 (\(T_0\)): The confidence in prior_mu, expressed as a

      pseudo-count of observations.

    • nu0 (\(\nu_0\)): The confidence in prior_sigma, expressed as a

      pseudo-count of observations.

  2. Call the update() method with sample statistics from observed data:

    • sample_mu (\(\hat{\mu}\)): The mean vector from the data.

    • sample_sigma (\(\hat{\Sigma}\)): The covariance matrix from the data.

    • n_obs (\(T\)): The number of observations in the data sample.

  3. The update() method returns an NIWParams object with the posterior parameters (\(T_1, \mu_1, \nu_1, \Sigma_1\)), which are a blend of the prior and the market data.

  4. Use accessor methods like get_mu_ce(), get_S_mu(), etc., to retrieve various quantities derived from the posterior distribution.

Parameters:
  • prior_mu (Union[npt.NDArray[np.floating], 'pd.Series[np.floating]'])

  • prior_sigma (Union[npt.NDArray[np.floating], 'pd.DataFrame[np.floating]'])

  • t0 (int)

  • nu0 (int)

prior_mu

The prior mean vector (\(\mu_0\)).

prior_sigma

The prior scale matrix (\(\Sigma_0\)).

t0

The prior pseudo-count for the mean (\(T_0\)).

nu0

The prior pseudo-count for the covariance (\(\nu_0\)).

N

The number of assets.

_asset_index

Stores pandas.Index if pandas objects are used.

_posterior

Stores the computed posterior parameters.

cred_radius_mu(p_mu)[source]

Computes the credibility factor \(\gamma_\mu\) for the mean’s uncertainty.

This factor, \(\gamma_\mu\), appears in the simplified robust mean-variance optimization problem (Eq. 19). It scales the portfolio’s posterior standard deviation to penalize for estimation risk in the mean vector. Its formula is given by Eq. (20):

\[\gamma_\mu = \sqrt{ \frac{q_\mu^2}{T_1} \frac{\nu_1}{\nu_1 - 2} }\]

where \(q_\mu^2 = Q_{\chi^2_N}(p_{mu})\) is the squared radius factor from the chi-square distribution.

Parameters:

p_mu – The confidence level for \(\mu\) (0 < p_mu < 1), which reflects aversion to estimation risk.

Returns:

The credibility factor \(\gamma_\mu\).

Raises:
  • RuntimeError – If posterior parameters have not been computed.

  • ValueError – If \(\nu_1 \le 2\) or p_mu is not in (0,1).

Parameters:

p_mu (float)

Return type:

float

cred_radius_sigma_factor(p_sigma)[source]

Computes the scaling factor for the worst-case portfolio variance.

In the robust framework, the maximum possible variance of a portfolio within the uncertainty ellipsoid for \(\Sigma\) is not simply \(w'\Sigma_1 w\), but a scaled version of it. This method computes that scaling factor, which we can call \(C_\Sigma\). The derivation is shown in Appendix 7.2, and the final result is presented in the maximization step in Eq. (47):

\[\max_{\Sigma \in \Theta_\Sigma} w'\Sigma w = \underbrace{ \left[ \frac{\nu_1}{\nu_1 + N + 1} + \sqrt{\frac{2\nu_1^2 q_\Sigma^2}{(\nu_1 + N + 1)^3}} \right] }_{C_\Sigma} (w'\Sigma_1 w)\]

where \(q_\Sigma^2 = Q_{\chi^2_{dof}}(p_\Sigma)\) with \(dof = N(N+1)/2\).

Parameters:

p_sigma – The confidence level for \(\Sigma\) (0 < p_sigma < 1), reflecting aversion to estimation risk.

Returns:

The credibility factor \(C_\Sigma\) for scaling the portfolio variance.

Raises:
  • RuntimeError – If posterior parameters have not been computed.

  • ValueError – If p_sigma is not in (0,1) or internal terms are invalid.

Parameters:

p_sigma (float)

Return type:

float

get_S_mu()[source]

Computes the scatter matrix \(S_{\mu}\) for the posterior of \(\mu\).

This matrix describes the dispersion of the marginal posterior distribution of \(\mu\) and is used to define the location-dispersion ellipsoid for robust optimization. It is defined in Eq. (16):

\[S_{\mu} = \frac{1}{T_1} \frac{\nu_1}{\nu_1 - 2} \Sigma_1\]

This computation requires the posterior degrees of freedom \(\nu_1 > 2\).

Returns:

The scatter matrix \(S_{\mu}\) as a NumPy array or pandas DataFrame.

Raises:
  • RuntimeError – If posterior parameters have not been computed.

  • ValueError – If \(\nu_1 \le 2\), as the scatter matrix is not defined.

Return type:

Union[npt.NDArray[np.floating], ‘pd.DataFrame[np.floating]’]

get_mu_ce()[source]

Computes the classical-equivalent estimator for the mean, \(\hat{\mu}_{ce}\).

For the NIW model, this estimator is the posterior mean \(\mu_1\), as defined in Eq. (15).

\[\hat{\mu}_{ce} = \mu_1\]
Returns:

The posterior mean vector \(\mu_1\) as a NumPy array or pandas Series.

Raises:

RuntimeError – If posterior parameters have not been computed via update().

Return type:

Union[npt.NDArray[np.floating], ‘pd.Series[np.floating]’]

get_sigma_ce()[source]

Computes the classical-equivalent estimator for the covariance, \(\hat{\Sigma}_{ce}\).

This estimator is a shrunk version of the posterior scale matrix \(\Sigma_1\), as defined in Eq. (17). It serves as the center of the uncertainty ellipsoid for \(\Sigma\).

\[\hat{\Sigma}_{ce} = \frac{\nu_1}{\nu_1 + N + 1} \Sigma_1\]
Returns:

The classical-equivalent estimator \(\hat{\Sigma}_{ce}\) as a NumPy array or pandas DataFrame.

Raises:
  • RuntimeError – If posterior parameters have not been computed.

  • ValueError – If \(\nu_1 + N + 1 = 0\), which is highly unlikely with valid inputs.

Return type:

Union[npt.NDArray[np.floating], ‘pd.DataFrame[np.floating]’]

property is_updated: bool

Whether the posterior has been computed via update().

update(sample_mu, sample_sigma, n_obs)[source]

Updates the posterior parameters using sample statistics.

This method implements the Bayesian update rules for the NIW parameters as given by Eqs. (11)-(14) in Meucci (2005).

\[\begin{split}\begin{align*} T_1 &= T_0 + T \\ \mu_1 &= \frac{T_0\mu_0 + T\hat{\mu}}{T_1} \\ \nu_1 &= \nu_0 + T \\ \Sigma_1 &= \frac{1}{\nu_1} \left[ \nu_0\Sigma_0 + T\hat{\Sigma} + \frac{(\mu_0 - \hat{\mu})(\mu_0 - \hat{\mu})'}{\frac{1}{T} + \frac{1}{T_0}} \right] \end{align*}\end{split}\]

The resulting posterior parameters blend the investor’s prior with information from the market, with the balance determined by the relative confidence levels (\(T_0, \nu_0\)) versus the amount of data (\(T\)).

Parameters:
  • sample_mu – 1D array (length N) of sample means (\(\hat{\mu}\)).

  • sample_sigma – 2D array (N x N) of the sample covariance matrix (\(\hat{\Sigma}\)).

  • n_obs – The number of observations in the sample (\(T\)).

Returns:

A NIWParams instance with the updated posterior parameters. If pandas objects were used in initialization, returns \(\mu_1\) as a Series and \(\Sigma_1\) as a DataFrame.

Raises:

ValueError – If sample statistics have inconsistent shapes or n_obs is not a positive integer.

Parameters:
  • sample_mu (Union[npt.NDArray[np.floating], 'pd.Series[np.floating]'])

  • sample_sigma (Union[npt.NDArray[np.floating], 'pd.DataFrame[np.floating]'])

  • n_obs (int)

Return type:

NIWParams

class pyvallocation.bayesian.RobustBayesPosterior(mu, sigma, s_mu, _nu1=None, _n_assets=None, _T1=None)[source]

Bases: object

Bundle NIW posterior moments with mean-uncertainty helpers.

The Normal-Inverse-Wishart (NIW) posterior implies a covariance of the mean \(S_\mu\) given by [Meucci, 2005]:

\[S_\mu = \frac{\nu_1}{T_1 (\nu_1 - 2)} \Sigma_1.\]

This object exposes \(S_\mu\) and convenience transforms into horizon-scaled log- and simple-return units, which are required by robust optimisation routines that penalise mean uncertainty.

Key fields:

mu: Posterior mean vector (same units as inputs). sigma: Posterior covariance matrix (same units as inputs). s_mu: Posterior covariance of the mean (\(S_\mu\)).

Parameters:
  • mu (Union[npt.NDArray[np.floating], pd.Series])

  • sigma (Union[npt.NDArray[np.floating], pd.DataFrame])

  • s_mu (Union[npt.NDArray[np.floating], pd.DataFrame])

  • _nu1 (Optional[float])

  • _n_assets (Optional[int])

  • _T1 (Optional[float])

cred_radius_mu(p_mu=0.75)[source]

Credibility radius for the mean uncertainty ellipsoid (Meucci Eq. 9.156).

Parameters:

p_mu – Probability level for the chi-square quantile. Defaults to 0.75.

Returns:

float – Credibility factor gamma_mu.

Raises:

ValueError – If NIW parameters are not available.

Parameters:

p_mu (float)

Return type:

float

cred_radius_sigma_factor(p_sigma=0.75)[source]

Scaling factor for worst-case variance under Sigma uncertainty (Meucci Eq. 9.157).

Parameters:

p_sigma – Probability level for the chi-square quantile. Defaults to 0.75.

Returns:

float – Scaling factor C_sigma.

Raises:

ValueError – If NIW parameters are not available.

Parameters:

p_sigma (float)

Return type:

float

classmethod from_niw(*, prior_mu, prior_sigma, t0, nu0, sample_mu, sample_sigma, n_obs)[source]

Construct the posterior bundle from NIW inputs.

Parameters:
  • prior_mu – Prior mean vector.

  • prior_sigma – Prior covariance matrix.

  • t0 – Prior strength for the mean.

  • nu0 – Prior degrees of freedom for the covariance.

  • sample_mu – Sample mean vector.

  • sample_sigma – Sample covariance matrix.

  • n_obs – Number of observations.

Returns:

RobustBayesPosterior – Posterior bundle with mean uncertainty.

Parameters:
  • prior_mu (Union[npt.NDArray[np.floating], pd.Series])

  • prior_sigma (Union[npt.NDArray[np.floating], pd.DataFrame])

  • t0 (int)

  • nu0 (int)

  • sample_mu (Union[npt.NDArray[np.floating], pd.Series])

  • sample_sigma (Union[npt.NDArray[np.floating], pd.DataFrame])

  • n_obs (int)

Return type:

RobustBayesPosterior

mean_uncertainty_cov_log(*, annualization_factor=1.0)[source]

Return mean-uncertainty covariance in log-return units.

The mean uncertainty scales with the square of the horizon:

\[S_{\mu,\,h} = h^2\, S_\mu.\]
Parameters:

annualization_factor (float)

Return type:

Union[npt.NDArray[np.floating], pd.DataFrame]

mean_uncertainty_cov_simple(*, annualization_factor=1.0, exact=False)[source]

Return mean-uncertainty covariance in simple-return units.

Assumes mu and s_mu are expressed in log-return units. The method annualises and applies a delta approximation to map to simple returns:

\[r \approx \exp(\mu_g) - 1, \quad S_{\mu,r} \approx J\,S_{\mu,g}\,J^{\top}, \quad J = \operatorname{diag}(\exp(\mu_g)).\]
Parameters:
  • annualization_factor – Horizon scaling factor (e.g. 12 for monthly -> annual).

  • exact – When True, use the exact delta-method Jacobian J = diag(exp(mu_g + 0.5 * diag(Sigma_g))) which accounts for the Jensen inequality correction. Default False uses the simpler J = diag(exp(mu_g)).

Parameters:
  • annualization_factor (float)

  • exact (bool)

Return type:

Union[npt.NDArray[np.floating], pd.DataFrame]

mu: numpy.typing.NDArray.numpy.floating | pandas.Series
s_mu: numpy.typing.NDArray.numpy.floating | pandas.DataFrame
sigma: numpy.typing.NDArray.numpy.floating | pandas.DataFrame
property sigma1: npt.NDArray[np.floating] | pd.DataFrame

Return the raw posterior scale matrix Sigma_1 (Meucci Eq. 7.31).

sigma stores the classical-equivalent Sigma_ce = nu1/(nu1+N+1) * Sigma_1. This property recovers Sigma_1 = (nu1+N+1)/nu1 * Sigma_ce for use in the robust Bayesian formulation (Meucci Eq. 9.155).

Returns:

Posterior scale matrix Sigma_1.

Raises:

ValueError – If the posterior was not constructed via from_niw.

pyvallocation.bayesian.chi2_quantile(p, dof, sqrt=False)[source]

Computes the quantile of the chi-square (chi^2) distribution.

This function is used to determine the size of the uncertainty ellipsoids for the market parameters (mean and covariance). The size is determined by a radius factor, \(q\), which is set according to a quantile of the chi-square distribution.

For the mean vector \(\mu\), under the assumption that its posterior distribution is normal, the squared Mahalanobis distance is chi-square distributed. The radius factor squared, \(q_\mu^2\), is set using a quantile of the \(\chi^2_N\) distribution.

\[q_\mu^2 = Q_{\chi_N^2}(p_\mu)\]

For the covariance matrix \(\Sigma\), a similar approach is used based on a heuristic argument that the Mahalanobis distance behaves like a \(\chi^2\) distribution with \(N(N+1)/2\) degrees of freedom .

\[q_\Sigma^2 = Q_{\chi_{N(N+1)/2}^2}(p_\Sigma)\]
Parameters:
  • p – The probability level (0 < p < 1) for the quantile.

  • dof – The degrees of freedom for the chi-square distribution.

  • sqrt – If True, returns the square root of the quantile. Defaults to False.

Returns:

The chi-square quantile \(Q_{\chi^2}(p)\) or \(\sqrt{Q_{\chi^2}(p)}\) if sqrt is True.

Raises:

ValueError – If p is not strictly between 0 and 1.

Parameters:
  • p (float)

  • dof (int)

  • sqrt (bool)

Return type:

float