Plotting: private API

PortfolioOptimisers.relevant_assetsFunction
relevant_assets(
    w::AbstractVector{<:Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}},
    M::Integer
) -> Tuple{Any, Any}
relevant_assets(
    w::AbstractVector{<:Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}},
    M::Integer,
    N_opt::Union{Nothing, Number}
) -> Tuple{Any, Any}

Select the top-N assets from a weight vector by absolute weight magnitude.

The ranking is taken over finite_magnitudes, so a non-investable asset ranks below every live one and counts for nothing. The frame is unchanged when the count does not truncate, which is the drawn plots' rule.

Arguments

  • w::VecNum: Portfolio weight vector.
  • M::Integer: Total number of assets (upper bound for N).
  • N_opt::Option{<:Number} = nothing: Asset-count specification. nothing auto-selects via number_effective_assets.

Returns

  • Tuple{Int, Vector{Int}}: (N, idx) where N is the number of selected assets and idx is a permutation vector sorted descending by |w|, with a non-finite entry last.

Details

  • When N_opt is nothing, N_eff = number_effective_assets(finite_magnitudes(w)).
  • When 0 < N_eff ≤ 1, N_eff is treated as a concentration threshold: N is the smallest index such that the cumulative normalised absolute weight covers at least 1 - N_eff of the total; falls back to M if no such index exists.
  • Otherwise N_eff is treated as a count: N = clamp(ceil(Int, N_eff), 1, M).

Related

source
PortfolioOptimisers.finite_magnitudesFunction
finite_magnitudes(
    v::AbstractVector{<:Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}}
) -> Any

Take the ranking magnitudes of a per-asset vector, with a non-finite entry ranked last.

A figure that shows the top assets sorts by the size of a per-asset number. A non-investable asset carries NaN in that number, and NaN sorts first under rev = true, so a blank bar would take the top slot from a live asset. The same NaN poisons every reduction the ranking needs: inv(dot(v, v)) and sum(v) are both NaN, and ceil(Int, NaN) throws an InexactError.

Mapping a non-finite entry to zero settles all three. The entry ranks below every live asset, it counts for nothing, and the frame is unchanged when the count does not truncate. A vector whose entries are all finite is untouched, because the ranking already read abs.

Arguments

  • v: A per-asset number the figure ranks by, such as an expected return, a volatility, a centrality score or a weight.

Returns

  • m::Vector: abs(vᵢ) at every finite entry, and zero at every other.

Related

source
PortfolioOptimisers.finite_symmetric_climFunction
finite_symmetric_clim(
    A::AbstractMatrix{<:Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}}
) -> Tuple{Any, Any}

Take the symmetric colour limits of a heatmap over its finite entries alone.

maximum(abs, A) is NaN when any entry of A is, and a NaN colour limit paints every cell one colour. A drawn plot of a Prior Result keeps the full universe, so it does meet a NaN: a non-investable asset carries one down its row and its column. The limits are therefore taken over the finite entries, and the blank cell is drawn against the scale the live cells set.

Arguments

  • A: The matrix the heatmap draws.

Returns

  • clim::Tuple: (-c, c), where c is the largest finite absolute entry of A.

Related

source
PortfolioOptimisers.finite_columnsFunction
finite_columns(
    X::AbstractMatrix{<:Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}},
    idx::AbstractVector{<:Integer}
) -> Vector

Keep the columns of idx whose column of X is finite throughout.

A figure that draws one line per asset keeps a gapped asset, and the break in its line is the delisting. A figure that sums several assets into one aggregate line cannot: a single NaN in the sum poisons every date of it, at any weight, the zero an optimiser gave a non-investable asset included. Such a series draws nothing at all, so the aggregate excludes the asset instead of carrying it.

The rule reads the column rather than a mask, because the aggregate is formed from a returns matrix the caller holds and no prior travels with it.

Arguments

  • X: Asset returns matrix (observations × assets).
  • idx: Column indices the aggregate would sum.

Returns

  • keep::VecInt: The entries of idx whose column of X holds no non-finite entry.

Related

source
PortfolioOptimisers.investable_plot_viewFunction
investable_plot_view(pr::AbstractPriorResult, nx = nothing, w = nothing)
investable_plot_view(rd::AbstractReturnsResult, nx = nothing, w = nothing)
investable_plot_view(imsk::Nothing, pr::AbstractPriorResult, nx, w)
investable_plot_view(imsk::BitVector, pr::AbstractPriorResult, nx, w)

Reduce a prior result, the axis names and the weights a computed figure draws to the Investable Mask.

A drawn plot keeps the frame: a heatmap or a bar chart of a Prior Result shows the full universe, and the backend leaves a blank cell and a missing bar where the asset is not investable. A computed plot has no such option. eigvals(Symmetric(sigma)) refuses a NaN, and a phylogeny or a centrality score is fitted by a plain moment estimator, which refuses one too. Such a figure reduces here instead, and it draws the investable universe alone.

The reduction is the one port_opt_view the prior's owner already writes, so a new block cannot be forgotten and the reduced pr.X carries no dead column. The names and the weights ride the asset axis, so they take the mask directly.

A ReturnsResult carries no moments, so no mask exists to derive and it passes through. That is what lets one door state the reduction once and dispatch decide whether it happens.

No diagnostic is emitted. A figure is a drawing rather than a number a caller acts on, and the docstring of each computed plot says that a non-investable asset is not drawn.

Algorithm

  1. Return the three arguments unchanged when the carrier is a returns result.
  2. Derive the Investable Mask once with investable_mask.
  3. Return the three unchanged when every asset is investable.
  4. Otherwise return a port_opt_view of the prior at findall(imsk), and the views of the names and the weights at the mask.

Arguments

  • pr: Prior result, or ReturnsResult.
  • nx: Asset names of the axis, or nothing.
  • w: Portfolio weights the figure sizes by, or nothing.
  • imsk: The Investable Mask, or nothing when every asset is investable.

Returns

  • (pr, nx, w): The three reduced to the Investable Mask, or unchanged.

Related

source
PortfolioOptimisers.attribution_plot_axisFunction
attribution_plot_axis(fa::FactorAttributionResult, by_family::Bool, rd::ReturnsResult,
                      nf::Option{<:AbstractVector})

Return the axis of a FactorAttributionResult a plot draws, and the labels of its rows.

The four attribution plots share one choice: the factor axis or the family axis, and where the labels of that axis come from. The family axis carries its own labels, because they are derived from the block; the factor axis carries none, because the factor names are carried input, so they come from nf, from rd.nf, or from the row's position.

Arguments

  • fa: Factor attribution result.
  • by_family: Whether to draw the family axis rather than the factor axis.
  • rd: Returns result providing the factor names through rd.nf.
  • nf: Factor names; overrides rd.nf when provided.

Validation

  • If by_family is true, fa.fmbd is not nothing, else an ArgumentError is raised.

Returns

  • bd::AttributionBreakdown: The axis the plot draws.
  • labels::AbstractVector: The label of each row of that axis.

Related

source