High Order Factor Prior: private API

PortfolioOptimisers.AbstractHiLoOrderPriorEstimator_FType
const AbstractHiLoOrderPriorEstimator_F = Union{<:AbstractLowOrderPriorEstimator_F,
                                                <:AbstractHighOrderPriorEstimator_F}

Groups the two families that require factor returns, one per order.

AbstractHiLoOrderPriorEstimator_F is the type-level half of the test for this estimator cannot run without factor returns, taken across both orders at once: a member answers true to needs_factor_returns. The doors that check for a missing factor matrix ask the predicate rather than this union, because a factor leaf may sit under a host whose own factor argument is optional, and the predicate walks the tree where an isa test reads the host alone.

Related

source
PortfolioOptimisers.coskewness_residualsFunction
coskewness_residuals(X::MatNum, me::AbstractExpectedReturnsEstimator)

Build the residual coskewness matrix a factor lift adds to its projected coskewness.

X is the residual matrix X - posterior_X, not the asset returns. The residuals are independent across assets and have zero mean, so every cross term of their coskewness vanishes and only the N own-third-moment entries survive.

Nothing is demeaned here. The zero-mean assumption is the factor model's, and me supplies the averaging rule rather than a centre. Feed residuals whose mean is not zero and the surviving entries hold the raw third moment $\mathbb{E}[(\varepsilon_i + c_i)^3]$ rather than the central one, so the result is a coskewness only as far as the assumption holds.

Mathematical definition

\[(\hat{\mathbf{M}}_{3,\varepsilon})_{i,\,c} = \begin{cases} \mathbb{E}[\varepsilon_i^3]\,, & c = (i - 1)N + i\,, \\ 0\,, & \text{otherwise.} \end{cases}\]

Where:

  • $\varepsilon_i$: residual of asset $i$, the $i$-th column of X.
  • $N$: Number of assets.
  • $\hat{\mathbf{M}}_{3,\varepsilon}$: the $N \times N^2$ residual coskewness matrix, sk_err.

Column $(i - 1)N + i$ is the one that holds $\varepsilon_i \varepsilon_i$ in a coskewness matrix, so row $i$ meets it at the only entry of that row a set of independent zero-mean residuals can fill.

Algorithm

  1. Take N = size(X, 2) and N2 = N^2.
  2. Cube the residuals entry by entry, giving X3.
  3. Allocate sk_err, an N × N2 sparse zero matrix of the element type of X3.
  4. Take idx, the linear index range 1:(N2 + N + 1):(N2 * N). In an N × N2 matrix stored column by column, its i-th entry addresses (i, (i - 1) * N + i).
  5. Average the columns of X3 under me, and write the N values into idx.

Arguments

  • X: Residual return matrix (observations × assets).
  • me: Expected returns estimator, used to average the cubed residuals.

Returns

  • sk_err::SparseMatrixCSC: N × N² residual coskewness matrix.

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

source
PortfolioOptimisers.cokurtosis_residualsFunction
cokurtosis_residuals(sigma::MatNum, X::MatNum, me::AbstractExpectedReturnsEstimator,
                     ex::FLoops.Transducers.Executor = FLoops.ThreadedEx())

Build the residual cokurtosis matrix a factor lift adds to its projected cokurtosis.

X is the residual matrix X - posterior_X, not the asset returns, and sigma is the systematic covariance $\mathbf{B} \mathbf{\Sigma}_f \mathbf{B}^\intercal$, with any residual block already removed. The caller does that removal; see factor_residual_config.

Nothing is standardised here. Every entry is written in closed form from the second and fourth residual moments e2 = mean(me, X .^ 2) and e4 = mean(me, X .^ 4) together with sigma, under the factor model's assumption that the residuals have zero mean and are independent both of each other and of the factors. Under those assumptions each index pattern collapses to one of the branches in the loop, and the only pattern that is zero is the one whose four indices are all distinct.

Entry (i - 1) * N + k, (j - 1) * N + l of the result is the residual contribution to $\mathbb{E}[r_i r_k r_j r_l]$. The matrix is symmetric, so only the upper triangle is computed and each value is written to both places.

Mathematical definition

Write $r_i = s_i + \varepsilon_i$, with $s_i$ the systematic return and $\varepsilon_i$ the residual. The residual contribution is $\mathbb{E}[r_i r_k r_j r_l] - \mathbb{E}[s_i s_k s_j s_l]$, and it depends on the four indices only through the pattern of their coincidences:

\[(\hat{\mathbf{\Sigma}}_{4,\varepsilon})_{(i-1)N+k,\;(j-1)N+l} = \begin{cases} 6 e_{2,a} \mathbf{\Sigma}_{aa} + e_{4,a}\,, & i = k = j = l = a\,, \\ 3 e_{2,a} \mathbf{\Sigma}_{ab}\,, & \text{three indices are } a \text{ and the fourth is } b\,, \\ e_{2,a} \mathbf{\Sigma}_{bb} + e_{2,b} \mathbf{\Sigma}_{aa} + e_{2,a} e_{2,b}\,, & \text{two pairs, } a \text{ and } b\,, \\ e_{2,a} \mathbf{\Sigma}_{bc}\,, & \text{one pair } a \text{, and singles } b \text{ and } c\,, \\ 0\,, & \text{all four distinct.} \end{cases}\]

Where:

  • $e_{2,i} = \mathbb{E}[\varepsilon_i^2]$, $e_{4,i} = \mathbb{E}[\varepsilon_i^4]$: the second and fourth residual moments, e2 and e4.
  • $\mathbf{\Sigma}$: the systematic covariance, sigma.
  • $N$: Number of assets.
  • $\hat{\mathbf{\Sigma}}_{4,\varepsilon}$: the $N^2 \times N^2$ residual cokurtosis matrix, kt_res.

A single $\varepsilon$ factor averages to zero and a lone $s$ factor is centred, which is what removes the odd terms. Four distinct indices leave no $\varepsilon$ paired with itself, so that case alone vanishes; a pattern with a pair and two singles does not, because the pair contributes $e_{2,a}$ and the two singles contribute their systematic covariance.

Algorithm

  1. Take N = size(X, 2) and N2 = N^2.
  2. Square and fourth-power the residuals entry by entry, giving X2 and X4.
  3. Average the columns of each under me, giving e2 and e4.
  4. Allocate kt_res, of size (N2, N2), in the promotion of the element types of e4 and sigma.
  5. Run ex over the N2 column pairs (j, l). For each column, walk the row pairs (i, k) and skip the pair when row > col, so only the upper triangle is visited.
  6. Select the value val for (i, k, j, l) through the branch chain of the closed form above, ordered so the most common patterns are tested first.
  7. Write val to kt_res[row, col] and to kt_res[col, row].

The executor changes the order in which the columns are visited and nothing else: each column writes its own entries, so FLoops.SequentialEx() and FLoops.ThreadedEx() give bit-identical results.

Arguments

  • sigma: Systematic covariance matrix, N × N.
  • X: Residual return matrix (observations × assets).
  • me: Expected returns estimator, used to average the squared and the fourth-power residuals.
  • ex: FLoops executor for the loop. Defaults to FLoops.ThreadedEx().

Returns

  • kt_res::Matrix: N² × N² residual cokurtosis matrix.

Related

source
PortfolioOptimisers.show_fieldsMethod
show_fields(
    _::HighOrderFactorPriorEstimator
) -> NTuple{5, Symbol}

Renders every field of a HighOrderFactorPriorEstimator except cache.

The state a cache holds is the running detail of an incremental fit, not the configuration a reader looks the type up for, and it prints under the estimator at every site that renders one. Set set_show_nothing_fields!(:HighOrderFactorPriorEstimator, true) to render it.

Arguments

  • ::HighOrderFactorPriorEstimator: Prior estimator, read for its type alone.

Returns

  • fields::Tuple: The field names to render, which is (:pe, :kte, :ske, :ex, :rsd).

Related

source