Simple expected returns: private API

The most basic moment is the simple expected return. These types and functions implement it.

PortfolioOptimisers.show_fieldsMethod
show_fields(
    me::SimpleExpectedReturns
) -> Union{Tuple{Symbol}, Tuple{Symbol, Symbol}}

Renders every field of a SimpleExpectedReturns except cache, and cvg only where a policy is set.

The state a cache holds is the running detail of an incremental fit, not the configuration a reader looks the type up for, and it prints under the estimator at every site that renders one. Set set_show_nothing_fields!(:SimpleExpectedReturns, true) to render it. cvg is read from the instance rather than from the type, because an opt-in that a caller has not taken is not part of the configuration they chose: an estimator whose cvg is nothing renders exactly as it did before the field existed, and one that carries a CoveragePolicy renders it.

Arguments

  • me: Expected returns estimator, read for its cvg field.

Returns

  • fields::Tuple: The field names to render, which is (:w,) with no policy and (:w, :cvg) with one.

Related

source

Incremental fit

The sample mean folds one observation at a time, so a long history need not be held or re-read. partial_fit! returns a new estimator whose cache field carries the state, and mean reads the fit off the estimator alone.

PortfolioOptimisers.SimpleExpectedReturnsStateType
struct SimpleExpectedReturnsState{__T_n, __T_mu, __T_cvg} <: AbstractPartialFitState

Carries the running observation count and mean of an incremental sample-mean fit.

The state of SimpleExpectedReturns under partial_fit!. It holds no second-moment accumulator, because a mean is the whole estimate, so merge_states folds the two counts and the two means and discards the accumulator chan_merge returns.

Fields

  • n: Number of observations folded into the state.
  • mu: Running mean of the observations folded into the state, assets × 1.
  • cvg: Optional CoverageCounts, the per-cell denominators and per-asset bookkeeping of an available-case fold. It is nothing when the estimator carries no CoveragePolicy, so the plain state costs nothing.

Constructors

SimpleExpectedReturnsState(;    n::Integer = 0,    mu::VecNum,    cvg::Option{<:CoverageCounts} = nothing) -> SimpleExpectedReturnsState

Keywords correspond to the struct's fields. A state seeded for N assets is SimpleExpectedReturnsState(; mu = zeros(N)), which partial_fit! builds when the cache field of the estimator holds nothing.

Validation

  • n >= 0. A DomainError is thrown otherwise.
  • !isempty(mu). An IsEmptyError is thrown otherwise.
  • Every entry of mu is finite. An IsNonFiniteError is thrown otherwise.

View parameters

When port_opt_view is called on this type, its fields are subset to the selected assets:

Examples

julia> PortfolioOptimisers.SimpleExpectedReturnsState(; mu = [0.0, 0.0])PortfolioOptimisers.SimpleExpectedReturnsState    n ┼ Int64: 0   mu ┼ Vector{Float64}: [0.0, 0.0]  cvg ┴ nothing

Related

source
PortfolioOptimisers.expected_returns_state_seedFunction
expected_returns_state_seed(
    cache::Union{Nothing, SimpleExpectedReturnsState},
    x::AbstractVector{<:Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}}
) -> Any
expected_returns_state_seed(
    cache::Union{Nothing, SimpleExpectedReturnsState},
    x::AbstractVector{<:Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}},
    cvg::Union{Nothing, CoveragePolicy}
) -> Any

Returns the SimpleExpectedReturnsState an incremental mean fit folds into, seeding one of zeros when the estimator carries none.

The seed is written here rather than inside partial_fit!, so the fold reads as one line and the branch that reads the cache field has one home.

Arguments

  • cache: The state the estimator carries, or nothing.
  • x: One observation, assets × 1, read for its length and its element type.
  • cvg: The policy the estimator carries, which decides whether the seed carries per-asset counts.

Returns

  • state::SimpleExpectedReturnsState: The state cache holds, or a state of zeros over length(x) assets.

Related

source
Base.copyMethod
copy(
    x::SimpleExpectedReturnsState
) -> SimpleExpectedReturnsState

Copies a SimpleExpectedReturnsState, so the copy shares no array with the original.

The copy method of the AbstractPartialFitState interface, which partial_fit calls before it folds. The count is a scalar and passes through, and the running mean is copied.

Arguments

  • x: The state to copy.

Returns

  • state::SimpleExpectedReturnsState: A fresh state, equal to x, whose mu is a fresh vector.

Related

source

Available-case fit

With a CoveragePolicy in its cvg field the estimator fits each asset on that asset's own finite and active observations, and PortfolioOptimisers.coverage_mean routes between that arm and the Coverage Universe one.

PortfolioOptimisers.coverage_meanFunction
coverage_mean(me, cvg, X; dims::Int = 1, active_mask = nothing, kwargs...) -> ArrNum

Routes a sample-mean fit to the Coverage Universe arm or to the available-case arm.

The cvg field of the estimator is passed as the second argument, so the arm is chosen by dispatch on the policy rather than by a branch on its value. An estimator that carries no policy therefore pays nothing for the field: the Nothing method is the body mean(me::SimpleExpectedReturns, X::MatNum; dims::Int = 1, kwargs...) has always had, and it is statically resolved because the field's type is concrete.

Arguments

  • me: Expected returns estimator.
  • cvg: The policy the estimator carries, which selects the arm.
  • X: Data matrix observations × assets if the dims keyword does not exist or dims = 1, assets × observations when dims = 2.
  • dims: Dimension along which to perform the computation.
  • active_mask: The active mask of the Asset Panel, observations × assets, or nothing. The Coverage Universe arm ignores it.
  • kwargs...: Additional keyword arguments passed to Statistics.mean.

Returns

  • mu::ArrNum: Expected returns vector assets x 1 if the dims keyword does not exist or dims = 2, 1 x assets if dims = 1.

Related

source
PortfolioOptimisers.coverage_meanMethod
coverage_mean(
    me::SimpleExpectedReturns,
    ::Nothing,
    X::AbstractMatrix{<:Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}};
    dims,
    active_mask,
    kwargs...
) -> Any

Nothing method of coverage_mean. The Coverage Universe arm, which is the sample mean of a window every asset covers, and the answer mean(me::SimpleExpectedReturns, X::MatNum; dims::Int = 1, kwargs...) gave before the cvg field existed. It refuses a gapped sample with assert_finite_sample and ignores the active mask, because an estimator with no policy reads the universe through coverage_reduction and never sees a gap.

Related

source
PortfolioOptimisers.coverage_meanMethod
coverage_mean(
    me::SimpleExpectedReturns,
    cvg::CoveragePolicy,
    X::AbstractMatrix{<:Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}};
    dims,
    active_mask,
    kwargs...
) -> Any

CoveragePolicy method of coverage_mean. The available-case arm: each asset's mean is fitted on that asset's own finite and active observations, and the batch answer is the incremental one, because the arm folds the block through partial_fit! and reads the state out.

Writing the batch arm as the fold is what makes the map's oracle hold to the last bit rather than to a tolerance: there is one recursion, and a caller who hands the same rows over one at a time reaches the same floating-point number.

Algorithm

  1. Fold every row of X into a fresh state with partial_fit!, carrying the active mask.
  2. Read the state out with mean(me::SimpleExpectedReturns, state::SimpleExpectedReturnsState).
  3. Orient the answer as the caller's dims asks.

Related

source
PortfolioOptimisers.coverage_meanMethod
coverage_mean(
    _::SimpleExpectedReturns,
    _::Nothing,
    state::SimpleExpectedReturnsState
) -> Any

Nothing method of the read-out arm of coverage_mean. Every asset shares one count, so the whole answer is NaN before the first observation and the running mean afterwards.

The running mean is copied rather than handed out. partial_fit! writes the Welford recursion into state.mu in place, so a read-out that returned the accumulator itself would hand the caller a vector that the next fold silently rewrites — and a prior that read its mu out and carried it into a Result would find the Result changed under it at the next observation. The CoveragePolicy method beside this one copies for the same reason, through coverage_frame.

Related

source
PortfolioOptimisers.coverage_meanMethod
coverage_mean(
    _::SimpleExpectedReturns,
    cvg::CoveragePolicy,
    state::SimpleExpectedReturnsState
) -> Any

CoveragePolicy method of the read-out arm of coverage_mean. Each asset's mean is read out against that asset's own count, and an asset the policy refuses is NaN.

Algorithm

  1. Read the admitted assets with coverage_admission.
  2. Frame the running mean with coverage_frame, which copies it where its asset has an observation. A running per-asset mean is already the ratio, so it is never divided again.

Related

source