Budget Constraints: private API

PortfolioOptimisers.set_budget_constraints!Function
set_budget_constraints!(args...)
set_budget_constraints!(model::JuMP.Model, val::Number, w::VecNum)
set_budget_constraints!(model::JuMP.Model, bgt::BudgetRange, w::VecNum)
set_budget_constraints!(model::JuMP.Model, bgt::BudgetCosts, w::VecNum)
set_budget_constraints!(model::JuMP.Model, bgt::BudgetMarketImpact, w::VecNum)

Add budget constraints to the JuMP optimisation model.

The fall-through method does nothing. The concrete methods add the appropriate portfolio budget constraint based on the type of budget specification provided.

Mathematical definition

Fixed / range:

\[\begin{align} k \cdot \mathrm{lb} &\leq \sum_i w_i \leq k \cdot \mathrm{ub}\,. \end{align}\]

Where:

  • $\boldsymbol{w}$: Portfolio weights vector $N \times 1$.
  • $k$: Budget scaling / homogenisation variable.
  • $\mathrm{lb}$, $\mathrm{ub}$: Lower and upper budget bounds.

Linear cost budget (BudgetCosts):

\[\begin{align} \sum_i w_i + \boldsymbol{v}_p^\intercal \boldsymbol{w}_p + \boldsymbol{v}_n^\intercal \boldsymbol{w}_n \in [k\,\mathrm{lb},\; k\,\mathrm{ub}]\,. \end{align}\]

Where:

  • $\boldsymbol{w}$: Portfolio weights vector $N \times 1$.
  • $k$: Budget scaling / homogenisation variable.
  • $\boldsymbol{w}_p$, $\boldsymbol{w}_n$: Positive and negative weight increments.
  • $\boldsymbol{v}_p$, $\boldsymbol{v}_n$: Cost coefficient vectors for positive and negative changes.
  • $\mathrm{lb}$, $\mathrm{ub}$: Lower and upper budget bounds.

Power-law market-impact budget (BudgetMarketImpact):

\[\begin{align} \sum_i w_i + \boldsymbol{v}_p^\intercal \boldsymbol{w}_p^{1/\beta} + \boldsymbol{v}_n^\intercal \boldsymbol{w}_n^{1/\beta} \in [k\,\mathrm{lb},\; k\,\mathrm{ub}]\,. \end{align}\]

Where:

  • $\boldsymbol{w}$: Portfolio weights vector $N \times 1$.
  • $k$: Budget scaling / homogenisation variable.
  • $\boldsymbol{w}_p$, $\boldsymbol{w}_n$: Positive and negative weight increments.
  • $\boldsymbol{v}_p$, $\boldsymbol{v}_n$: Market-impact cost coefficient vectors.
  • $\beta \in (0, 1)$: Reciprocal of the market impact exponent, so the realised power law is $1/\beta$.
  • $\mathrm{lb}$, $\mathrm{ub}$: Lower and upper budget bounds.

The power is raised through a three-dimensional power cone, one per asset per sign, so the model stays convex. BudgetMarketImpact states the full system.

Arguments

Returns

  • nothing.

Related

source
PortfolioOptimisers.set_cost_budget_constraints!Function
set_cost_budget_constraints!(model, vp, vn, val_or_bgt, w, cp, cn)

Charge the trading costs against the portfolio budget.

Registers the cost_bgt_expr expression and the constraint that the weights and their costs must together meet the budget. Two overloads handle the two budget shapes: a fixed value pins the sum, and a BudgetRange bounds it on either side.

The charged quantity is a positional argument, not a field read from the model, because the two cost families charge different quantities. BudgetCosts charges the raw increments wp and wn; BudgetMarketImpact charges the power-cone variables wip and win, which bound the increments raised to 1/beta. Both pass the same coefficient vectors.

Arguments

  • model::JuMP.Model: The JuMP optimisation model.
  • vp: Cost coefficients for positive trades; a vector or a scalar.
  • vn: Cost coefficients for negative trades; a vector or a scalar.
  • val_or_bgt: Fixed budget value or BudgetRange.
  • w: Portfolio weight vector.
  • cp: The positive-side quantity the coefficients charge.
  • cn: The negative-side quantity the coefficients charge.

Returns

  • nothing.

Related

source
PortfolioOptimisers.set_gross_budget_constraints!Method
set_gross_budget_constraints!(model::JuMP.Model, gbgt::Option{<:Num_BgtRg})

Constrain the gross exposure (leverage) sum(lw) + sum(sw), independently of the net exposure.

A number pins it; a BudgetRange bounds it on either side. Whether the constraint pins the realised gross exposure norm(w, 1) or merely bounds it depends on xbgt — see short_mip_threshold_constraints.

Arguments

  • model::JuMP.Model: JuMP optimisation model.
  • gbgt: Gross budget specification, or nothing for no constraint.

Returns

  • nothing.

Related

source
PortfolioOptimisers.set_gross_budget_constraints!Method
set_gross_budget_constraints!(model::JuMP.Model, gbgt::Option{<:Num_BgtRg})

Constrain the gross exposure (leverage) sum(lw) + sum(sw), independently of the net exposure.

A number pins it; a BudgetRange bounds it on either side. Whether the constraint pins the realised gross exposure norm(w, 1) or merely bounds it depends on xbgt — see short_mip_threshold_constraints.

Arguments

  • model::JuMP.Model: JuMP optimisation model.
  • gbgt: Gross budget specification, or nothing for no constraint.

Returns

  • nothing.

Related

source
PortfolioOptimisers.assert_gross_budget_admissibleFunction
assert_gross_budget_admissible(bgt, sbgt, gbgt, wb)

Assert that a gross budget is admissible alongside the net and short budgets.

bgt and sbgt constrain the net and gross exposures only together: pinning both gives 1'w == bgt and norm(w, 1) == bgt + 2 * sbgt. gbgt exists for the combination they cannot reach — gross pinned with net free — so it is rejected when they already determine the gross exposure, and when the weight bounds forbid short positions (whereupon gross and net coincide and bgt already owns the constraint).

Only the statically-decidable contradictions are caught. Ranges may still conflict at solve time, surfacing as infeasibility, and a WeightBoundsEstimator is not resolved until then. TimeDependent schedules are reached through assert_time_dependent_substitution.

Related

source
PortfolioOptimisers.set_exact_budget_constraints!Function
set_exact_budget_constraints!(
    model::Model,
    sp::AbstractMIPSpace,
    ind::AbstractMIPIndicators,
    wx::AbstractVector{<:Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}},
    ss,
    _::WeightsFromParts
)

Pin the long/short decomposition, so the budgets built on lw/sw hold exactly.

lw and sw carry the long and short parts of the weights, and bgt, sbgt and gbgt are all stated in terms of them. Whether those budgets pin the realised exposure or merely bound it depends on how the head related the parts to the weights, so this dispatches on the head's AbstractDecompositionContract. Both methods take the sign from ind, whichever bundle carries one — a dedicated sign bit, or the long/short bits of a builder that already needed them for thresholds or cardinality.

Mathematical definition

Both contracts emit the sign pair, which forces sw = 0 on the long side and lw = 0 on the short side:

\[\begin{align} lw_i - M\, g^l_i &\leq 0\,, & sw_i - M\, g^s_i &\leq 0\,. \end{align}\]

Under PartsBoundWeights that leaves the held side free to overstate itself (lw_i may exceed w_i), so two more constraints close the slack:

\[\begin{align} lw_i - w_i - M(1 - b^l_i) &\leq 0\,, & sw_i + w_i - M(1 - b^s_i) &\leq 0\,. \end{align}\]

Where:

  • $g^l_i$, $g^s_i$: Long and short gates (long_gate, short_gate), which relax to continuous variables when the budget is free.
  • $b^l_i$, $b^s_i$: Long and short binaries (long_bin, short_bin). The slack-closing pair keys on these rather than the gates, because it must key on a bit that is exactly 0 or 1 even when the gates relax.
  • $w_i$: Weight expression for position $i$ in the space of sp.
  • $M$: Big-M constant.

Together they give lw_i == max(w_i, 0) and sw_i == max(-w_i, 0): b^l_i = 1 turns the first into lw_i <= w_i, which against the head's lw_i >= w_i is an equality; both bits zero gives lw_i == sw_i == 0, hence w_i == 0.

Under WeightsFromParts the pair alone is enough and the slack-closing constraints are not emitted: w = lw - sw is an identity, so sw_i = 0 already leaves lw_i == w_i.

Arguments

  • model::JuMP.Model: The JuMP optimisation model.
  • sp::AbstractMIPSpace: Weight space the constraints act on.
  • ind::AbstractMIPIndicators: Indicator bundle supplying the sign.
  • wx::VecNum: Weight expression in the space of sp.
  • ss: Big-M expression, as registered by set_mip_ss_expr!.
  • dc::AbstractDecompositionContract: The head's contract. There is deliberately no method for nothing — a model with no head contract has no short side, so there is no decomposition to pin and a caller that got here is confused.

Returns

  • nothing.

Related

source