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.ReturnsResult — Type
struct ReturnsResult{__T_nx, __T_X, __T_nf, __T_F, __T_nb, __T_B, __T_ts, __T_iv, __T_ivpa, __T_pnl} <: AbstractReturnsResultStores 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: OptionalAssetPanel: 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,) -> ReturnsResultKeywords correspond to the struct's fields.
Validation
- If
nxorXis notnothing,!isempty(nx),!isempty(X), andlength(nx) == size(X, 2). - If
nforFis notnothing,!isempty(nf),!isempty(F),length(nf) == size(F, 2), andsize(X, 1) == size(F, 1). - If
nborBis notnothingandBis a matrix:!isempty(nb),!isempty(B), andlength(nb) == size(B, 2). - If
nborBis notnothingandBis a vector:length(nb) == 1. - If
XandBare notnothing: ifBis a vector,size(X, 1) == size(B, 1); ifBis a matrix,size(X) == size(B). - If
tsis notnothing,!isempty(ts),allunique(ts), andlength(ts) == size(X, 1). Uniqueness is required becausetskeys the observation axis rather than merely labelling it:feature_row_indicesrecovers 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
tsandBare notnothing:length(ts) == size(B, 1). - If
ivis notnothing,!isempty(iv),size(iv) == size(X), and every value is finite and non-negative where it is present (an absent one isNaN; seeassert_nonneg_where_present). ivpais validated in that same branch, so it is checked only whenivis 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. Anivpapassed without anivreaches no check, because it has no implied volatility to adjust.pnl's asset axis islength(nx), and its observation axis issize(X, 1)when it is time-varying. Seecheck_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 ┴ nothingRelated
Functions
PortfolioOptimisers.asset_panel — Method
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.
rdis aReturnsResult: answerrd.pnl.pris aReturnsResultand there is nord: answerpr.pnl.- 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 matrixXand the feature matrixZ, so either can supply them.rd: The returns result to use.X: Returns matrix of the subproblem. Unread here; a producer reads it.
Validation
- A data carrier is present, and it holds an
AssetPanel. Raises anIsNothingError.
Returns
pnl::AssetPanel: The Asset Panel the data carrier holds.
Related
PortfolioOptimisers.port_opt_view — Method
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.
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
- View the asset names
nxatiwithnothing_scalar_array_view. - View the asset returns as
view(rd.X, :, i). Axis 2 is the assets, and every observation is kept. - When
Bis a matrix, it holds one column per asset: viewnbati, and viewBasview(rd.B, :, i). Otherwise — a single shared benchmark, or none at all —nbandBboth pass through untouched. - View the implied volatilities as
view(rd.iv, :, i), and the adjustmentivpaati. - View the
AssetPanelpnlwithpanel_carrier_viewation the asset axis, handing it the asset namesrd.nx. The observation index is aColon, 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. - Rebuild the
ReturnsResult. The factor namesnf, the factor returnsFand the timestampstspass through untouched, because none of the three has an asset axis.
Each field that is nothing stays nothing. No step copies data.
Arguments
rd: AReturnsResultobject containing asset and/or factor returns.i: Indices of the assets to view.
Returns
new_rr::ReturnsResult: A newReturnsResultcontaining 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 ┴ nothingRelated
portoptview( rd::ReturnsResult, i, j, k = : ) -> ReturnsResult
Return a view of the ReturnsResult object for assets at indices j, observations at indices i, and factors at indices k.
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
- View the asset names
nxatjwithnothing_scalar_array_view. - View the asset returns as
view(rd.X, i, j). Axis 1 is the observations, and axis 2 is the assets. - View the factor names
nfatk, unlesskis aColon, in which casenfpasses through. View the factor returns asview(rd.F, i, k). - When
Bis a matrix, it holds one column per asset: viewnbatj, and viewBasview(rd.B, i, j). WhenBis a vector, it is a single shared benchmark: view it asview(rd.B, i), and carrynbthrough. - View the timestamps
tsati, the implied volatilities asview(rd.iv, i, j), and the adjustmentivpaatj. - View the
AssetPanelpnlwithpanel_carrier_viewat the observationsiand the assetsj, handing it the asset namesrd.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 ignoresi, which is the same asymmetryivpahas on the asset axis. - Rebuild the
ReturnsResult.
Each field that is nothing stays nothing. No step copies data.
Arguments
rd: AReturnsResultobject 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 newReturnsResultcontaining 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 ┴ nothingportoptview(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
- Throw an
ArgumentErrornaming the concrete type and the number of index arguments the call gave. The method reads neither the indices nor the fields ofrd.
Related
portoptview(rd::ReturnsResult, args...; kwargs...)
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
- Count the positional arguments
args, and read the names of the keyword argumentskwargs. - Throw an
ArgumentErrorreporting both counts, and naming the three supported call shapes.
Related
PortfolioOptimisers.returns_result_picker — Function
returns_result_picker(rd::ReturnsResult, brt::Bool) -> ReturnsResultReturn 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.
rdcarries no benchmark, because itsBfield isNothing: returnrditself.brtisfalse: returnrditself.brtistrue: subtract the benchmark from the asset returns, givingX. 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.- Rebuild the
ReturnsResultfromX, and leavenbandBunset. 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,ivpaandpnl— is carried over. The argument itself is never modified.
Arguments
rd: AReturnsResultobject containing asset, factor and/or benchmark returns.brt: Boolean flag indicating whether benchmark-tracking behaviour should be applied. Whentrue, asset returns are adjusted by subtracting the benchmarkB(if present).
Returns
rd::ReturnsResult:- If
brtistrueand a benchmarkBis present: A newReturnsResultwith adjusted asset returns - Otherwise: The
rdis returned unchanged.nbandBholdnothingon an adjusted result, which is what makes the adjustment idempotent.
- If
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.XtrueRelated