The programme of a programme Allocation Set: private API

PortfolioOptimisers.calibrated — Function
calibrated(x)
calibrated(x::AbstractVector)

Whether a penalty slot or a norm-ceiling slot holds a Calibration Rule.

A Calibration Rule resolves against the prior result, so a calibrated slot reads the head's rows. The test reads the slot itself and every slot of the term it holds, which calibration_slots lists. A vector is calibrated when one of its entries is.

Returns

  • ::Bool: true when the slot, or a slot of its term, holds a Calibration Rule.

Related

source
PortfolioOptimisers.resolve_allocation_set_rows — Function
resolve_allocation_set_rows(
    _::ProgrammeAllocationSet,
    _::AbstractPriorResult,
    _::Nothing
)

Resolves the slots of a ProgrammeAllocationSet that read the head's rows, at every step.

This is the second stage of the set's resolution. resolve_allocation_set resolves the slots keyed by name once per fold. This function resolves the rest on the prior result pr that is fitted on the rows carrier rd, as processed_jump_optimiser_attributes and assemble_jump_model! resolve them for a batch head. A set that reads no rows has no prior, and its slots come back as they are.

Algorithm

The method on a prior result runs these steps.

  1. Read strict from the current ProjectionStep.
  2. When lcse is keyed by exposure, build lcsr from it through linear_constraints with the prior's factor regression pr.rr and the rows. Otherwise lcsr is lcse.
  3. Fit the centrality rows ctr and the phylogeny result plr on the prior and the rows.
  4. Resolve the return floor ret against the prior.
  5. Resolve the Calibration Rules of the norm ceilings l2c, linfc and lpc, and of the penalties l1, l2, lp and linf, against the prior and the set's solver.
  6. Return the resolved slots and rd.

Arguments

  • set: The programme set, resolved once per fold.
  • pr: The prior result fitted on rd, or nothing when the set reads no rows.
  • rd: The rows carrier the head holds, a ReturnsResult.

Validation

  • A prior result with no carrier. An IsNothingError is thrown. The prior is fitted on the carrier, so this pair does not occur in the library.

Returns

  • attrs::NamedTuple: lcsr, ctr, plr, ret, l2c, lpc, linfc, l1, l2, lp, linf, and the rows carrier rd, which is nothing when the set reads no rows.

Related

source
PortfolioOptimisers.add_allocation_set_constraints! — Function
add_allocation_set_constraints!(model::JuMP.Model, set::BoundedAllocationSet, w::AbstractVector, X)
add_allocation_set_constraints!(model::JuMP.Model, set::ProgrammeAllocationSet, w::AbstractVector, X)

Adds the constraints of a resolved Allocation Set to a JuMP head's model during its assembly.

This is the arm of the Allocation Set Constraint that a FollowTheLeader rule appends to its held optimiser. The head's builders have already registered their named entries: the weight bounds, the budgets, and the turnover and tracking-error terms under their indices. So this function adds the set's rows under other names than the bare projection model uses. It adds the bounds, the budget of one, and the short and gross budgets as anonymous rows. It adds every other kind through the same builders as set_allocation_set_constraints!, in the same order. A builder that takes a prefix gets :aset_. A builder that takes an index gets the first index the model has not used. The function registers the head's w under the prefix, so the entries of a set's risk ceiling never collide with the head's own risk measures.

The set's penalties and the p · tr(W) term of its semidefinite phylogeny go into the Objective Penalty, which the head's objective builder adds to the head's objective. The tracking errors read the head's rows carrier X and its row count. The set's prior is fitted on that carrier, as the leader's prior is. The leader's model trades only the Investable Mask of its own prior, so assert_set_prior_priced refuses a set prior that prices fewer assets than the leader's.

Some builders register their entries under one fixed name per model: a MIP kind, the exact long-short pin, a norm ceiling, a penalty and an integer phylogeny. State such a kind on the set or on the held optimiser, not on both. When it is on both, the second registration fails with an error that names the entry.

Algorithm

  1. Add the bounds and the budgets as anonymous rows with add_allocation_set_bounds!.
  2. On a programme set, fit the set's prior on X with allocation_set_prior, and add every other kind with assemble_allocation_set! under the prefix :aset_.

Arguments

  • model::JuMP.Model: The JuMP optimisation model.
  • set: The Allocation Set, resolved once per fold.
  • w: The Price-Adjusted Allocation the step trades from.
  • X: The rows carrier the head holds through the period, a ReturnsResult, or nothing.

Returns

  • nothing.

Related

source
PortfolioOptimisers.assemble_allocation_set! — Function
assemble_allocation_set!(
    model::Model,
    set::ProgrammeAllocationSet,
    w::AbstractVector,
    X::Union{Nothing, ReturnsResult},
    pr::Union{Nothing, AbstractPriorResult},
    prefix::Symbol
)

Adds every kind of a programme set other than the bounds and the budgets, in the order assemble_jump_model! runs them.

Both arms call it after they add the bounds. The bare projection model passes the empty prefix Symbol(""). A leader's model passes :aset_. Then the builders that take an index use the first free index, and the head's w is registered under the prefix.

Algorithm

  1. Check that the prior prices every asset in X with assert_set_prior_priced.
  2. Resolve the slots that read the rows with resolve_allocation_set_rows. This gives lcsr, ctr, plr, ret, l2c, lpc, linfc, l1, l2, lp, linf and rd.
  3. When prefix is not empty, register the model's w under it, with the owner w_owner = Symbol(""). Then weights_prefix keeps the lifted W, variance_flag and rc_variance in the bare namespace, because the weights are the leader's.
  4. Add the linear rows lcsr, then the centrality rows ctr.
  5. Add the MIP kinds, then the sub-group MIP kinds.
  6. Add the turnover ceilings, each with its reference replaced by w, through set_allocation_turnover!.
  7. When the set has tracking errors, resolve each against w, so that a benchmark that is not fixed takes w. Cut each benchmark series to the row count of the prior with prefix_tracking_benchmark, and add the rows.
  8. Add the norm ceilings l2c, lpc and linfc.
  9. Add the penalties l1, l2, lp and linf to the Objective Penalty.
  10. Add the risk ceilings with set_allocation_risk_ceiling!.
  11. Add the return floor ret with set_allocation_return_floor!.
  12. Add the integer phylogeny rows and the semidefinite phylogeny rows of plr.
  13. Add the custom constraints.

Arguments

  • model::JuMP.Model: The JuMP optimisation model.
  • set: The programme set, resolved once per fold.
  • w: The Price-Adjusted Allocation the step trades from.
  • X: The rows carrier the head holds through the period, a ReturnsResult, or nothing.
  • pr: The set's prior result on X, or nothing when the set reads no rows.
  • prefix: Symbol("") on the bare projection model, :aset_ in a leader's model.

Returns

  • nothing.

Related

source
PortfolioOptimisers.prefix_tracking_benchmark — Function
prefix_tracking_benchmark(tr, T::Integer)
prefix_tracking_benchmark(tr::TrackingError{<:ReturnsTracking}, T::Integer)
prefix_tracking_benchmark(trs::AbstractVector, T::Integer)

Cuts a ReturnsTracking benchmark of a programme set to its first T entries when it is longer.

The head's rows are the first rows of the fold, up to the current period, so a benchmark series stated over the whole fold aligns with them by its first T entries. A benchmark shorter than the rows goes to the builder unchanged, and the builder throws a DimensionMismatch. A weights benchmark reads the rows themselves and is returned unchanged. A vector of tracking errors is cut entry by entry.

Arguments

  • tr: A tracking error, a vector of them, or any other value.
  • T: The row count of the head's rows.

Returns

  • tr': tr with each returns benchmark cut to T entries.

Related

source
PortfolioOptimisers.set_allocation_turnover! — Function
set_allocation_turnover!(model::JuMP.Model, tn::Nothing, bare::Bool)
set_allocation_turnover!(model::JuMP.Model, tn::Turnover, bare::Bool)
set_allocation_turnover!(model::JuMP.Model, tns::VecTn, bare::Bool)

Adds a programme set's turnover ceilings, whose reference is already the step's Price-Adjusted Allocation.

On the bare projection model the ceilings take the indices one to the number of ceilings. In a leader's model each ceiling takes the first index that the model has not used, so the set's terms do not replace the head's own.

Arguments

  • model::JuMP.Model: The JuMP optimisation model.
  • tn: The turnover ceilings, one Turnover, a vector of them, or nothing.
  • bare: true on the bare projection model, false in a leader's model.

Returns

  • nothing.

Related

source
PortfolioOptimisers.set_allocation_return_floor! — Function
set_allocation_return_floor!(model::JuMP.Model, ret::Nothing, pr, bare::Bool; kwargs...)
set_allocation_return_floor!(model::JuMP.Model, ret::JuMPReturnsEstimator, pr::AbstractPriorResult, bare::Bool; kwargs...)
set_allocation_return_floor!(model::JuMP.Model, rets::VecJRE, pr::AbstractPriorResult, bare::Bool; kwargs...)

Adds a programme set's return floor.

For each term, the per-index arm of set_return_constraints! writes the return expression on the prior's expected returns and the row of its settings.lb. The term's scale is set to one first. On the bare projection model the terms take the indices one to the number of terms. In a leader's model each term takes the first index the model has not used. The function does not use a term in the objective, as a scalarised return or the normalisation of a ratio, because the objective of the projection is the geometry's divergence.

Arguments

  • model::JuMP.Model: The JuMP optimisation model.
  • ret: The return floor, one return estimator, a vector of them, or nothing.
  • pr: The set's prior result.
  • bare: true on the bare projection model, false in a leader's model.
  • kwargs...: Forwarded to set_return_constraints!. The rows carrier arrives as rd.

Returns

  • nothing.

Related

source
PortfolioOptimisers.add_allocation_set_bounds! — Function
add_allocation_set_bounds!(
    model::Model,
    wb::WeightBounds,
    sbgt,
    gbgt
)

Adds an Allocation Set's weight bounds, its budget of one, and its short and gross budgets to a model that already names its own.

The rows are the inequalities that set_allocation_set_bounds! writes, registered under no name. The short and gross budgets read the model's long and short parts when the model holds them. Otherwise the function makes two anonymous vectors of parts and bounds them by w.

JuMP formulation

Variables

  • w, k: read from the model.
  • lw, sw: long and short parts. They are read from the model when it holds them. Otherwise, when the set has a short or a gross budget, they are two anonymous vectors of $N$ variables, each bounded below by zero.

Constraints

Every row is registered under no name.

  • $s_c (\boldsymbol{w} - k \boldsymbol{l}) \geq 0$, when an entry of $\boldsymbol{l}$ is finite.
  • $s_c (\boldsymbol{w} - k \boldsymbol{u}) \leq 0$, when an entry of $\boldsymbol{u}$ is finite.
  • $s_c \left(\sum_{i=1}^{N} w_i - k\right) = 0$.
  • $s_c (\boldsymbol{w} - \boldsymbol{lw}) \leq 0$ and $s_c (\boldsymbol{w} + \boldsymbol{sw}) \geq 0$, when the function makes the parts.
  • The rows of anonymous_budget_rows! on $\sum_i sw_i$ for the short budget, and on $\sum_i (lw_i + sw_i)$ for the gross budget.

Where:

  • $s_c$: Constraint scale. It multiplies both sides of a row, so a positive value leaves the feasible set unchanged.
  • $k$: Budget scaling / homogenisation variable.
  • $\boldsymbol{w}$: Portfolio weights vector $N \times 1$.
  • $N$: Number of assets.
  • $\boldsymbol{l}$, $\boldsymbol{u}$: Lower and upper weight bounds of the set, resolved.
  • $\boldsymbol{lw}$, $\boldsymbol{sw}$: Long and short parts of $\boldsymbol{w}$, each $N \times 1$ and non-negative.

Arguments

  • model::JuMP.Model: The JuMP optimisation model.
  • wb: The set's resolved WeightBounds.
  • sbgt: The short budget, a number, a BudgetRange, or nothing.
  • gbgt: The gross budget, a number, a BudgetRange, or nothing.

Validation

Related

source
PortfolioOptimisers.anonymous_budget_rows! — Function
anonymous_budget_rows!(model::JuMP.Model, expr, bgt::Nothing)
anonymous_budget_rows!(model::JuMP.Model, expr, bgt::Number)
anonymous_budget_rows!(model::JuMP.Model, expr, bgt::BudgetRange)

Adds one budget on an expression as rows under no name.

A number fixes the expression at k · bgt. A BudgetRange bounds it on each side that is set. nothing adds no row.

JuMP formulation

Variables

  • k: read from the model.

Constraints

Every row is registered under no name.

  • $s_c (e - k b) = 0$, when the budget is a number.
  • $s_c (e - k b_l) \geq 0$ and $s_c (e - k b_u) \leq 0$, when the budget is a range, each row only when its bound is set.

Where:

  • $s_c$: Constraint scale. It multiplies both sides of a row, so a positive value leaves the feasible set unchanged.
  • $k$: Budget scaling / homogenisation variable.
  • $e$: The expression the budget bounds.
  • $b$: The budget, when it is a number.
  • $b_l$, $b_u$: Lower and upper bounds of the range.

Arguments

  • model::JuMP.Model: The JuMP optimisation model.
  • expr: The expression the budget bounds.
  • bgt: A number, a BudgetRange, or nothing.

Returns

  • nothing.

Related

source
PortfolioOptimisers.set_allocation_set_bounds! — Function
set_allocation_set_bounds!(model::JuMP.Model, wb::WeightBounds, sbgt = nothing, gbgt = nothing)

Adds an Allocation Set's weight bounds, its budget of one, and its short and gross budgets to the bare projection model.

set_weight_constraints! writes the bounds, the short budget and the gross budget, with no net budget. A negative lower bound then builds the long and short parts of w and leaves the long part free. Under JuMPOptimiser, a net budget with no short budget fixes the long part at the net budget, which forbids every short position. An Allocation Set lets the book hold short positions, so the function passes no net budget there. It adds Σw = k after that, through set_budget_constraints!.

Algorithm

  1. Check the bounds against the budget with assert_feasible_bounds.
  2. Add the bounds, the short budget and the gross budget with set_weight_constraints!, with no net budget.
  3. Add the budget Σw = k with set_budget_constraints!.

Arguments

  • model::JuMP.Model: The JuMP optimisation model.
  • wb: The set's resolved WeightBounds.
  • sbgt: The short budget, a number, a BudgetRange, or nothing.
  • gbgt: The gross budget, a number, a BudgetRange, or nothing.

Validation

Related

source
PortfolioOptimisers.set_projection_objective! — Function
set_projection_objective!(model::JuMP.Model, proj::EuclideanProjection, q::AbstractVector)
set_projection_objective!(model::JuMP.Model, proj::EntropicProjection, q::AbstractVector)
set_projection_objective!(model::JuMP.Model, proj::GramProjection, q::AbstractVector)
set_projection_objective!(model::JuMP.Model, proj::DiagonalProjection, q::AbstractVector)
set_projection_objective!(model::JuMP.Model, proj::TsallisProjection, q::AbstractVector)
set_projection_objective!(model::JuMP.Model, proj::LogBarrierProjection, q::AbstractVector)

Sets the objective of the projection programme to the geometry's divergence from the raw step q, plus the Objective Penalty.

Each arm writes its divergence $f$ as a conic model and hands it to set_divergence_objective!, which adds the penalty and sets the objective.

Mathematical definition

\[\begin{align} f_{\mathrm{E}}(\boldsymbol{w}) &= \lVert \boldsymbol{w} - \boldsymbol{q} \rVert_2\,, \\ f_{\mathrm{E}}^{\pi}(\boldsymbol{w}) &= \frac{1}{2} \lVert \boldsymbol{w} - \boldsymbol{q} \rVert_2^2\,, \\ f_{\mathrm{G}}(\boldsymbol{w}) &= \lVert \mathbf{G} (\boldsymbol{w} - \boldsymbol{q}) \rVert_2\,, \\ f_{\mathrm{G}}^{\pi}(\boldsymbol{w}) &= \frac{1}{2} \lVert \mathbf{G} (\boldsymbol{w} - \boldsymbol{q}) \rVert_2^2\,, \\ f_{\mathrm{KL}}(\boldsymbol{w}) &= \sum_{i \in P} w_i \log \frac{w_i}{q_i}\,, \\ f_{\alpha}(\boldsymbol{w}) &= \frac{1}{1 - \alpha} \sum_{i \in P} \left( q_i^{\alpha - 1} w_i - \frac{w_i^{\alpha}}{\alpha} \right)\,, \\ f_{\mathrm{IS}}(\boldsymbol{w}) &= \sum_{i \in P} \left( \frac{w_i}{q_i} - \log w_i \right)\,. \end{align}\]

Where:

  • $\boldsymbol{w}$: Portfolio weights vector $N \times 1$.
  • $\boldsymbol{q}$: Raw step, the vector the rule's unconstrained step gives before the projection onto the Allocation Set.
  • $\pi$: Objective Penalty, the sum of the set's penalties and of the other terms added to the objective before it.
  • $f_{\mathrm{E}}$, $f_{\mathrm{E}}^{\pi}$: Divergence of EuclideanProjection, without and with an Objective Penalty.
  • $f_{\mathrm{G}}$, $f_{\mathrm{G}}^{\pi}$: Divergence of GramProjection and of DiagonalProjection, without and with an Objective Penalty.
  • $\mathbf{G}$: Upper Cholesky factor of the geometry's Gram matrix $\mathbf{A}$, so $\mathbf{G}^\intercal \mathbf{G} = \mathbf{A}$. Under DiagonalProjection it is $\mathrm{diag}(\sqrt{\boldsymbol{h}})$ and $\mathbf{A} = \mathrm{diag}(\boldsymbol{h})$.
  • $f_{\mathrm{KL}}$: Relative entropy, the divergence of EntropicProjection.
  • $f_{\alpha}$: Tsallis divergence, the divergence of TsallisProjection.
  • $\alpha$: Power of the Tsallis potential, in $(0, 1)$.
  • $f_{\mathrm{IS}}$: Itakura–Saito divergence, the divergence of LogBarrierProjection.
  • $P = \{i : q_i > 0\}$: Positive entries of the raw step. Under the entropic, Tsallis and log-barrier geometries every $w_i$ with $i \notin P$ is zero.
  • $\Psi$: Potential of the Projection Geometry.
  • $D_\Psi$: Bregman divergence of $\Psi$.

Each $f$ differs from the Bregman divergence $D_\Psi(\boldsymbol{w}, \boldsymbol{q})$ of its geometry's potential by a term that is constant in $\boldsymbol{w}$ on the budget $\sum_i w_i = 1$, so each has the minimiser of $D_\Psi$ and a penalty added to it weighs against $D_\Psi$ itself. $f_{\mathrm{E}}^{\pi}$ and $f_{\mathrm{G}}^{\pi}$ are these Bregman divergences, of $\tfrac{1}{2} \lVert \boldsymbol{w} \rVert_2^2$ and $\tfrac{1}{2} \boldsymbol{w}^\intercal \mathbf{A} \boldsymbol{w}$. The norm $f_{\mathrm{E}}$ has the same minimiser as $f_{\mathrm{E}}^{\pi}$ when no penalty is present, and $f_{\mathrm{G}}$ has the same minimiser as $f_{\mathrm{G}}^{\pi}$. A penalty $\pi$ added to the norm does not move the minimiser off a feasible $\boldsymbol{q}$ while the dual norm of $\nabla \pi(\boldsymbol{q})$ is at most one, because the subdifferential of a norm at zero is its dual unit ball. Added to the squared norm, it gives the proximal step.

JuMP formulation

Variables

  • w: read from the model.
  • t_proj: created. It is a scalar under the Euclidean, Gram, diagonal and entropic arms, and a vector with one entry for each index in $P$ under the Tsallis and log-barrier arms.

Constraints

  • Euclidean, Gram and diagonal arms: the row of set_distance_cone! on $\boldsymbol{x} = s_c (\boldsymbol{w} - \boldsymbol{q})$ or $\boldsymbol{x} = s_c \mathbf{G} (\boldsymbol{w} - \boldsymbol{q})$.
  • Entropic arm, proj_zero: $s_c w_i = 0$ for each $i \notin P$, when such an $i$ exists.
  • Entropic arm, proj_rec: $s_c t \geq \sum_{i \in P} s_c w_i \log (w_i / q_i)$, the relative entropy cone over $(s_c t, s_c \boldsymbol{q}_P, s_c \boldsymbol{w}_P)$.
  • Tsallis arm, under no name: $t_j \leq w_j^{\alpha}$ for each $j \in P$, the power cone of power $\alpha$ over $(s_c w_j, s_c, s_c t_j)$.
  • Log-barrier arm, under no name: $t_j \leq \log w_j$ for each $j \in P$, the exponential cone over $(s_c t_j, s_c, s_c w_j)$.
  • Tsallis and log-barrier arms: the rows of barrier_objective_entries, which fix $w_i = 0$ for each $i \notin P$.

The divergence each arm hands to set_divergence_objective! is $t$ under the Euclidean, Gram, diagonal and entropic arms, $\frac{1}{1 - \alpha} \left( \sum_{j \in P} q_j^{\alpha - 1} w_j - \frac{1}{\alpha} \sum_{j \in P} t_j \right)$ under the Tsallis arm, and $\sum_{j \in P} w_j / q_j - \sum_{j \in P} t_j$ under the log-barrier arm.

Where:

  • $s_c$: Constraint scale. It multiplies both sides of a row, so a positive value leaves the feasible set unchanged.
  • $t$, $t_j$: The entries of t_proj.
  • $\boldsymbol{q}_P$, $\boldsymbol{w}_P$: The entries of $\boldsymbol{q}$ and $\boldsymbol{w}$ at the indices in $P$.

Arguments

  • model::JuMP.Model: The JuMP optimisation model.
  • proj: The Projection Geometry.
  • q: The raw step.

Validation

Related

source
PortfolioOptimisers.set_distance_cone! — Function
set_distance_cone!(model::Model, t, x, sc)

Bounds a projection's distance variable t below by the norm of the scaled deviation x, or by half its square when the model holds an Objective Penalty.

Without a penalty the function writes the second-order cone. Its minimum is sharp, and the solver finds it to a tight tolerance. With a penalty it writes the rotated second-order cone, because a penalty added to the norm does not move the minimiser off a feasible raw step while its gradient is small, and added to half the square it gives the proximal step. The penalty is present when the set's penalties, a custom objective term or the p · tr(W) term of a semidefinite phylogeny were added before the objective. set_projection_objective! states the two divergences.

JuMP formulation

Variables

  • t: read. It is the caller's distance variable.

Constraints

  • proj_soc: $(s_c t, \boldsymbol{x}) \in \mathcal{K}_{\mathrm{SOC}}$, that is $t \geq \lVert \boldsymbol{x} \rVert_2 / s_c$, when the model holds no Objective Penalty.
  • proj_rsoc: $(s_c t, s_c, \boldsymbol{x}) \in \mathcal{K}_{\mathrm{RSOC}}$, that is $t \geq \lVert \boldsymbol{x} \rVert_2^2 / (2 s_c^2)$, when it holds one.

Where:

  • $s_c$: Constraint scale. It multiplies both sides of a row, so a positive value leaves the feasible set unchanged.
  • $\boldsymbol{x}$: The scaled deviation, $s_c (\boldsymbol{w} - \boldsymbol{q})$ or $s_c \mathbf{G} (\boldsymbol{w} - \boldsymbol{q})$, so the two rows give $t \geq \lVert \boldsymbol{w} - \boldsymbol{q} \rVert_2$ and $t \geq \tfrac{1}{2} \lVert \boldsymbol{w} - \boldsymbol{q} \rVert_2^2$ in the Euclidean geometry.
  • $\mathcal{K}_{\mathrm{SOC}} = \{(u, \boldsymbol{v}) : u \geq \lVert \boldsymbol{v} \rVert_2\}$: Second-order cone.
  • $\mathcal{K}_{\mathrm{RSOC}} = \{(u, v, \boldsymbol{z}) : 2 u v \geq \lVert \boldsymbol{z} \rVert_2^2,\, u \geq 0,\, v \geq 0\}$: Rotated second-order cone.

Arguments

  • model::JuMP.Model: The JuMP optimisation model.
  • t: The distance variable.
  • x: The scaled deviation, a vector of affine expressions.
  • sc: The constraint scale.

Returns

  • nothing.

Related

source
PortfolioOptimisers.set_divergence_objective! — Function
set_divergence_objective!(model::Model, div, so)

Sets the projection programme's objective to the minimum of the geometry's divergence div plus the Objective Penalty, times the objective scale.

The penalty holds the set's penalties, its custom objective terms and the p · tr(W) term of its semidefinite phylogeny. It enters through add_penalty_to_objective!, which every JuMP head's objective also uses. With no penalty the objective is the divergence alone, which is the projection that the papers of the rules state.

JuMP formulation

Expressions

  • obj_expr: $d + \pi$. The function registers $d$ first and then adds the model's op when it holds one.

Objective

  • Min: $s_o$ times obj_expr.

Where:

  • $d$: The divergence div of the geometry.
  • $\pi$: The Objective Penalty op, zero when the model holds none.
  • $s_o$: Objective scale. It multiplies the objective, so a positive value leaves the argument of the optimum unchanged.

Arguments

  • model::JuMP.Model: The JuMP optimisation model.
  • div: The divergence, an affine expression or a variable.
  • so: The objective scale.

Returns

  • nothing.

Related

source
PortfolioOptimisers.barrier_objective_entries — Function
barrier_objective_entries(
    model::Model,
    q::AbstractVector
) -> NTuple{4, Any}

Prepares the entries that a barrier objective is written over.

It refuses a raw step with a negative entry or with no positive entry. It fixes the weight of every zero entry of q at zero. It returns the positive indices, the model's w and the two scales.

JuMP formulation

Variables

  • w: read from the model.

Constraints

  • Under no name: $s_c w_i = 0$ for each $i$ with $q_i = 0$.

Where:

  • $s_c$: Constraint scale. It multiplies both sides of a row, so a positive value leaves the feasible set unchanged.
  • $\boldsymbol{q}$: Raw step, the vector the rule's unconstrained step gives before the projection onto the Allocation Set.

Arguments

  • model::JuMP.Model: The JuMP optimisation model.
  • q: The raw step.

Validation

  • all(>= 0, q). A DomainError is thrown otherwise.
  • sum(q) > 0. A DomainError is thrown otherwise.

Returns

  • (pos, w, sc, so)::Tuple: The indices of the positive entries of q, the model's w, the constraint scale and the objective scale.

Related

source
PortfolioOptimisers.projection_solver — Function
projection_solver(proj::AbstractProjectionGeometry, set::ProgrammeAllocationSet)
projection_solver(proj::GramProjection, set::AbstractAllocationSet)
projection_solver(proj::GramProjection, set::ProgrammeAllocationSet)
projection_solver(proj::GramProjection, set::BoundedAllocationSet)
projection_solver(proj::AbstractProjectionGeometry, set::BoundedAllocationSet)

Returns the solver a projection programme runs on.

A GramProjection carries its own solver, and the programme uses it on every set. Every other geometry uses the programme set's solver. On a BoundedAllocationSet every geometry other than the Gram one is a scalar root with no programme, so the function throws an ArgumentError that names the geometry. project does not call it in that case.

Returns

  • slv: The solver, or a vector of solvers.

Related

source
PortfolioOptimisers.allocation_set_ready — Function
allocation_set_ready(set::AbstractAllocationSet, X)
allocation_set_ready(set::ProgrammeAllocationSet, X)

Tells whether the set's constraints can be built on the rows X of the step.

A programme set that fits its prior on the rows cannot be built while the head holds fewer than two rows, because a covariance of one observation does not exist. Every other set can always be built.

Returns

  • reason: nothing when the constraints can be built, and otherwise the reason for the Held Step as a String.

Related

source
PortfolioOptimisers.projection_programme — Function
projection_programme(
    proj::AbstractProjectionGeometry,
    set::AbstractAllocationSet,
    q::AbstractVector,
    w::AbstractVector
) -> Any

Projects the raw step q onto the Allocation Set in the geometry's divergence, as a JuMP programme.

A solved programme returns its weights. A programme that does not solve, or whose constraints cannot be built on the step's rows (allocation_set_ready), is a Held Step. Then record_held_step! writes the record to the current ProjectionStep, and the function returns a copy of w, the book the fund already holds.

A set that reads the rows fits its prior on the step's rows carrier, as a batch head does. The programme then runs on the prior's Investable Mask, also as a batch head does. It solves over the assets the prior prices, and gives a zero weight to every other asset. Under a time-varying panel, an asset without a listing for part of the window is outside the universe of a plain prior until the window no longer holds that part. So the programme gives it a zero, which is the one zero in the recursion's allocation that the rule's step did not make. A batch head gives the same zero. The asset comes back at the first step at which the prior prices it, and the rule's geometry decides whether the raw step then gives it weight, as for a zero in the Start Allocation. A set that reads no rows fits no prior and solves over the full universe.

Mathematical definition

\[\begin{align} \boldsymbol{w}^{+} &= \underset{\boldsymbol{w} \in \mathcal{W}}{\arg\min} \; D_\Psi(\boldsymbol{w}, \boldsymbol{q}) + \pi(\boldsymbol{w})\,. \end{align}\]

Where:

  • $\boldsymbol{w}^{+}$: Projected allocation, the answer of the projection onto the Allocation Set.
  • $\mathcal{W}$: Allocation Set. Under an Investable Mask it also holds $w_i = 0$ for every asset $i$ outside the mask.
  • $D_\Psi$: Bregman divergence of $\Psi$.
  • $\Psi$: Potential of the Projection Geometry.
  • $\boldsymbol{q}$: Raw step, the vector the rule's unconstrained step gives before the projection onto the Allocation Set.
  • $\pi$: Objective Penalty of the set, zero when the set has no penalty.

set_projection_objective! states the objective each geometry writes for $D_\Psi$.

Algorithm

  1. Read the rows X of the current ProjectionStep.
  2. When allocation_set_ready gives a reason, record the Held Step with it and return a copy of w. Stop.
  3. Fit the set's prior pr on X with allocation_set_prior, and read its Investable Mask imsk.
  4. Reduce set, X and pr to the mask with programme_investable_reduction. View q and w at the mask, which gives q and wr.
  5. Make a bare model with the set's two scales and the row count of X, zero when there are no rows. Register k = 1, and the vector w of weight variables.
  6. Add the set's constraints with set_allocation_set_constraints!, with wr as the reference.
  7. Add the set's custom objective terms with add_custom_objective_term!, with the geometry as the objective and the set as the owner.
  8. Set the objective with set_projection_objective!.
  9. Solve on projection_solver. When the solve succeeds, return the solution with a zero at every asset outside the mask. Otherwise record the Held Step with the solver trials, and return a copy of w.

JuMP formulation

Variables

  • w: created, with the base name w, one variable for each asset in the mask. It is registered in the Model State under the key w.

Expressions

  • k: the constant one.

Where:

  • $\boldsymbol{w}$: Portfolio weights vector $N \times 1$.
  • $k$: Budget scaling / homogenisation variable.

Arguments

  • proj: The geometry.
  • set: The set, resolved.
  • q: The raw step.
  • w: The Price-Adjusted Allocation the step trades from.

Returns

  • w'::Vector: The projected allocation, or w copied on a Held Step.

Related

source
PortfolioOptimisers.programme_investable_reduction — Function
programme_investable_reduction(imsk::Nothing, set::AbstractAllocationSet, X::Option{<:ReturnsResult}, pr)
programme_investable_reduction(imsk::BitVector, set::AbstractAllocationSet, X::ReturnsResult, pr::AbstractPriorResult)
programme_investable_reduction(imsk::BitVector, set::AbstractAllocationSet, X, pr)

Reduces the set, the rows carrier and the set's prior to the prior's Investable Mask.

The projection programme then runs on the assets the prior can price, as a batch head does with investable_reduction. The caller views the raw step and the Price-Adjusted Allocation at the same mask with investable_weights_view.

Dispatch selects one of three methods. A nothing mask means that every asset is investable, which is true at every step on a static panel and at every step of a set that reads no rows. That method returns its arguments unchanged. The BitVector method takes the views at findall(imsk). The third method refuses a mask with no carrier or no prior, because a mask comes from a prior fitted on a carrier, so this pair does not occur in the library. The set is viewed against the carrier's full returns matrix, because a view of a tracking estimator needs it. The Price-Adjusted Allocation is cut to the mask and is not renormalised, as the batch reduction cuts a turnover reference. So an asset the prior cannot price leaves the reference, and the programme allocates the budget of one over the investable assets. The function writes no warning for such an asset, because the Investable Mask of the read-out already shows it at every step.

Validation

  • A mask with no carrier or no prior. An IsNothingError is thrown.

Returns

  • (set, X, pr)::Tuple: The three reduced to the mask, or unchanged.

Related

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

Projects the blend of an ExpertMixture onto the head's set a second time, when the set needs it.

On a BoundedAllocationSet dispatch returns the blend unchanged. The bounds and the budget define a convex set, so a convex blend of allocations in it is in it too, and the projection is the identity. On every other set the function calls project, which gives the repair that a turnover ceiling or a MIP kind needs.

Related

source