Price-level expected returns: private API

PortfolioOptimisers.push_memory — Function
push_memory(
    alg::AbstractPriceLevelStatistic,
    hist::Union{Nothing, AbstractMatrix},
    x::AbstractVector
) -> Any

Appends the price relative of a row to the memory of a statistic and keeps the last memory_rows relatives.

Algorithm

  1. Read m, the number of relatives the statistic carries. Return nothing when m is zero.
  2. Make row, the relative x as a one-row matrix. Return it when hist is nothing.
  3. Stack row under hist, giving H, and return the last m rows of H.

Arguments

  • alg: The statistic.
  • hist: The carried memory, or nothing.
  • x: The price relative of the row.

Returns

  • hist'::Union{Nothing, Matrix}: The memory after the row, a new matrix.

Related

source
PortfolioOptimisers.price_levels — Function
price_levels(
    X::AbstractMatrix{<:Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}}
) -> Matrix

Reconstructs the price levels of a window of returns, with the last level at one.

The last row of the result is ones, and the row above it is 1 ./ (1 .+ r_T), where r_T is the last row of X. Each earlier row divides the row below it by one plus the return between them, back to the level before the first row of X. So the result holds size(X, 1) + 1 rows. The statistic reads the last window of them, or all of them when the window truncates.

Mathematical definition

\[\begin{align} \boldsymbol{p}_{T} &= \boldsymbol{1}\,,\quad \boldsymbol{p}_{t-1} = \boldsymbol{p}_{t} \oslash (\boldsymbol{1} + \boldsymbol{r}_{t})\,,\quad t = T, \ldots, 1\,. \end{align}\]

Where:

  • $\boldsymbol{p}_t$: Price level vector of period $t$, one entry per asset.
  • $\boldsymbol{r}_{t}$: Returns vector of period $t$, one entry per asset.
  • $T$: Number of observations.

Arguments

  • X: Returns, observations × assets.

Returns

  • P::Matrix: The levels, (observations + 1) × assets, the last row ones.

Related

source
PortfolioOptimisers.fold_levels — Function
fold_levels(
    alg::AbstractPriceLevelStatistic,
    P::AbstractMatrix
) -> Any

Runs the batch form of a folding statistic over a matrix of levels, from the first row.

Algorithm

  1. Start with no carried statistic stat and no memory hist.
  2. For each row t from the second, form the price relative x of row t over row t - 1.
  3. Append x to hist with push_memory.
  4. Step stat with fold_statistic_row.
  5. Multiply stat by the last level and return it. For a single level there is no relative, and the result is the last level.

Arguments

  • alg: The folding statistic.
  • P: The levels, levels × assets, the last row the current level.

Returns

  • stat::Vector: The statistic per asset, in units of the levels.

Related

source
PortfolioOptimisers.fold_statistic_row — Function
fold_statistic_row(
    alg::AbstractPriceLevelStatistic,
    stat::Union{Nothing, AbstractVector},
    hist::Union{Nothing, AbstractMatrix},
    x::AbstractVector
) -> Any

Runs one row of the recursion of a folding statistic, at the arity that its memory needs.

A statistic that carries no memory takes the three-argument fold_statistic, and a statistic that carries one takes the four-argument form.

Arguments

  • alg: The folding statistic.
  • stat: The carried statistic, or nothing before the first row.
  • hist: The carried memory with the row appended, or nothing.
  • x: The price relative of the row, 1 .+ r.

Returns

  • stat'::AbstractVector: The statistic after the row, a new vector.

Related

source
PortfolioOptimisers.fold_active — Function
fold_active(
    alg::AbstractPriceLevelStatistic,
    stat::AbstractVector,
    hist::Union{Nothing, AbstractMatrix},
    x::AbstractVector,
    active::AbstractVector{<:Bool}
) -> Any

Runs one row of the recursion of a folding statistic over the active assets alone, and writes the result back into the carried statistic.

The caller has already written the cold seed into stat at every asset that the mask turns off. So an inactive asset keeps that seed, and it starts the recursion from its seed when it relists. The active assets are the Coverage Universe of the row, and the recursion reads them alone. So a statistic that couples the assets, such as the regression of the kernel trend pattern, pools the listed assets and needs no mask of its own.

Algorithm

  1. When every asset is active, return the row of the recursion from fold_statistic_row.
  2. Find i, the indices of the active assets. When i is empty, return a copy of stat, which holds the cold seed at every asset.
  3. Run the row of the recursion on the active entries of stat, of the columns i of hist and of x, giving y.
  4. Make out, a copy of stat in the promoted element type, write y at i, and return it.

Arguments

  • alg: The folding statistic.
  • stat: The carried statistic, the cold seed at every inactive asset.
  • hist: The carried memory with the row appended, or nothing.
  • x: The price relative of the row, one at every asset the fold holds.
  • active: The active assets of the row.

Returns

  • stat'::AbstractVector: The statistic after the row, a new vector.

Related

source
PortfolioOptimisers.weiszfeld_sums — Function
weiszfeld_sums(
    P::AbstractMatrix,
    y::AbstractVector
) -> Tuple{Any, Any, Any, Int64}

The three sums of one step of the modified Weiszfeld iteration at a point y, over the rows of P that differ from y, and the number of rows equal to it.

Mathematical definition

\[\begin{align} \boldsymbol{s}(\boldsymbol{y}) &= \sum_{i : \boldsymbol{p}_i \neq \boldsymbol{y}} \frac{\boldsymbol{p}_i}{\lVert \boldsymbol{p}_i - \boldsymbol{y} \rVert}\,,\quad d(\boldsymbol{y}) = \sum_{i : \boldsymbol{p}_i \neq \boldsymbol{y}} \frac{1}{\lVert \boldsymbol{p}_i - \boldsymbol{y} \rVert}\,,\\ r(\boldsymbol{y}) &= \left\lVert \sum_{i : \boldsymbol{p}_i \neq \boldsymbol{y}} \frac{\boldsymbol{p}_i - \boldsymbol{y}}{\lVert \boldsymbol{p}_i - \boldsymbol{y} \rVert} \right\rVert\,,\quad \eta(\boldsymbol{y}) = \left\lvert \{ i : \boldsymbol{p}_i = \boldsymbol{y} \} \right\rvert\,. \end{align}\]

Where:

  • $\boldsymbol{p}_i$: Row $i$ of P, one point.
  • $\boldsymbol{y}$: The point the sums are taken at.
  • $r(\boldsymbol{y})$: The norm of the sum of the unit vectors from $\boldsymbol{y}$ to the other points, which is the norm of the gradient of the sum of distances where no point equals $\boldsymbol{y}$.
  • $\eta(\boldsymbol{y})$: The number of points equal to $\boldsymbol{y}$.

Arguments

  • P: The points, one per row.
  • y: The point.

Returns

  • num::Vector: The sum $\boldsymbol{s}(\boldsymbol{y})$.
  • den::Number: The sum $d(\boldsymbol{y})$, zero when every row equals y.
  • R::Number: The norm $r(\boldsymbol{y})$.
  • eta::Int: The count $\eta(\boldsymbol{y})$.

Related

source
PortfolioOptimisers.spatial_median — Function
spatial_median(
    P::AbstractMatrix,
    iters::Integer,
    tol::Real
) -> Any

The spatial median of the rows of P, by the modified Weiszfeld iteration of Vardi and Zhang (2000).

The plain Weiszfeld iteration converges slowly when the median is one of the points, and it cannot step from an iterate that equals a point. The function first tests every point with the optimality condition of Vardi and Zhang, so a median that is one of the points is found exactly. The iteration then uses the modified map, which stays defined on a point.

Mathematical definition

\[\begin{align} \boldsymbol{m} &= \underset{\boldsymbol{y}}{\arg\min} \sum_{i} \lVert \boldsymbol{p}_i - \boldsymbol{y} \rVert\,,\\ \boldsymbol{p}_k = \boldsymbol{m} &\iff r(\boldsymbol{p}_k) \leq \eta(\boldsymbol{p}_k)\,,\\ T(\boldsymbol{y}) &= \max\left(0, 1 - \frac{\eta(\boldsymbol{y})}{r(\boldsymbol{y})}\right) \frac{\boldsymbol{s}(\boldsymbol{y})}{d(\boldsymbol{y})} + \min\left(1, \frac{\eta(\boldsymbol{y})}{r(\boldsymbol{y})}\right) \boldsymbol{y}\,. \end{align}\]

Where:

  • $\boldsymbol{m}$: The spatial median.
  • $\boldsymbol{p}_i$: Row $i$ of P, one point.
  • $\boldsymbol{s}$, $d$, $r$, $\eta$: The sums and the count of weiszfeld_sums.
  • $T$: The modified Weiszfeld map. At a point that equals no row it is the plain Weiszfeld step $\boldsymbol{s} / d$, and the median is its fixed point.

Algorithm

  1. For each row k of P, take the sums at the row with weiszfeld_sums. When R <= eta, the row is the median, and the function returns it.
  2. Otherwise seed y at the coordinatewise median of the rows.
  3. Take the sums at y, and form the next iterate ynew with the map $T$.
  4. Return ynew when its Euclidean distance to y is below tol. Otherwise set y = ynew and repeat step 3, at most iters times, and then return y.

Arguments

  • P: The points, one per row.
  • iters: Maximum number of iterations.
  • tol: Convergence tolerance on the Euclidean distance between two successive iterates.

Returns

  • y::Vector: The spatial median.

Related

References

  • [51] Y. Vardi and C.-H. Zhang. The multivariate L1-median and associated data depth. Proceedings of the National Academy of Sciences 97, 1423–1426 (2000).
source

References

[51]
Y. Vardi and C.-H. Zhang. The multivariate L1-median and associated data depth. Proceedings of the National Academy of Sciences 97, 1423–1426 (2000).