Online selection rules: mirror descent, its schedules and its gradient transforms: private API

PortfolioOptimisers.MirrorDescentState — Type
struct MirrorDescentState{__T_n, __T_u, __T_w0, __T_s, __T_gs} <: AbstractPartialFitState

Holds the state of a MirrorDescent rule between its updates.

The state holds the unmixed iterate, the Start Allocation that a restart returns to, the statistic of the schedule and the averages of the Gradient Transform. The mirror step reads and writes the iterate. The head plays the uniform mix of the iterate when alpha is positive, and the iterate itself otherwise.

Fields

  • n: Number of observations folded into the state.
  • u: The unmixed iterate $\boldsymbol{w}_t$, over the full pinned universe. It sums to one.
  • w0: The Start Allocation projected onto the Allocation Set, which a restart returns to.
  • s: The schedule's statistic, or nothing.
  • gs: The Gradient Transform's carrier, or nothing.

Related

source
PortfolioOptimisers.doubling_stage — Function
doubling_stage(
    sched::DoublingTrickRate,
    t::Integer
) -> Tuple{Any, Any}

Finds the stage of a DoublingTrickRate that holds period t.

Algorithm

  1. Compute base = N^2 * log(N).
  2. Set the length len of stage 0 to ceil(2 * base), and its last period fin to len.
  3. While t > fin, go to the next stage i. Set len to ceil(2^i * base), and add len to fin.

Returns

  • (len, fin)::Tuple{Int, Int}: The length of the stage that holds t, and its last period.

Related

source
PortfolioOptimisers.WindowedBestRateState — Type
struct WindowedBestRateState{__T_n, __T_B, __T_L}

Holds the state of a WindowedBestRate on the rule's carrier.

The state holds one expert allocation per rate, each an exponentiated-gradient run, and a ring of the experts' log period returns. The schedule sums the ring over the window.

Fields

  • n: Number of observations folded into the state.
  • B: The experts' allocations, assets × rates. Column k is the exponentiated-gradient run at the k-th rate, from the Start Allocation.
  • L: The experts' log period returns, window × rates. The row of period t overwrites row mod1(t, window) of this ring. Under the whole history the matrix is 1 × rates and holds a running sum.

Related

source
PortfolioOptimisers.played_allocation — Function
played_allocation(u::AbstractVector, alpha::Real) -> Any

Forms the allocation that a MirrorDescent rule plays from its iterate.

At alpha = 0 the function returns a copy of the iterate u.

Mathematical definition

\[\begin{align} \tilde{\boldsymbol{w}} &= (1 - \alpha) \boldsymbol{w} + \frac{\alpha}{N} \boldsymbol{1}\,. \end{align}\]

Where:

  • $\tilde{\boldsymbol{w}}$: Played allocation.
  • $\boldsymbol{w}$: Iterate of the rule.
  • $\alpha$: Uniform-mix share, in $[0, 1)$.
  • $N$: Number of assets.
  • $\boldsymbol{1}$: Vector of ones.

Related

source
PortfolioOptimisers.mixed_relatives — Function
mixed_relatives(x::AbstractVector, alpha::Real) -> Any

Mixes the price relatives that a MirrorDescent rule's update reads under the uniform mix alpha.

At alpha = 0 the function returns x itself.

Helmbold, Schapire, Singer and Warmuth (1998, Theorem 4.2) state the mix for price relatives with a period maximum of one, $(1 - \alpha / N) \boldsymbol{x} / \max_i x_i + (\alpha / N) \boldsymbol{1}$. Every reader of the mixed vector, the gradient of the log-wealth loss and the hint residual, gives the same result at every scale of the vector. So the function scales the floor to the period's maximum, and does not scale the price relatives down to one.

The mix of the raw price relatives with an unscaled floor is a different step, outside the proof of the theorem. Take the exponentiated gradient at the default rate and $\alpha = 0.2$, over the price relatives 1 .+ 0.02 .* randn(StableRNG(7), 60, 4). The two steps then give played allocations that differ by at most $2.6 \times 10^{-6}$. From the uniform allocation, over one period in which the first of four assets rises by half and the others stay flat, they differ by $7.0 \times 10^{-5}$.

Mathematical definition

\[\begin{align} \tilde{\boldsymbol{x}} &= \left(1 - \frac{\alpha}{N}\right) \boldsymbol{x} + \frac{\alpha}{N} \max_i x_i \boldsymbol{1}\,. \end{align}\]

Where:

  • $\tilde{\boldsymbol{x}}$: Mixed price relative vector.
  • $\boldsymbol{x}$: Price relative vector of the period.
  • $x_i$: Price relative of asset $i$.
  • $\alpha$: Uniform-mix share, in $[0, 1)$.
  • $N$: Number of assets.
  • $\boldsymbol{1}$: Vector of ones.

The result is $\max_i x_i$ times the paper's mix.

Related

source
PortfolioOptimisers.reprojection — Function
reprojection(proj::AbstractProjectionGeometry, set::BoundedAllocationSet, q::AbstractVector, w::AbstractVector)
reprojection(proj::AbstractProjectionGeometry, set::AbstractAllocationSet, q::AbstractVector, w::AbstractVector)

Projects onto the head's Allocation Set an allocation that a MirrorDescent rule plays without a step to it.

There are two such allocations. One is the uniform mix of the iterate under alpha. The other is the Start Allocation that a restart of a Learning-Rate Schedule returns to. Either can lie outside the set. The mix does when the set excludes the uniform allocation. The start does when a turnover ceiling or a MIP kind excludes it from the book the fund now holds. So the head projects them again, in the rule's own geometry, as an ExpertMixture projects its blend with blend_projection.

On a BoundedAllocationSet, the function returns an allocation inside the bounds unchanged, so the default set costs nothing. On every other set the projection is a programme, and a rule with a positive alpha solves two programmes per period.

Arguments

  • proj: The rule's geometry.
  • set: The set, resolved.
  • q: The allocation to play.
  • w: The Price-Adjusted Allocation the step trades from.

Returns

  • w'::AbstractVector: q itself on a bounded set it lies in, its projection otherwise.

Related

source
PortfolioOptimisers.investable_risk_gradient — Function
investable_risk_gradient(obj::RiskLoss, u::AbstractVector, pr::AbstractPriorResult, imsk::Nothing)
investable_risk_gradient(obj::RiskLoss, u::AbstractVector, pr::AbstractPriorResult, imsk::BitVector)

Computes the gradient of a Risk Loss on the Investable Mask of its prior result.

A leg that the window does not cover gets a zero gradient, so the loss does not move it.

Algorithm

When the mask is nothing, every asset is priced, and the method returns risk_gradient of the measure at u. Otherwise it runs these steps.

  1. Find the indices idx of the assets on the mask.
  2. Make the zero vector g of the length of u.
  3. Write risk_gradient of the measure at u[idx], against the prior viewed at idx, into g[idx]. The slice of u is not renormalised.

Related

source