Factor Prior

PortfolioOptimisers.FactorPriorType
struct FactorPrior{__T_pe, __T_mp, __T_re, __T_ve, __T_rsd, __T_cache} <: AbstractLowOrderPriorEstimator_F

Factor-based prior estimator for asset returns.

FactorPrior is a low order prior estimator that computes the mean and covariance of asset returns using a factor model. It combines a factor prior estimator, matrix post-processing, regression, and variance estimation to produce posterior moments. Optionally, it can add residual variance to the posterior covariance for robust estimation.

Fields

  • pe: Prior estimator.
  • mp: Matrix processing estimator.
  • re: Regression estimator.
  • ve: Variance estimator.
  • rsd: Whether to include residual variance in the posterior covariance.
  • cache: Optional partial-fit state. It is nothing until partial_fit! writes one, and the estimator's read-out verb reads it when the caller gives no data matrix. Each propagation channel does one thing with it: factory carries it unchanged, because a factory call resolves configuration rather than the sample; port_opt_view slices it to the selected assets by index copy, so the viewed estimator answers over those assets alone; and obs_weights_view drops it, because no slice of a state exists on the observation axis. A family whose state has no exact asset slice drops it on both axes and names the reason.

Constructors

FactorPrior(;    pe::AbstractLowOrderPriorEstimator_A_AF = EmpiricalPrior(),    mp::AbstractMatrixProcessingEstimator = MatrixProcessing(),    re::AbstractTimeSeriesRegressionEstimator = StepwiseRegression(),    ve::AbstractVarianceEstimator = SimpleVariance(),    rsd::Bool = true,    cache::Option{<:AbstractPartialFitState} = nothing) -> FactorPrior

Keywords correspond to the struct's fields.

Propagated parameters

When factory is called on this type, the following @fprop-tagged fields are automatically propagated:

  • pe: Recursively updated via factory.
  • re: Recursively updated via factory.
  • ve: Recursively updated via factory.

View parameters

When port_opt_view is called on this type, the following @vprop-tagged fields are automatically subset to the selected indices:

Composition: what this estimator forwards

This estimator lifts a factor-axis prior onto the asset axis, reconstructing X as F * transpose(M) .+ transpose(b), so it builds its carrier directly rather than forwarding one along its own axis; the forwarding rule still governs each field. It is the plain projection of the family — nothing here modifies the factor distribution, so FactorBlackLittermanPrior is this estimator with views landing on the factor block on the way through.

  • The factor block fpr is the wrapped factor prior, forwarded whole and untouched: it needs no reconstruction, because the asset moments are its projection rather than an update of it.
  • mu and sigma are that block projected through the loadings, so the returned carrier is internally consistent: mu == rr.M * fpr.mu + rr.b holds by construction. sigma optionally gains a residual correction when rsd is true.
  • chol is not forwarded but rebuilt on the asset axis, as M * cholesky(fpr.sigma).L widened by the residual block when rsd is true, so it stays in sync with the sigma it factorises.
  • w is the factor prior's, and is over the right axis: this estimator wraps only a factor prior, and posterior_X has exactly F's rows, so it is the only weighting in existence. Its ens, kld and ow travel with it.

Examples

julia> FactorPrior()FactorPrior   pe ┼ EmpiricalPrior      │           ce ┼ PortfolioOptimisersCovariance      │              │   ce ┼ Covariance      │              │      │    me ┼ SimpleExpectedReturns      │              │      │       │   w ┴ nothing      │              │      │    ce ┼ GeneralCovariance      │              │      │       │   ce ┼ StatsBase.SimpleCovariance: StatsBase.SimpleCovariance(true)      │              │      │       │    w ┴ nothing      │              │      │   alg ┼ FullMoment()      │              │      │     w ┴ nothing      │              │   mp ┼ MatrixProcessing      │              │      │     pdm ┼ Posdef      │              │      │         │      alg ┼ UnionAll: NearestCorrelationMatrix.Newton      │              │      │         │   kwargs ┴ @NamedTuple{}: NamedTuple()      │              │      │      dn ┼ nothing      │              │      │      dt ┼ nothing      │              │      │     alg ┼ nothing      │              │      │   order ┴ NTuple{4, Symbol}: (:pdm, :dn, :dt, :alg)      │           me ┼ SimpleExpectedReturns      │              │   w ┴ nothing      │      horizon ┼ nothing      │   fill_limit ┴ nothing   mp ┼ MatrixProcessing      │     pdm ┼ Posdef      │         │      alg ┼ UnionAll: NearestCorrelationMatrix.Newton      │         │   kwargs ┴ @NamedTuple{}: NamedTuple()      │      dn ┼ nothing      │      dt ┼ nothing      │     alg ┼ nothing      │   order ┴ NTuple{4, Symbol}: (:pdm, :dn, :dt, :alg)   re ┼ StepwiseRegression      │   crit ┼ PValue      │        │   t ┴ Float64: 0.05      │    alg ┼ ForwardSelection()      │    tgt ┼ LinearModel      │        │   kwargs ┴ @NamedTuple{}: NamedTuple()   ve ┼ SimpleVariance      │          me ┼ SimpleExpectedReturns      │             │   w ┴ nothing      │           w ┼ nothing      │   corrected ┴ Bool: true  rsd ┴ Bool: true

The incremental fit

This prior has no exact incremental recursion, so it takes the online step by refitting from a sample buffer: Online seeds cache, partial_fit! appends each observation to it verbatim, and the one-argument prior runs this estimator's own batch verb over the rows the buffer kept. The answer is therefore exactly a batch fit over those rows, and a max_history on the wrapper windows the whole fit.

cache travels the three propagation channels as every partial-fit state does: factory carries it unchanged, port_opt_view slices it to the selected assets, and obs_weights_view drops it, because no slice of a state exists on the observation axis. It is not rendered, because a running buffer is not the configuration a reader looks the type up for.

Related

References

  • [5] D. Cajas. Advanced Portfolio Optimization: A Cutting-edge Quantitative Approach (Springer Nature Switzerland, 2025). Section 4.1, Equations 4.4 and 4.5.
  • [73] J. Fan, Y. Fan and J. Lv. High dimensional covariance matrix estimation using a factor model. Journal of Econometrics 147, 186–197 (2008).
source
PortfolioOptimisers.priorFunction
prior(pe::FactorPrior, X::MatNum, F::MatNum, pnl::Option{<:AssetPanel} = nothing;
      dims::Int = 1, strict::Bool = false, kwargs...)

Compute factor-based prior moments for asset returns using a factor model.

prior estimates the mean and covariance of asset returns using the specified factor prior estimator, regression, and matrix post-processing. The factor returns matrix F is used to compute factor moments, which are then mapped to asset space via regression. Optionally, residual variance is added to the posterior covariance for robust estimation. The result is returned as a LowOrderPrior object.

Mathematical definition

The factor model maps factor moments to asset space via the loadings matrix $\mathbf{B}$ (with intercepts $\boldsymbol{\alpha}$):

\[\begin{align} \hat{\boldsymbol{\mu}} &= \mathbf{B} \hat{\boldsymbol{f}} + \boldsymbol{\alpha}\,, \\ \hat{\mathbf{\Sigma}} &= \mathbf{B} \mathbf{\Sigma}_f \mathbf{B}^\intercal + \mathbf{\Sigma}_\varepsilon\,. \end{align}\]

Where:

  • $\mathbf{B}$: $N \times K$ factor loadings matrix, rr.M.
  • $\hat{\boldsymbol{f}}$: $K \times 1$ vector of factor expected returns.
  • $\boldsymbol{\alpha}$: $N \times 1$ vector of regression intercepts, rr.b.
  • $\mathbf{\Sigma}_f$: $K \times K$ factor covariance matrix.
  • $\mathbf{\Sigma}_\varepsilon$: $N \times N$ diagonal matrix of residual variances (when rsd = true).

The factor moments $\hat{\boldsymbol{f}}$ and $\mathbf{\Sigma}_f$ come from pe.pe fit on F, and the loadings from pe.re fit on (X, F). The two equations are factor_lift.

Algorithm

  1. Orient X and F with dims_oriented, to observations × assets and observations × factors.
  2. Fit the wrapped prior pe.pe on F, giving f_prior, the factor-axis prior result. strict reaches it, because pe.pe admits BlackLittermanPrior and EntropyPoolingPrior, which resolve view names against a universe.
  3. Fit the loadings and rebuild the asset returns with factor_reconstruction, giving rr and posterior_X.
  4. Project f_prior.mu and f_prior.sigma through rr with factor_lift, giving mu, sigma, chol and esigma.
  5. Write esigma onto the esigma field of rr. Under pe.rsd = true the field holds the residual variances the lift measured, and under pe.rsd = false it holds nothing, because the lift added no residual block.
  6. Assemble a LowOrderPrior over posterior_X, with the oriented X under o_X, the three lifted moments, the factor prior's w, ens, kld and ow, the regression result under rr, and f_prior itself under fpr. No Z is carried; the composition note of FactorPrior says why.

Arguments

  • pe: Factor prior estimator.
  • X: Asset returns matrix (observations × assets).
  • F: Factor returns matrix (observations × factors).
  • pnl: Optional AssetPanel, the panel the carrier held. A wrapping prior forwards it unchanged, so that it can compose an estimator that is fitted on a panel. An estimator that reads no panel ignores it. The prior this estimator nests is fitted on the factors, whose axis no panel describes, so the panel stops here.
  • dims: Dimension along which to perform the computation.
  • strict: Whether to throw an error if sets does not contain the desired value in sets.dict[key].
  • kwargs...: Additional keyword arguments passed to matrix processing and estimators.

Validation

  • dims in (1, 2).

Returns

  • pr::LowOrderPrior: Result object containing posterior asset returns, mean vector, covariance matrix, Cholesky factor, regression result, and factor moments.

Related

source

References

[5]
D. Cajas. Advanced Portfolio Optimization: A Cutting-edge Quantitative Approach (Springer Nature Switzerland, 2025).
[73]
J. Fan, Y. Fan and J. Lv. High dimensional covariance matrix estimation using a factor model. Journal of Econometrics 147, 186–197 (2008).