Median expected returns

PortfolioOptimisers.MedianExpectedReturnsType
struct MedianExpectedReturns{__T_w} <: AbstractExpectedReturnsEstimator

Computes the expected returns as the per-asset median of the asset returns.

w carries optional observation weights. If w is nothing, the median is unweighted. The median resists an outlier that would move the sample mean.

Fields

  • w: Optional observation weights vector observations × 1, or a concrete subtype of DynamicAbstractWeights. If nothing, the computation is unweighted.

Constructors

MedianExpectedReturns(;    w::Option{<:ObsWeights} = nothing) -> MedianExpectedReturns

Keywords correspond to the struct's fields.

Validation

  • If w is not nothing, !isempty(w).

Propagated parameters

When factory is called on this type, the following @fprop-tagged fields are automatically propagated:

Observation weight parameters

When obs_weights_view is called on this type, the following fields are automatically indexed to the selected observations:

Examples

julia> me = MedianExpectedReturns()MedianExpectedReturns  w ┴ nothingjulia> factory(me, StatsBase.Weights([0.1, 0.2, 0.7]))MedianExpectedReturns  w ┴ StatsBase.Weights{Float64, Float64, Vector{Float64}}: [0.1, 0.2, 0.7]

Related

source
Statistics.meanMethod
Statistics.mean(me::MedianExpectedReturns, X::MatNum;
                dims::Int = 1, kwargs...)

Compute expected returns as the median of each asset.

This method returns the median of each asset across observations in X. If me.w is nothing, the median is computed directly with Statistics.median(X; dims = dims). Otherwise, the method computes a weighted median for each asset using the observation weights w.

Mathematical definition

Unweighted:

\[\begin{align} \hat{\mu}_j &= \mathrm{median}(r_{1j}, r_{2j}, \ldots, r_{Tj})\,. \end{align}\]

Where:

  • $\hat{\mu}_j$: Median expected return of asset $j$.
  • $r_{tj}$: Return of asset $j$ at time $t$.
  • $T$: Number of observations.

Weighted. The weighted median is the StatsBase weighted quantile at probability $1/2$, which interpolates between two order statistics. Order the returns of asset $j$ so that $r_{(1)j} \leq \ldots \leq r_{(T)j}$, and let $w_{(t)}$ be the weight that travels with each one:

\[\begin{align} S_m &= \sum_{t=1}^{m} w_{(t)}\,, \\ h &= \frac{1}{2} \left( \sum_{t=1}^{T} w_t - w_{(1)} \right) + w_{(1)}\,, \\ k &= \max \left\lbrace m : S_m \leq h \right\rbrace\,, \\ \hat{\mu}_j &= r_{(k)j} + \frac{h - S_k}{S_{k+1} - S_k} \left( r_{(k+1)j} - r_{(k)j} \right)\,. \end{align}\]

Where:

  • $w_{t}$: Observation weight of observation $t$.
  • $w_{(t)}$: Weight of the $t$-th smallest return, so the weights are permuted with the returns.
  • $r_{(t)j}$: $t$-th smallest return of asset $j$.
  • $S_m$: Cumulative weight of the $m$ smallest returns.
  • $h$: Cumulative weight that the probability $1/2$ corresponds to.

Two consequences follow, and both separate this from an order statistic.

  • The result is not in general one of the observed returns, because the last line interpolates between $r_{(k)j}$ and $r_{(k+1)j}$. Under equal weights it reduces to the ordinary median.
  • $w_{(1)}$ in the second line is the weight of the smallest return, not the weight of the first observation. The weights are sorted with the returns before $h$ is formed, so a sample whose smallest return arrives last gives a different $h$ from the one that reading w[1] would give, and a different result.

Arguments

  • me: Median expected returns estimator.
  • X: Data matrix of asset returns (observations × assets).
  • dims: Dimension along which to perform the computation.
  • kwargs: Additional keyword arguments (ignored).

Validation

  • dims in (1, 2).

Returns

  • mu::Matrix{<:Number}: Median vector, shaped as (1, N) if dims == 1 or (N, 1) if dims == 2.

Related

source
Statistics.meanMethod
mean(
    me::MedianExpectedReturns{<:Union{var"#s137", var"#s136"} where {var"#s137"<:DynamicAbstractWeights, var"#s136"<:AbstractWeights}},
    X::AbstractMatrix{<:Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}};
    dims,
    kwargs...
) -> Any

Weighted-median overload of mean(me::MedianExpectedReturns, X::MatNum; dims::Int = 1, kwargs...). Computes per-asset weighted median using the ObsWeights stored in me.w.

The weighted branch has no matrix-wide method to call, so it reduces one column at a time. The mathematics of one column is the weighted-quantile expression of the method above.

Algorithm

  1. Orient X with dims_oriented, so that the observations run down the columns. This step also checks dims.
  2. Resolve me.w against the oriented X with get_observation_weights, giving w.
  3. Allocate the result vector Y, of length size(X, 2).
  4. For each column i of X, take the weighted median of that column under w, giving Y[i].
  5. Insert the reduced dimension back into Y with insertdims, giving a (1, N) matrix when dims == 1 and an (N, 1) matrix when dims == 2.

Validation

  • dims in (1, 2). The check is not made by this method: dims_oriented is what raises the DomainError.
source