Returns result

Prices to returns

Other than FiniteAllocationOptimisationEstimator, all optimisations work based off returns data rather than price data. These functions and types are involved in computing returns.

Types

PortfolioOptimisers.ReturnsResultType
struct ReturnsResult{__T_nx, __T_X, __T_nf, __T_F, __T_nb, __T_B, __T_ts, __T_iv, __T_ivpa, __T_pnl} <: AbstractReturnsResult

Stores the results of asset and factor returns calculations.

ReturnsResult is the standard result type returned by returns-processing routines, such as prices_to_returns.

It supports both asset and factor returns, as well as optional time series and implied volatility information, and is designed for downstream compatibility with optimisation and analysis routines.

It also carries the optional AssetPanel pnl: the two universe masks the ingestion layer states, and the Panel Fields that FeatureDistance turns into a distance. The panel is data, not configuration, which is why it is held here rather than on an estimator: the clustering stack is asset-subset-blind by construction, so an estimator-held Feature Matrix would survive a nested-clustered subproblem or a cross-validation fold unsliced, with its asset axis silently pointing at the full universe. ReturnsResult implements port_opt_view, so a carried panel is subselected in step with X.

Fields

  • nx: Names or identifiers of asset columns (assets × 1).
  • X: Asset returns matrix (observations × assets).
  • nf: Names or identifiers of factor columns (factors × 1).
  • F: Factor returns matrix (observations × factors).
  • nb: Names or identifiers of benchmark columns (observations × 1) or (observations × assets).
  • B: Benchmark prices (observations × 1) or (observations × assets).
  • ts: Optional timestamps for each observation (observations × 1).
  • iv: Implied volatilities matrix (observations × assets).
  • ivpa: Implied volatility risk premium adjustment, if a vector (assets × 1).
  • pnl: Optional AssetPanel: the Panel Fields of the universe, and its two universe masks.

Constructors

ReturnsResult(;    nx::Option{<:VecStr} = nothing,    X::Option{<:MatNum} = nothing,    nf::Option{<:VecStr} = nothing,    F::Option{<:MatNum} = nothing,    nb::Option{<:VecStr} = nothing,    B::Option{<:VecNum_MatNum} = nothing,    ts::Option{<:VecDate} = nothing,    iv::Option{<:MatNum} = nothing,    ivpa::Option{<:Num_VecNum} = nothing,    pnl::Option{<:AssetPanel} = nothing,) -> ReturnsResult

Keywords correspond to the struct's fields.

Validation

  • If nx or X is not nothing, !isempty(nx), !isempty(X), and length(nx) == size(X, 2).
  • If nf or F is not nothing, !isempty(nf), !isempty(F), length(nf) == size(F, 2), and size(X, 1) == size(F, 1).
  • If nb or B is not nothing and B is a matrix: !isempty(nb), !isempty(B), and length(nb) == size(B, 2).
  • If nb or B is not nothing and B is a vector: length(nb) == 1.
  • If X and B are not nothing: if B is a vector, size(X, 1) == size(B, 1); if B is a matrix, size(X) == size(B).
  • If ts is not nothing, !isempty(ts), allunique(ts), and length(ts) == size(X, 1). Uniqueness is required because ts keys the observation axis rather than merely labelling it: feature_row_indices recovers a subset's rows by matching its surviving timestamps back into this clock, and a repeated timestamp would resolve to the first occurrence and pair an asset with another period's features.
  • If ts and B are not nothing: length(ts) == size(B, 1).
  • If iv is not nothing, !isempty(iv), size(iv) == size(X), and every value is finite and non-negative where it is present (an absent one is NaN; see assert_nonneg_where_present).
  • ivpa is validated in that same branch, so it is checked only when iv is given: all(x -> x > 0, ivpa), all(x -> isfinite(x), ivpa), and, if a vector, length(ivpa) == size(iv, 2). The bound is strict — a zero adjustment is rejected. An ivpa passed without an iv reaches no check, because it has no implied volatility to adjust.
  • pnl's asset axis is length(nx), and its observation axis is size(X, 1) when it is time-varying. See check_asset_panel.

Examples

julia> ReturnsResult(; nx = ["A", "B"], X = [0.1 0.2; 0.3 0.4])ReturnsResult    nx ┼ Vector{String}: ["A", "B"]     X ┼ 2×2 Matrix{Float64}    nf ┼ nothing     F ┼ nothing    nb ┼ nothing     B ┼ nothing    ts ┼ nothing    iv ┼ nothing  ivpa ┼ nothing   pnl ┴ nothing

Related

source

Functions

PortfolioOptimisers.asset_panelMethod
asset_panel(ape::Nothing, pr, rd::ReturnsResult, X) -> AssetPanel
asset_panel(ape::Nothing, pr::ReturnsResult, rd::Nothing, X) -> AssetPanel
asset_panel(ape::Nothing, pr, rd::Nothing, X) -> Union{}

Resolve the AssetPanel a FeatureDistance with no producer measures.

nothing in the ape slot says read the panel the data carrier already holds. The carriers reach the kernel as the two keywords pr and rd, and this verb resolves the source by dispatch: a ReturnsResult in either slot answers its pnl, and rd wins when both hold one, because the data carrier is where a panel is data rather than a by-product. Pr_RR admits a ReturnsResult in the pr slot, which is what clusterise(cle, rd) and every Pipeline step pass, so the second method is not a fallback but the shortest public call.

A prior result alone carries no panel, so it raises an IsNothingError naming the two ways forward.

Algorithm

The method that Julia selects is the algorithm.

  1. rd is a ReturnsResult: answer rd.pnl.
  2. pr is a ReturnsResult and there is no rd: answer pr.pnl.
  3. Neither slot holds a data carrier: raise.

Each of the first two checks that the carrier it read holds a panel, with assert_asset_panel_supplied.

Arguments

  • ape: nothing, which reads the carrier's panel.
  • pr: Prior result or returns result. Both carry the asset returns matrix X and the feature matrix Z, so either can supply them.
  • rd: The returns result to use.
  • X: Returns matrix of the subproblem. Unread here; a producer reads it.

Validation

Returns

  • pnl::AssetPanel: The Asset Panel the data carrier holds.

Related

source
PortfolioOptimisers.port_opt_viewMethod
port_opt_view(
    rd::ReturnsResult,
    i
) -> Union{ReturnsResult{_A, _B, _C, _D, _E, _F, _G, _H, _I, Nothing} where {_A, _B, _C, _D, _E, _F, _G, _H, _I}, ReturnsResult{_A, _B, _C, _D, _E, _F, _G, _H, _I, AssetPanel{__T_pf, __T_amsk, __T_emsk}} where {_A, _B, _C, _D, _E, _F, _G, _H, _I, __T_pf, __T_amsk, __T_emsk}}

Return a view of the ReturnsResult object for the assets at indices i.

This is the port_opt_view method for ReturnsResult — the View of the library's central data structure, restricting it to a subset of assets.

Warning

This two-argument method indexes assets, matching the rest of the port_opt_view family. The four-argument method port_opt_view(rd, i, j, k) indexes observations first and assets second. The two arities therefore give i different meanings; see port_opt_view(rd::ReturnsResult, i, j, k).

Algorithm

  1. View the asset names nx at i with nothing_scalar_array_view.
  2. View the asset returns as view(rd.X, :, i). Axis 2 is the assets, and every observation is kept.
  3. When B is a matrix, it holds one column per asset: view nb at i, and view B as view(rd.B, :, i). Otherwise — a single shared benchmark, or none at all — nb and B both pass through untouched.
  4. View the implied volatilities as view(rd.iv, :, i), and the adjustment ivpa at i.
  5. View the AssetPanel pnl with panel_carrier_view at i on the asset axis, handing it the asset names rd.nx. The observation index is a Colon, so a time-varying panel keeps every observation. The view slices every Panel Field's values and both universe masks on the asset axis, and a tensor Panel Field whose labels are the asset names (features_are_assets) on its label axis as well; every other field's label axis addresses features, which an asset view does not reach.
  6. Rebuild the ReturnsResult. The factor names nf, the factor returns F and the timestamps ts pass through untouched, because none of the three has an asset axis.

Each field that is nothing stays nothing. No step copies data.

Arguments

  • rd: A ReturnsResult object containing asset and/or factor returns.
  • i: Indices of the assets to view.

Returns

  • new_rr::ReturnsResult: A new ReturnsResult containing only the data for the specified index.

Examples

julia> rd = ReturnsResult(; nx = ["A", "B"], X = [0.1 0.2; 0.3 0.4])ReturnsResult    nx ┼ Vector{String}: ["A", "B"]     X ┼ 2×2 Matrix{Float64}    nf ┼ nothing     F ┼ nothing    nb ┼ nothing     B ┼ nothing    ts ┼ nothing    iv ┼ nothing  ivpa ┼ nothing   pnl ┴ nothingjulia> PortfolioOptimisers.port_opt_view(rd, 2:2)ReturnsResult    nx ┼ SubArray{String, 1, Vector{String}, Tuple{UnitRange{Int64}}, true}: ["B"]     X ┼ 2×1 SubArray{Float64, 2, Matrix{Float64}, Tuple{Base.Slice{Base.OneTo{Int64}}, UnitRange{Int64}}, true}    nf ┼ nothing     F ┼ nothing    nb ┼ nothing     B ┼ nothing    ts ┼ nothing    iv ┼ nothing  ivpa ┼ nothing   pnl ┴ nothing

Related

Return a view of the ReturnsResult object for assets at indices j, observations at indices i, and factors at indices k.

Warning

Unlike every other port_opt_view method — including port_opt_view(rd::ReturnsResult, i) — the first index of this method selects observations, not assets. Assets are the second index. Cross-validation splits observations and assets together, which is why this arity exists at all.

Algorithm

  1. View the asset names nx at j with nothing_scalar_array_view.
  2. View the asset returns as view(rd.X, i, j). Axis 1 is the observations, and axis 2 is the assets.
  3. View the factor names nf at k, unless k is a Colon, in which case nf passes through. View the factor returns as view(rd.F, i, k).
  4. When B is a matrix, it holds one column per asset: view nb at j, and view B as view(rd.B, i, j). When B is a vector, it is a single shared benchmark: view it as view(rd.B, i), and carry nb through.
  5. View the timestamps ts at i, the implied volatilities as view(rd.iv, i, j), and the adjustment ivpa at j.
  6. View the AssetPanel pnl with panel_carrier_view at the observations i and the assets j, handing it the asset names rd.nx, which slices both axes of every Panel Field and of both universe masks, and the label axis of a tensor Panel Field whose labels are the asset names (features_are_assets). A static panel has no observation axis and ignores i, which is the same asymmetry ivpa has on the asset axis.
  7. Rebuild the ReturnsResult.

Each field that is nothing stays nothing. No step copies data.

Arguments

  • rd: A ReturnsResult object containing asset and/or factor returns.
  • i: Index or indices of the observation(s) to view.
  • j: Index or indices of the assets to view.
  • k: Index or indices of the factors to view.

Returns

  • new_rr::ReturnsResult: A new ReturnsResult containing only the data for the specified indices.

Related

Examples

julia> rd = ReturnsResult(; nx = ["A", "B"], X = [0.1 0.2; 0.3 0.4; 0.5 0.6], nf = ["F1"],                          F = [1.0; 2.0; 3.0;;])ReturnsResult    nx ┼ Vector{String}: ["A", "B"]     X ┼ 3×2 Matrix{Float64}    nf ┼ Vector{String}: ["F1"]     F ┼ 3×1 Matrix{Float64}    nb ┼ nothing     B ┼ nothing    ts ┼ nothing    iv ┼ nothing  ivpa ┼ nothing   pnl ┴ nothingjulia> PortfolioOptimisers.port_opt_view(rd, 1:2, 2:2)ReturnsResult    nx ┼ SubArray{String, 1, Vector{String}, Tuple{UnitRange{Int64}}, true}: ["B"]     X ┼ 2×1 SubArray{Float64, 2, Matrix{Float64}, Tuple{UnitRange{Int64}, UnitRange{Int64}}, false}    nf ┼ Vector{String}: ["F1"]     F ┼ 2×1 SubArray{Float64, 2, Matrix{Float64}, Tuple{UnitRange{Int64}, Base.Slice{Base.OneTo{Int64}}}, false}    nb ┼ nothing     B ┼ nothing    ts ┼ nothing    iv ┼ nothing  ivpa ┼ nothing   pnl ┴ nothing

      portoptview(rd::AbstractReturnsResult, args...; kwargs...)

Erroring tripwire for AbstractReturnsResult subtypes that do not implement port_opt_view.

Without it, the universal leaf fallback port_opt_view(x, i, args...) would hand back the returns result unsubselected, and a meta-optimiser or cross-validation fold would silently train on the full universe. Returns data is never a leaf value, so an unhandled subtype is a missing method, not a pass-through.

Subtypes carrying an AssetPanel owe it the same treatment as X: subselect its asset axis on every arity, its observation axis on the arities that take one, and — when a tensor Panel Field's labels are the assets (features_are_assets) — that field's label axis as well. A panel that survives a fold unsliced is the same silent-wrongness as an unsliced returns matrix, one level down: the distance it produces is finite, plausible, and computed over the wrong universe. port_opt_view implements the rule; the ReturnsResult methods are the reference.

Algorithm

  1. Throw an ArgumentError naming the concrete type and the number of index arguments the call gave. The method reads neither the indices nor the fields of rd.

Related

Erroring tripwire for ReturnsResult calls whose call shape no supported arity matches.

ReturnsResult does implement port_opt_view, so the AbstractReturnsResult tripwire above would misreport a mistyped call as an unimplemented subtype. This method takes the call instead and names the call shape: the supported arities take one, two, or three positional index arguments and no keyword arguments — in particular factors is the third positional index, not a keyword.

Algorithm

  1. Count the positional arguments args, and read the names of the keyword arguments kwargs.
  2. Throw an ArgumentError reporting both counts, and naming the three supported call shapes.

Related

source
PortfolioOptimisers.returns_result_pickerFunction
returns_result_picker(rd::ReturnsResult, brt::Bool) -> ReturnsResult

Return a ReturnsResult appropriate for benchmark-tracking optimisations.

This helper inspects the ReturnsResult's benchmark field B and the boolean flag brt (benchmark-tracking). If brt is true and a benchmark B is present it returns a new ReturnsResult in which asset returns X have the benchmark removed (i.e. X - B or broadcast X .- B for vector benchmarks). If brt is false or no benchmark is present, the original ReturnsResult is returned unchanged.

Algorithm

The first step is a method selected on the field type of B, so a carrier with no benchmark runs no branch at all.

  1. rd carries no benchmark, because its B field is Nothing: return rd itself.
  2. brt is false: return rd itself.
  3. brt is true: subtract the benchmark from the asset returns, giving X. A vector benchmark subtracts by broadcast, rd.X .- rd.B, which takes one benchmark value per observation from every asset column. A matrix benchmark subtracts elementwise, rd.X - rd.B.
  4. Rebuild the ReturnsResult from X, and leave nb and B unset. The benchmark is spent on the subtraction, which is what makes a second call return its argument unchanged. Every other field — nx, nf, F, ts, iv, ivpa and pnl — is carried over. The argument itself is never modified.

Arguments

  • rd: A ReturnsResult object containing asset, factor and/or benchmark returns.
  • brt: Boolean flag indicating whether benchmark-tracking behaviour should be applied. When true, asset returns are adjusted by subtracting the benchmark B (if present).

Returns

  • rd::ReturnsResult:

    • If brt is true and a benchmark B is present: A new ReturnsResult with adjusted asset returns
    • Otherwise: The rd is returned unchanged. nb and B hold nothing on an adjusted result, which is what makes the adjustment idempotent.

Examples

julia> rd = ReturnsResult(; nx = ["A", "B"], X = [0.10 0.20; 0.30 0.40], nb = ["BM"],                          B = [0.01; 0.02])ReturnsResult    nx ┼ Vector{String}: ["A", "B"]     X ┼ 2×2 Matrix{Float64}    nf ┼ nothing     F ┼ nothing    nb ┼ Vector{String}: ["BM"]     B ┼ Vector{Float64}: [0.01, 0.02]    ts ┼ nothing    iv ┼ nothing  ivpa ┼ nothing   pnl ┴ nothingjulia> rd2 = returns_result_picker(rd, false)  # no change when brt is falseReturnsResult    nx ┼ Vector{String}: ["A", "B"]     X ┼ 2×2 Matrix{Float64}    nf ┼ nothing     F ┼ nothing    nb ┼ Vector{String}: ["BM"]     B ┼ Vector{Float64}: [0.01, 0.02]    ts ┼ nothing    iv ┼ nothing  ivpa ┼ nothing   pnl ┴ nothingjulia> rd === rd2truejulia> rd3 = returns_result_picker(rd, true)ReturnsResult    nx ┼ Vector{String}: ["A", "B"]     X ┼ 2×2 Matrix{Float64}    nf ┼ nothing     F ┼ nothing    nb ┼ nothing     B ┼ nothing    ts ┼ nothing    iv ┼ nothing  ivpa ┼ nothing   pnl ┴ nothingjulia> rd.X .- rd.B == rd3.Xtrue

Related

source