Base moments: private API

Abstract moment types and fallbacks

Some optimisations and constraints make use of summary statistics. These types and functions form the base for moment estimation in PortfolioOptimisers.jl.

They also provide generic fallbacks for the various functionality in the library.

PortfolioOptimisers.AbstractExpectedReturnsAlgorithmType
abstract type AbstractExpectedReturnsAlgorithm <: AbstractAlgorithm

Abstract supertype for all expected returns algorithm types.

All concrete and/or abstract types that implement a specific algorithm used by an expected returns estimator should be subtypes of AbstractExpectedReturnsAlgorithm.

Interfaces

Given that these are meant to be used by expected returns estimators, there are no specific methods that need to be implemented for this abstract type. However, it serves as a marker for dispatching and organising different expected returns algorithms within the library. The interfaces should be defined at the level of the expected returns estimator that utilises these algorithms.

Related

source
PortfolioOptimisers.AbstractMomentAlgorithmType
abstract type AbstractMomentAlgorithm <: AbstractAlgorithm

Abstract supertype for all moment algorithm types.

All concrete and/or abstract types that implement a specific algorithm for moment estimation should be subtypes of AbstractMomentAlgorithm.

Interfaces

Given that these are meant to be used by covariance estimators, there are no specific methods that need to be implemented for this abstract type. However, it serves as a marker for dispatching and organising different moment algorithms within the library. The interfaces should be defined at the level of the covariance estimator that utilises these algorithms.

Related

source
PortfolioOptimisers.gap_fill_valueMethod
gap_fill_value(ce::StatsBase.CovarianceEstimator) -> Number

Return the value that stands in for a gapped cell of a sample handed to ce.

A gapped sample carries a non-finite cell because the asset was not there, not because a number went wrong. A consumer that holds such a sample and an arbitrary covariance estimator cannot know which of the two the estimator wants of it, so it asks the estimator, and the answer is a property of the estimator alone.

A finite answer is written over every gapped cell before the sample is handed over, so the estimator sees a complete sample and no mask. A non-finite answer leaves the gap where it is, and the consumer hands the active mask that explains it to Statistics.cov(ce, X; dims, active_mask), which an estimator answering a non-finite value owns.

The fallback is zero: a plain moment estimator refuses a gapped sample outright, and zero is the neutral value of a standardised series. A gap-aware estimator answers NaN instead, and that one method is the whole cost of adding one.

A covariance estimator nests, so the answer recurses. An estimator that wraps another and forwards the sample and its keywords untouched — PortfolioOptimisersCovariance and CorrelationCovariance — answers what the estimator it wraps answers, so wrapping a gap-aware estimator keeps the gap. One that reads the sample itself before it delegates, as Covariance does through its own expected-returns estimator, keeps the fallback: it refuses the gap on its own account, and no answer of its inner estimator changes that.

Arguments

  • ce: Covariance estimator.

Returns

  • fv::Number: The value a gapped cell takes, zero for a plain estimator and NaN for a gap-aware one.

Related

source
PortfolioOptimisers.densifyFunction
densify(X::MatNum) -> MatNum

Materialise a lazy or sparse observation matrix as a dense Matrix.

StatsBase's weighted moment API is typed on DenseMatrix. A Transpose, an Adjoint, a SubArray or a sparse matrix does not match it. Without a mean the call raises a MethodError, which is recoverable. With a mean it is not: cov(::SimpleCovariance, X, w; mean = mu) forwards four positional arguments as covm(X, mu, w, dims), and when the DenseMatrix method does not match, that call resolves to Statistics.covm(x, xmean, y, ymean, vardim) — the cross-covariance of X against the weight vector. It returns an N × 1 matrix in place of an N × N one and raises nothing. robust_cov and robust_cor densify before every weighted call so that neither outcome is reachable.

Algorithm

  1. When X is a DenseMatrix of numbers or of JuMP scalars, return it unchanged.
  2. Otherwise return Matrix(X), a dense copy.

Arguments

  • X: Data matrix observations × assets if the dims keyword does not exist or dims = 1, assets × observations when dims = 2.

Returns

  • X::MatNum: X itself when it is already a dense Matrix, and Matrix(X) otherwise.

Related

source
PortfolioOptimisers.robust_covFunction
robust_cov(
    ce::StatsBase.CovarianceEstimator,
    X::MatNum,
    [w::StatsBase.AbstractWeights];
    dims::Int = 1,
    mean = nothing,
    kwargs...
) -> MatNum

Computes the optionally weighted covariance with compat_cov on dense observations. The unweighted method retries once with a densified Matrix after a MethodError. The weighted method calls densify before the estimator.

Algorithm

  1. Without w, call compat_cov on X as the caller gave it. When that call raises a MethodError, call compat_cov once more on Matrix(X). Any other error propagates to the caller.
  2. With w, densify X with densify, then call compat_cov once. There is no retry, because the densification already removes the only failure the retry answers.
  3. Return the matrix that compat_cov returned.

Arguments

  • ce: Covariance estimator.
  • X: Data matrix observations × assets if the dims keyword does not exist or dims = 1, assets × observations when dims = 2.
  • w: Optional observation weights vector observations × 1, or a concrete subtype of DynamicAbstractWeights. If nothing, the computation is unweighted.
  • dims: Dimension along which to perform the computation.
  • mean: Optional mean value to use for centering.
  • kwargs...: Additional keyword arguments passed to compat_cov.

Validation

  • dims in (1, 2).

Returns

  • sigma::MatNum: Covariance matrix assets x assets.

Related

source
PortfolioOptimisers.robust_corFunction
robust_cor(
    ce::StatsBase.CovarianceEstimator,
    X::MatNum,
    [w::StatsBase.AbstractWeights];
    dims::Int = 1,
    mean = nothing,
    kwargs...
) -> MatNum

Computes the optionally weighted correlation with compat_cor on dense observations. The unweighted method retries once with a densified Matrix after a MethodError. The weighted method calls densify before the estimator.

Algorithm

  1. Without w, call compat_cor on X as the caller gave it. When that call raises a MethodError, call compat_cor once more on Matrix(X). Any other error propagates to the caller.
  2. With w, densify X with densify, then call compat_cor once. There is no retry, because the densification already removes the only failure the retry answers.
  3. Return the matrix that compat_cor returned.

Arguments

  • ce: Covariance estimator.
  • X: Data matrix observations × assets if the dims keyword does not exist or dims = 1, assets × observations when dims = 2.
  • w: Optional observation weights vector observations × 1, or a concrete subtype of DynamicAbstractWeights. If nothing, the computation is unweighted.
  • dims: Dimension along which to perform the computation.
  • mean: Optional mean value to use for centering.
  • kwargs...: Additional keyword arguments passed to compat_cor.

Validation

  • dims in (1, 2).

Returns

  • rho::MatNum: Correlation matrix assets x assets.

Related

source
PortfolioOptimisers.compat_covFunction
compat_cov(
    ce::StatsBase.CovarianceEstimator,
    X::MatNum,
    [w::StatsBase.AbstractWeights];
    dims::Int = 1,
    mean = nothing,
    kwargs...
) -> MatNum

Compute the covariance matrix robustly using the specified covariance estimator ce, data matrix X, and optional weights vector w.

Algorithm

  1. When the caller passed extra keyword arguments, and hasmethod reports that Statistics.cov takes dims, mean and those keys for this estimator and these arguments, call Statistics.cov with all of them and return the result.
  2. When step 1 raises a MethodError, drop the extra keyword arguments and continue. A method whose signature ends in a kwargs... slurp satisfies hasmethod and can still reject a key further down its call chain. Any other error propagates to the caller.
  3. Call Statistics.cov(ce, X, args...; dims = dims, mean = mean), and return the result.

Arguments

  • ce: Covariance estimator.
  • X: Data matrix observations × assets if the dims keyword does not exist or dims = 1, assets × observations when dims = 2.
  • w: Optional observation weights vector observations × 1, or a concrete subtype of DynamicAbstractWeights. If nothing, the computation is unweighted.
  • dims: Dimension along which to perform the computation.
  • mean: Optional mean value to use for centering.
  • kwargs...: Additional keyword arguments passed to cov.

Returns

  • sigma::MatNum: Covariance matrix assets x assets.

Related

source
PortfolioOptimisers.compat_corFunction
compat_cor(
    ce::StatsBase.CovarianceEstimator,
    X::MatNum,
    [w::StatsBase.AbstractWeights];
    dims::Int = 1,
    mean = nothing,
    kwargs...
) -> MatNum

Compute the correlation matrix robustly using the specified covariance estimator ce, data matrix X, and optional weights vector w.

Algorithm

  1. When hasmethod reports that Statistics.cor takes dims and mean for this estimator and these arguments, take steps 2 and 3. Otherwise take step 4.
  2. When the caller passed extra keyword arguments, and hasmethod reports that Statistics.cor takes those keys too, call Statistics.cor with all of them and return the result. When that call raises a MethodError, drop the extra keyword arguments and continue. A method whose signature ends in a kwargs... slurp satisfies hasmethod and can still reject a key further down its call chain.
  3. Call Statistics.cor(ce, X, args...; dims = dims, mean = mean), and return the result. When that call raises a MethodError, continue to step 4. Any other error propagates to the caller.
  4. The estimator answers no cor call, so compute the covariance matrix sigma with robust_cov instead.
  5. When sigma is mutable, convert it to a correlation matrix in place with StatsBase.cov2cor! and the square roots of its own diagonal. Otherwise convert a dense copy with StatsBase.cov2cor.
  6. Return sigma.

Arguments

  • ce: Covariance estimator.
  • X: Data matrix observations × assets if the dims keyword does not exist or dims = 1, assets × observations when dims = 2.
  • w: Optional observation weights vector observations × 1, or a concrete subtype of DynamicAbstractWeights. If nothing, the computation is unweighted.
  • dims: Dimension along which to perform the computation.
  • mean: Optional mean value to use for centering.
  • kwargs...: Additional keyword arguments passed to cor.

Returns

  • rho::MatNum: Correlation matrix assets x assets.

Related

source
PortfolioOptimisers.moment_window_and_weightsFunction
moment_window_and_weights(
    X::VecNum_MatNum,
    w::Option{<:ObsWeights},
    args...;
    dims::Int = 1,
    kwargs...
) -> (VecNum_MatNum, Option{<:StatsBase.AbstractWeights})
moment_window_and_weights(
    X::VecNum_MatNum,
    w::Option{<:ObsWeights},
    window::VecInt;
    dims::Int = 1,
    kwargs...
) -> (VecNum_MatNum, Option{<:StatsBase.AbstractWeights})

Apply the observation window and resolve weights for moment estimation.

Takes the view of X over the observations that window indexes, and resolves the observation weights over that same view. The caller resolves window first with get_window, so an Int window has already become a range by the time it reaches this function.

Algorithm

  1. Without a window, X passes through unchanged, and step 3 resolves the weights over the whole of it.
  2. With a window, take the view of X over those observations. For a matrix that is view(X, window, :) when dims == 1, and view(X, :, window) when dims == 2. For a vector it is view(X, window). Index w to the same observations with nothing_scalar_array_getindex.
  3. Resolve the observation weights with get_observation_weights, over w and the X of the step above.
  4. Return that X and the resolved w.

Arguments

  • X: Data matrix or vector.
  • w: Optional observation weights vector observations × 1, or a concrete subtype of DynamicAbstractWeights. If nothing, the computation is unweighted.
  • Either:
    • args: Additional positional arguments (ignored).
    • window: Observation window. An integer selects the last window observations, and a vector of indices selects those observations.
  • dims: Dimension along which to perform the computation. Ignored if X is a vector.
  • kwargs: Additional keyword arguments (ignored).

Returns

  • X::VecNum_MatNum: Appropriately windowed data matrix.
  • w::Option{<:StatsBase.AbstractWeights}: Resolved and appropriately windowed weights.

Related

source
PortfolioOptimisers.windowed_preambleFunction
windowed_preamble(
    est,
    w::Union{Nothing, DynamicAbstractWeights, AbstractWeights},
    window::Union{Nothing, Integer, AbstractVector{<:Integer}},
    X::AbstractMatrix{<:Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}};
    iv,
    dims,
    kwargs...
) -> Tuple{Any, Any, Nothing}

Shared preamble for windowed moment estimators (matrix input).

Whenever a window is given — an Int, which resolves to a range, or an explicit index vector — iv is subset to the same rows, or columns when dims = 2, so it stays aligned with the windowed returns. Only window = nothing, which resolves to a Colon, leaves iv unchanged.

Algorithm

  1. Resolve window with get_window, giving win. nothing resolves to a Colon, an Int to the range of the last window observations, and an index vector passes through.
  2. Apply win to X and rebind the observation weights to it with moment_window_and_weights, giving the windowed X and w_new.
  3. Build inner, a copy of est that carries w_new, with factory.
  4. When iv is given and win is an index vector, subset iv to the same rows, or to the same columns when dims == 2. A Colon leaves iv unchanged, so the full-data case never copies it.
  5. Return inner, the windowed X, and iv.

Arguments

  • est: Wrapped moment estimator to be cloned with updated weights.
  • w: Optional observation weights applied after windowing.
  • window: Window specification — nothing (full data), an Int (last window observations), or a VecInt of explicit row/column indices.
  • X: Data matrix of asset returns.
  • iv: Optional instrument variable matrix; subsetted to the window when window is a VecInt.
  • dims: Observation dimension — 1 for rows (default), 2 for columns. Checked by assert_dims, so every generated windowed method rejects an out-of-range dims instead of silently resolving a one-observation window.
  • kwargs...: Passed through to moment_window_and_weights.

Validation

  • dims in (1, 2).

Returns

  • (inner, X, iv): Weight-updated estimator, windowed returns matrix, and (possibly subsetted) instrument variable matrix.

Related

source
windowed_preamble(
    est,
    w::Union{Nothing, DynamicAbstractWeights, AbstractWeights},
    window::Union{Nothing, Integer, AbstractVector{<:Integer}},
    X::AbstractVector{<:Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}}
) -> Tuple{Any, Any}

Shared preamble for windowed moment estimators (vector input).

This method takes no dims, because a vector carries one axis, and no iv, because no vector generic of the family declares one.

Algorithm

  1. Resolve window with get_window, giving win. nothing resolves to a Colon, an Int to the range of the last window observations, and an index vector passes through.
  2. Apply win to X and rebind the observation weights to it with moment_window_and_weights, giving the windowed X and w_new.
  3. Build inner, a copy of est that carries w_new, with factory.
  4. Return inner and the windowed X.

Arguments

  • est: Wrapped moment estimator to be cloned with updated weights.
  • w: Optional observation weights applied after windowing.
  • window: Window specification — nothing (full data), an Int (last window observations), or a VecInt of explicit indices.
  • X: Data vector of returns.

Returns

  • (inner, X): Weight-updated estimator and windowed returns vector.

Related

source
PortfolioOptimisers.weighted_centreFunction
weighted_centre(X::MatNum, me::AbstractExpectedReturnsEstimator,
                w::Option{<:ObsWeights}; dims::Int = 1, mean = nothing,
                kwargs...) -> Union{<:Number, <:ArrNum}

Resolve the centre that a moment estimator's own observation weights weight.

A moment estimator's observation weights weight its centre, and not its deviations alone. This verb is the one place that rule lives, so SimpleVariance, Covariance, Coskewness and Cokurtosis reach their centre by one rule.

Algorithm

  1. mean is not nothing: return it unchanged. The keyword is the escape hatch for a centre that w does not describe.
  2. w is nothing: return Statistics.mean(me, X; dims = dims, kwargs...).
  3. w is not nothing: send me through factory with w, so the centre carries the weights of the deviations, and return the mean of the rebuilt estimator.

Step 2 is a performance guard and not a second contract. w reaches this verb from a field, so its type decides the branch, and the guard keeps a windowed loop from rebuilding the estimator tree of me once per window.

Arguments

  • X: Data matrix observations × assets if the dims keyword does not exist or dims = 1, assets × observations when dims = 2.
  • me: Expected returns estimator.
  • w: Optional observation weights vector observations × 1, or a concrete subtype of DynamicAbstractWeights. If nothing, the computation is unweighted.
  • dims: Dimension along which to perform the computation.
  • mean: Optional mean value to use for centering.
  • kwargs...: Additional keyword arguments for the expected returns estimator.

Returns

  • mu::Union{<:Number, <:ArrNum}: Centring vector, weighted by w when w is not nothing.

Related

source
PortfolioOptimisers.demean_returnsFunction
demean_returns(X::MatNum, me::AbstractExpectedReturnsEstimator; dims::Int = 1, mean = nothing,
               kwargs...) -> MatNum

Demeans the returns in X using the expected returns estimator me or if provided, a mean array.

Algorithm

  1. Resolve the centre mu with weighted_centre. When mean is nothing, the estimator computes it; otherwise mu is mean. me carries whatever weights it holds, and this verb adds none of its own.
  2. Subtract mu from X by broadcast, and return the result. dims names the observation axis, and the estimator shapes mu along the other one, so the broadcast subtracts one value per asset from every observation of that asset.

Arguments

  • X: Data matrix observations × assets if the dims keyword does not exist or dims = 1, assets × observations when dims = 2.
  • me: Expected returns estimator.
  • dims: Dimension along which to perform the computation.
  • mean: Optional mean value to use for centering.
  • kwargs...: Additional keyword arguments for the expected returns estimator.

Returns

  • MatNum: The demeaned returns matrix.

Related

source

Windowed estimator generation

The five windowed estimators — WindowedExpectedReturns, WindowedVariance, WindowedCovariance, WindowedCoskewness and WindowedCokurtosis — share one shape: wrap an inner estimator, restrict it to a trailing window, and forward every moment call to it. Each is generated from a single declaration by @windowed_estimator, so the struct, its constructor, its factory/port_opt_view methods, its forwarding methods and all of their docstrings cannot drift apart.

The entries below are the macro and its expansion-time machinery. They are internal: callers use the five estimators, not these.

PortfolioOptimisers.@windowed_estimatorMacro
@windowed_estimator Name <: Super begin
    field::FieldType = Default()
    noun    = "Noun"
    forward = [generic(::MatNum; mean) => :ret_key, ...]
    doctest = """..."""
end

Declare a windowed moment estimator: a wrapper that restricts an inner moment estimator to a sub-window of observations and rebinds observation weights to that window, leaving the inner estimator's semantics untouched.

One invocation emits the whole family member — the @propagatable @concrete struct (inner estimator tagged @fprop @vprop, w tagged @wprop, plus window), both constructors with their validation, one forwarding method per forward entry, the export, and every docstring.

Five nominal types exist rather than one parametric Windowed{E} because each answers a different generic and must subtype a different abstract estimator — AbstractCovarianceEstimator, CoskewnessEstimator, and the rest are load-bearing for dispatch across the library, and a Julia struct's supertype cannot depend on a type parameter. This macro is what keeps the five in sync.

Body

  • field::FieldType = Default(): the inner estimator. The field name is also its field_dict key and the argument name of every generated method, so it must follow the library convention (me, ce, ve, ske, kte).
  • noun: capitalised noun phrase naming the moment, e.g. "Expected returns". Drives all generated prose.
  • forward: one mini-signature per generic to forward, paired with the ret_dict key(s) documenting its return values. Naming mean in the mini-signature emits it as a named keyword instead of letting it ride in kwargs..., where it would leak into windowed_preamble.
  • doctest: the body of the jldoctest block for the # Examples section, without its fences.

Unknown keys, malformed forward entries, and unknown field_dict/ret_dict keys are rejected at macro-expansion time with a did_you_mean suggestion.

Algorithm

  1. Read name and super from the header.
  2. Walk the body once. The one field::Type = default line goes to windowed_parse_field, which returns field, ftype and default. The noun, forward and doctest lines bind their values. Any other key raises.
  3. Parse every entry of forward with windowed_parse_forward, giving specs.
  4. Render one cross-reference per entry of specs with windowed_method_ref, giving refs.
  5. For each entry of specs, build one documented forwarding method: windowed_method_doc writes its docstring, and windowed_method_def writes its body. Each method's # Related section lists the refs of its siblings and not its own.
  6. Build structexpr, the @concrete struct. It declares the inner estimator tagged @fprop @vprop, w tagged @wprop, and window, each with its live field_dict lookup, and the inner constructor that validates w and window.
  7. Build kwctor, the keyword constructor, whose defaults are default, nothing and nothing.
  8. Write the type's docstring with windowed_type_doc, and attach it to structexpr wrapped in @propagatable @concrete.
  9. Return the escaped block: the documented struct, kwctor, the forwarding methods, and the export of name.

Validation

  • The header reads Name <: Super, and Name is a Symbol.
  • The declaration body is a begin ... end block, and every line of it is an assignment.
  • At most one field::Type = default line appears.
  • Every other key names an entry of WINDOWED_ESTIMATOR_KEYS. An unknown key raises with a windowed_estimator_suggest suffix.
  • All four of field::Type = default, noun, forward and doctest are present.
  • noun and doctest are string literals.
  • forward is a vector, and it declares at least one generic.
  • Every failure above raises an ArgumentError through windowed_estimator_error, at macro-expansion time.

Returns

  • ex::Expr: The escaped block that declares the whole family member.

Examples

@windowed_estimator WindowedVariance <: AbstractVarianceEstimator begin    ve::AbstractVarianceEstimator = SimpleVariance()    noun = "Variance"    forward = [Statistics.var(::MatNum; mean) => :vararr,               Statistics.var(::VecNum; mean) => :varnum]    doctest = """    julia> WindowedVariance()    ...    """end

Related

source
PortfolioOptimisers.windowed_parse_fieldFunction
windowed_parse_field(ex) -> Tuple{Symbol, Any, Any}

Parse the field::Type = default line of a @windowed_estimator body into the inner estimator's field name, its declared type, and its keyword-constructor default.

The field name doubles as the field_dict key for the generated field docstring and as the argument name of every generated forwarding method, so it must follow the library's naming convention (me, ce, ve, ske, kte).

Algorithm

  1. Read the field name and its declared type from the left of the =, giving name and type.
  2. Check name against field_dict with windowed_estimator_check_key.
  3. Return name, type, and the right of the =, which is the keyword-constructor default.

Arguments

  • ex: The one field::Type = default line of the declaration body.

Validation

  • ex reads field::Type = default. Any other shape raises.
  • The field name is a Symbol. Any other name raises.
  • The field name names an entry of field_dict.

Returns

  • (name, type, default): The field name, its declared type, and its keyword-constructor default.

Related

source
PortfolioOptimisers.windowed_parse_forwardFunction
windowed_parse_forward(
    ex
) -> Tuple{Any, Any, Bool, Vector{Symbol}}

Parse one forward entry of a @windowed_estimator body — generic(::MatNum; mean) => :ret_key — into the generic being forwarded, its input type, whether it names a mean keyword, and the ret_dict keys documenting its return values.

Naming mean in the mini-signature is what keeps it out of the forwarded kwargs..., where it would otherwise leak into windowed_preamble.

Algorithm

  1. Split ex at the =>, giving the mini-signature sig and the return keys rets.
  2. Read the forwarded generic from the head of sig, giving gen.
  3. Walk the arguments of sig. A mean keyword sets has_mean. The one positional argument type sets input.
  4. Check input against WINDOWED_ESTIMATOR_INPUTS.
  5. Check every entry of rets against ret_dict with windowed_estimator_check_key, collecting them into keys_.
  6. Return gen, input, has_mean and keys_.

Arguments

  • ex: One entry of the forward vector of the declaration body.

Validation

  • ex reads generic(::Input[; mean]) => :ret_key, or => (:k1, :k2) for a tuple return. Any other shape raises.
  • The left of the => is a call.
  • mean is the only keyword the mini-signature may name.
  • The mini-signature declares exactly one positional argument type.
  • That type names an entry of WINDOWED_ESTIMATOR_INPUTS.
  • Every return key is a quoted symbol, and names an entry of ret_dict.

Returns

  • (gen, input, has_mean, keys_): The forwarded generic, its input type, whether the mini-signature names mean, and the ret_dict keys of its return values.

Related

source
PortfolioOptimisers.windowed_estimator_check_keyFunction
windowed_estimator_check_key(
    key::Symbol,
    dict::AbstractDict,
    what::AbstractString
) -> Symbol

Validate that key names an entry of dict, appending a did_you_mean suggestion to the error when it does not. what names the table in the message.

Arguments

  • key::Symbol: The key the declaration wrote.
  • dict::AbstractDict: The table the key must name an entry of.
  • what::AbstractString: Name of that table, written into the message.

Validation

Returns

  • key::Symbol: The key, unchanged, so a caller can check and bind in one expression.

Related

source
PortfolioOptimisers.windowed_estimator_suggestFunction
windowed_estimator_suggest(key, candidates) -> String

Suggest the nearest candidates entry to a mistyped @windowed_estimator key.

Delegates to suggest_declared_key, which holds the looser scoped configuration every declaration-key suggestion shares: Damerau-Levenshtein at min_score = 0.5, because the candidates here are compile-time constants — block keys and field_dict/ret_dict names — with nothing to leak.

Arguments

  • key: The mistyped key.
  • candidates: The keys the declaration accepts.

Returns

  • msg::String: The suggestion suffix, ready to append to a message, and empty when no candidate scores high enough.

Related

source
PortfolioOptimisers.windowed_type_docFunction
windowed_type_doc(
    name::Symbol,
    super,
    field::Symbol,
    ftype,
    default,
    noun::AbstractString,
    doctest::AbstractString,
    methods::Vector{String}
) -> Expr

Build the type docstring of a generated windowed estimator as an interpolation AST, keeping DocStringExtensions abbreviations and dictionary lookups live (see windowed_method_doc).

Algorithm

  1. Read the inner estimator's own name out of default, giving inner_ref. A call such as SimpleVariance() contributes its head; a bare name contributes itself.
  2. Open parts with the live DocStringExtensions.TYPEDEF abbreviation, the two summary sentences, the # Fields heading, and the live DocStringExtensions.FIELDS abbreviation.
  3. Push the # Constructors section, the keyword signature built from name, field, ftype and default, and the ## Validation subsection carrying the live val_dict[:oow] lookup and the window rule.
  4. Push the three propagation subsections, ## Propagated parameters, ## View parameters and ## Observation weight parameters, each naming field and w as the tags on the generated struct declare them.
  5. Push the # Examples section, fencing doctest as a jldoctest block.
  6. Push the # Related heading, the supertype, inner_ref, every entry of methods, and the four seam functions the type answers.
  7. Return parts wrapped in Expr(:string, ...), so every lookup stays live.

Arguments

  • name::Symbol: Name of the windowed estimator type.
  • super: Its supertype.
  • field::Symbol: Name of the inner estimator's field.
  • ftype: Declared type of that field.
  • default: Its keyword-constructor default.
  • noun::AbstractString: Capitalised noun phrase naming the moment, which drives the generated prose.
  • doctest::AbstractString: Body of the jldoctest block of the # Examples section, without its fences.
  • methods::Vector{String}: Cross-references to the type's generated methods, from windowed_method_ref.

Returns

  • doc::Expr: An Expr(:string, ...) holding the docstring, with every abbreviation and lookup left unevaluated.

Related

source
PortfolioOptimisers.windowed_method_refFunction
windowed_method_ref(
    gen,
    field::Symbol,
    name::Symbol,
    input::Symbol
) -> String

Render the generic(field::Name, X::Input) reference used to cross-link one generated forwarding method from the type's and its siblings' # Related sections.

Keyword arguments are deliberately omitted: Documenter resolves an @ref by positional method signature, and the two positional types already identify the method uniquely.

Arguments

  • gen: The forwarded generic.
  • field::Symbol: Name of the inner estimator's field, which is also the argument name of the generated method.
  • name::Symbol: Name of the windowed estimator type.
  • input::Symbol: Input type of the generated method, :MatNum or :VecNum.

Returns

  • ref::String: A Documenter cross-reference to the generated method. It links the code span gen(field::Name, X::Input) to that method.

Related

source
PortfolioOptimisers.windowed_method_docFunction
windowed_method_doc(
    gen,
    field::Symbol,
    name::Symbol,
    input::Symbol,
    has_mean::Bool,
    ret_keys::Vector{Symbol},
    noun::AbstractString,
    siblings::Vector{String}
) -> Expr

Build the docstring for one generated forwarding method as an interpolation AST.

Returning Expr(:string, ...) rather than a String is load-bearing: it keeps arg_dict and ret_dict lookups as live parts of the DocStr, exactly as a hand-written $(arg_dict[:dims]) would be.

Algorithm

  1. Build the signature line sig from gen, field, name and input. The matrix signature carries dims, iv and kwargs...; the vector signature carries neither. mean joins either one when has_mean is set.
  2. Open parts with sig, the summary sentences, the two steps of the generated method's own algorithm, and the arguments heading with its two prose bullets. The summary names the generic and not the type's noun, because std on a WindowedVariance computes a standard deviation and not a variance.
  3. For the matrix input, push the arg_dict[:dims] lookup as a live expression.
  4. When has_mean is set, push the mean bullet.
  5. For the matrix input, push the arg_dict[:oiv] lookup and the kwargs... bullet.
  6. Push the returns heading, then one live ret_dict lookup per entry of ret_keys.
  7. Push the related heading, the type, every entry of siblings, and windowed_preamble.
  8. Return parts wrapped in Expr(:string, ...), so every lookup stays live.

Arguments

  • gen: The forwarded generic.
  • field::Symbol: Name of the inner estimator's field, which is also the argument name of the generated method.
  • name::Symbol: Name of the windowed estimator type.
  • input::Symbol: Input type of the generated method, :MatNum or :VecNum.
  • has_mean::Bool: Whether the method declares a mean keyword.
  • ret_keys::Vector{Symbol}: The ret_dict keys documenting the return values.
  • noun::AbstractString: Capitalised noun phrase naming the moment, which drives the generated prose.
  • siblings::Vector{String}: Cross-references to the type's other generated methods, from windowed_method_ref.

Returns

  • doc::Expr: An Expr(:string, ...) holding the docstring, with every dictionary lookup left unevaluated.

Related

source
PortfolioOptimisers.windowed_method_defFunction
windowed_method_def(
    gen,
    field::Symbol,
    name::Symbol,
    input::Symbol,
    has_mean::Bool
) -> Expr

Build one generated forwarding method: resolve the window via windowed_preamble, then delegate to the inner estimator's own method.

Algorithm

  1. Build the three field accesses the body reads: field.field, the inner estimator; field.w, the observation weights; and field.window, the window specification.
  2. Build the keyword list of the signature. The matrix method takes dims, then mean when has_mean is set, then iv and kwargs.... The vector method takes mean alone, and only when has_mean is set.
  3. Build the matching keyword list of the delegated call. It carries the same names, each forwarded by value, and the iv it forwards is the windowed one.
  4. Build the body: one call to windowed_preamble that binds inner and the windowed X, and iv too for the matrix method, then a return of gen applied to inner and that X.
  5. Return the whole Expr(:function, ...).

Arguments

  • gen: The forwarded generic.
  • field::Symbol: Name of the inner estimator's field, which is also the argument name of the generated method.
  • name::Symbol: Name of the windowed estimator type.
  • input::Symbol: Input type of the generated method, :MatNum or :VecNum.
  • has_mean::Bool: Whether the method declares a mean keyword.

Returns

  • def::Expr: The Expr(:function, ...) of the forwarding method.

Related

source

FullMoment and semi moments

Moments other than the expected return can be estimated using the entire spectrum of deviations (full), or only the deviations below a target (semi/downside). These types allow us to provide such functionality.

PortfolioOptimisers.coverage_comoment_deviationsFunction
coverage_comoment_deviations(alg::FullMoment, Xo::MatNum, mu::VecNum) -> MatNum
coverage_comoment_deviations(alg::SemiMoment, Xo::MatNum, mu::VecNum) -> MatNum

Centres an available-case block on each asset's own mean, and clips the deviations where the moment algorithm asks for it.

The one line the two arms of a higher-order available-case fit differ in, taken out so that coverage_comoment_block is one body. FullMoment keeps the deviations whole and SemiMoment clips every positive one to zero, which is the same asymmetry the plain path already has.

Arguments

  • alg: Moment algorithm of the estimator.
  • Xo: The oriented block, observations × assets, whose invalid entries are still non-finite.
  • mu: Each asset's available-case mean, over its own finite and active observations.

Returns

  • Y::MatNum: The deviations, non-finite wherever Xo is.

Related

source
PortfolioOptimisers.coverage_comoment_blockFunction
coverage_comoment_block(
    alg::AbstractMomentAlgorithm,
    cvg::CoveragePolicy,
    X::AbstractMatrix{<:Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}},
    active_mask::Union{Nothing, AbstractMatrix{<:Bool}},
    dims::Int64
) -> Tuple{Any, Any, Any, Any, Any, Union{Nothing, BitVector}}

Builds the pairwise expansions and the admission mask a higher-order available-case fit reads.

The block half of the available-case arm, shared by coverage_coskewness and coverage_cokurtosis. A third and a fourth co-moment differ in which pair of expansions they multiply, and in nothing else, so this returns both expansions and lets each order take its own product. Neither order folds, so the block is seen whole and there is no state.

The deviations are centred on each asset's own available-case mean and the invalid entries are then zeroed, so an observation at which an asset has no quote contributes to neither the numerator nor the denominator of any cell that asset appears in. The mask expansion zc is the same product over the mask, so the denominator of a cell is the count of observations at which every asset of that cell is valid.

Algorithm

  1. Read the valid entries, the per-asset available-case mean and the per-asset bookkeeping of the block with coverage_valid_block.
  2. Centre and clip the block with coverage_comoment_deviations, and zero the invalid entries.
  3. Take the valid mask as integers, Mi.
  4. Build the pairwise expansions z of the deviations and zc of the mask, both observations × assets².
  5. Admit the assets with coverage_admission, against each asset's own observation count.

Arguments

  • alg: Moment algorithm of the estimator.
  • cvg: The policy the estimator carries.
  • X: Data matrix.
  • active_mask: The active mask of the Asset Panel over the window, or nothing.
  • dims: Dimension along which to perform the computation.

Returns

  • (Xo, Y, Mi, z, zc, cmsk)::Tuple: The oriented block, the zeroed deviations, the valid mask as integers, the pairwise expansion of each, and the admitted assets or nothing.

Related

source