Asset Panel Estimators: private API

PortfolioOptimisers.AbstractPhylogenyFeatureAlgorithmType
abstract type AbstractPhylogenyFeatureAlgorithm <: AbstractAlgorithm

Abstract supertype for all phylogeny feature algorithms.

A phylogeny feature algorithm is the rule turning the structure a PhylogenyPanel source describes into an assets × assets proximity matrix, Z[i, k] = f(separation(i, k)). The family is open: a user needing a different rule defines a member and a phylogeny_features method for it.

Two neighbouring choices need neither. A different fall-off is an AbstractSeparationDecayAlgorithm, which Proximity carries as a field; a different notion of far is an AbstractSeparationAlgorithm, which the source NetworkEstimator carries as sep. Between them those two knobs span every neighbourhood rule the family has needed so far, which is why exactly one member ships.

One member is an extension point, not a taxonomy. The type exists so that a rule which is not a decayed separation — a role-similarity matrix, say, or a rule reading structure the separation kernels do not expose — has a place to dispatch from. It is not a partition of anything, and nothing infers a second member's existence from the first.

Every member includes self, so f(0) is the top of its scale — see PhylogenyPanel for why the diagonal is load-bearing rather than cosmetic.

Related

source
PortfolioOptimisers._proximity_featuresFunction
_proximity_features(
    alg::Proximity,
    sep::AbstractSeparationAlgorithm,
    d::AbstractMatrix{<:Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}},
    dmax::Number,
    et::Type
) -> Matrix

Score a separation matrix under a decay, inside a budget.

The loop behind phylogeny_features's Proximity method, split out because it is a function of the separations alone: the structure, the estimator and the data are all spent by the time it runs. Handing it a matrix is how the unreachable branch is tested — every structure a shipped estimator builds is connected, so a disconnected one arrives as an argument rather than through a test double that answers calc_adjacency.

Algorithm

  1. Probe the decay over 0:dmax through assert_separation_decay, before the pair loop.
  2. Allocate Z, of the element type et and the size of d, filled with zeros.
  3. For each pair, read its separation duv and ask is_related whether it is inside the budget, giving rel. This is the sentinel test as well as the budget rule, so it is the guard on the next step.
  4. Where rel holds, write separation_decay(dk, duv, dmax) into Z[u, v]. Where it does not, leave the zero of step 2.

Arguments

Returns

  • Z::Matrix: Square assets × assets feature matrix.

Related

source
PortfolioOptimisers.panel_axis_labelsFunction
panel_axis_labels(names::Nothing, n::Integer) -> Vector{String}
panel_axis_labels(names::VecStr, n::Integer) -> Vector{String}

Name the trailing axis of a produced TensorPanelField, from the carrier or positionally.

A producer's field has a labelled trailing axis, and neither a loadings matrix nor a proximity matrix names that axis as data. So the data carrier is the one place a name exists: rd.nx names a proximity field's assets, and rd.nf names a loadings field's factors. Where the carrier gives no name, or gives the wrong number of them, the labels are positional, "1" to "n".

The count is checked rather than assumed. A reduced or re-based L has fewer columns than the carrier has factors, and a positional label is then the only honest one.

Algorithm

The method that Julia selects is the algorithm.

  1. names is nothing: return the positional labels.
  2. names is a vector: return it as Strings when it holds n of them, and the positional labels otherwise.

Arguments

  • names: The carrier's names for the axis, or nothing.
  • n: Length of the axis to label.

Returns

  • labels::Vector{String}: One label per trailing-axis entry.

Related

source
PortfolioOptimisers.carrier_asset_namesFunction
carrier_asset_names(rd::Nothing) -> nothing
carrier_asset_names(rd::AbstractReturnsResult) -> Option{<:VecStr}

Read the asset names a producer labels its trailing axis with, or nothing.

A producer is handed the two carriers the kernel received. Only the data carrier names the assets, so this is the one read, and it answers nothing when there is no data carrier.

Algorithm

The method that Julia selects is the algorithm.

Arguments

  • rd: The data carrier, or nothing.

Returns

  • nx::Option{<:VecStr}: The asset names, or nothing.

Related

source
PortfolioOptimisers.regression_factor_namesFunction
regression_factor_names(rr::Regression{<:Any, Nothing, <:Any, <:Any},
                        rd::AbstractReturnsResult) -> Option{<:VecStr}
regression_factor_names(rr::CrossSectionalFactorModel, rd) -> Option{<:VecStr}
regression_factor_names(rr, rd) -> nothing

Read the factor names a RegressionPanel labels its loadings axis with, or nothing.

A name is read wherever one exists for the axis pr.rr.L spans, and the axis is labelled positionally by panel_axis_labels otherwise.

  • A time-series Regression names no factor as data, so the names are the carrier's nf, read only where the loadings axis is the carrier's factor axis: a Regression whose L is unset, whose loadings are therefore the raw M, one column per factor the carrier holds. A reduced or re-based L has its own axis, which no data names.
  • A CrossSectionalFactorModel names its own factors: the prior derives the raw axis from its Exposure Estimators and stores it as nf on the block, and cs_diagnostic_factor_names maps that list onto the reduced axis when the block carries a family re-basis, which is the axis L spans. So the names come off the block, and the data carrier is not read.

Algorithm

The method that Julia selects is the algorithm. The Nothing type parameter of L is the raw-M case, which is the same parameter the swap(L, M) rule of Regression fires on.

Arguments

  • rr: The regression result the loadings came from.
  • rd: The data carrier, or nothing.

Returns

  • nf::Option{<:VecStr}: The factor names, or nothing.

Related

source
PortfolioOptimisers.expand_investable_loadingsFunction
expand_investable_loadings(L::MatNum, imsk::Nothing) -> (L, nothing)
expand_investable_loadings(L::MatNum, imsk::BitVector) -> (vals, omsk)

Write the loadings a RegressionPanel read on the Investable Mask back onto the full asset universe.

A prior fitted on a point-in-time Asset Panel writes NaN on the loadings of every asset outside its Investable Mask, and a Panel Field admits no NaN. The producer therefore reads the loadings on the mask and expands them here: an asset outside the mask takes a zero row and a false observed mask, the same rule every uncertainty set fitted standalone on such a prior follows, and the shape a Panel Field already has for a cell a fill policy wrote. A zero row is a zero feature vector, which AngularDist places at distance 1 from every asset that has loadings and 0 from every other asset that has none, and "loadings" => :observed selects the mask as a column. A view of the expanded field at the mask recovers the reduced loadings, so a panel built standalone can be handed to an optimiser on the full universe.

Algorithm

The method that Julia selects is the algorithm.

  1. imsk is nothing, or every asset is investable: the loadings are the full universe's, and the field carries no mask.
  2. Otherwise allocate a zero frame in eltype(L) over every asset, write L on the mask's rows, and build the observed mask as true on those rows and false elsewhere.

Arguments

  • L: The loadings on the Investable Mask, investable assets × factors.
  • imsk: The prior's Investable Mask over every asset, or nothing.

Returns

  • vals::MatNum: The loadings over every asset.
  • omsk::Option{<:AbstractMatrix{Bool}}: The observed mask of the same shape, or nothing when every asset is investable.

Related

source
PortfolioOptimisers.assert_producer_priorFunction
assert_producer_prior(ape::AbstractAssetPanelEstimator, pr::AbstractPriorResult) -> nothing
assert_producer_prior(ape::AbstractAssetPanelEstimator, pr) -> Union{}

Assert that a producer that reads a prior result was handed one, and name the site when it was not.

A producer runs wherever the estimator holding it runs, and one of those sites has no prior by construction: preselection is fitted from the returns data alone, before any prior exists. So does the shortest public call, clusterise(cle, rd), which puts a data carrier in the pr slot. This turns both into a diagnostic that names the site rather than a nothing field access or a MethodError.

Algorithm

The method that Julia selects is the algorithm. A prior result returns; anything else, nothing and a data carrier alike, raises.

Arguments

  • ape: The producer, named in the message.
  • pr: The prior result, or whatever reached the slot.

Validation

Returns

  • nothing.

Related

source