Asset Panel Estimators: private API
PortfolioOptimisers.AbstractPhylogenyFeatureAlgorithm — Type
abstract type AbstractPhylogenyFeatureAlgorithm <: AbstractAlgorithmAbstract 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
PortfolioOptimisers._proximity_features — Function
_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
- Probe the decay over
0:dmaxthroughassert_separation_decay, before the pair loop. - Allocate
Z, of the element typeetand the size ofd, filled with zeros. - For each pair, read its separation
duvand askis_relatedwhether it is inside the budget, givingrel. This is the sentinel test as well as the budget rule, so it is the guard on the next step. - Where
relholds, writeseparation_decay(dk, duv, dmax)intoZ[u, v]. Where it does not, leave the zero of step 2.
Arguments
alg: Proximity algorithm carrying the decay.sep: Separation algorithm the matrix was measured under, forwarded tois_related.d: Separation matrix fromseparation_matrix.dmax: Separation budget fromseparation_budget.et: Element type of the result.phylogeny_featurespasseseltype(X), so thatAngularDistkeeps its BLASgemmpath.
Returns
Z::Matrix: Squareassets × assetsfeature matrix.
Related
PortfolioOptimisers.panel_axis_labels — Function
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.
namesisnothing: return the positional labels.namesis a vector: return it asStrings when it holdsnof them, and the positional labels otherwise.
Arguments
names: The carrier's names for the axis, ornothing.n: Length of the axis to label.
Returns
labels::Vector{String}: One label per trailing-axis entry.
Related
PortfolioOptimisers.carrier_asset_names — Function
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, ornothing.
Returns
nx::Option{<:VecStr}: The asset names, ornothing.
Related
PortfolioOptimisers.regression_factor_names — Function
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) -> nothingRead 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
Regressionnames no factor as data, so the names are the carrier'snf, read only where the loadings axis is the carrier's factor axis: aRegressionwhoseLis unset, whose loadings are therefore the rawM, one column per factor the carrier holds. A reduced or re-basedLhas its own axis, which no data names. - A
CrossSectionalFactorModelnames its own factors: the prior derives the raw axis from its Exposure Estimators and stores it asnfon the block, andcs_diagnostic_factor_namesmaps that list onto the reduced axis when the block carries a family re-basis, which is the axisLspans. 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, ornothing.
Returns
nf::Option{<:VecStr}: The factor names, ornothing.
Related
PortfolioOptimisers.expand_investable_loadings — Function
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.
imskisnothing, or every asset is investable: the loadings are the full universe's, and the field carries no mask.- Otherwise allocate a zero frame in
eltype(L)over every asset, writeLon the mask's rows, and build the observed mask astrueon those rows andfalseelsewhere.
Arguments
L: The loadings on the Investable Mask,investable assets × factors.imsk: The prior's Investable Mask over every asset, ornothing.
Returns
vals::MatNum: The loadings over every asset.omsk::Option{<:AbstractMatrix{Bool}}: The observed mask of the same shape, ornothingwhen every asset is investable.
Related
PortfolioOptimisers.assert_producer_prior — Function
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
isa(pr, AbstractPriorResult). Raises anIsNothingError.
Returns
nothing.
Related