Discrete allocation: private API
PortfolioOptimisers.finite_sub_allocation — Function
finite_sub_allocation(w::VecNum, p::VecNum, cash::Number, bgt::Number,
sf::Option{<:NamedTuple}, da::DiscreteAllocation,
str_names::Bool = false)Build and solve the discrete allocation MIP for one side, long or short, of the portfolio.
Implements the sub-problem of DiscreteAllocation. An empty w returns three empty vectors, the untouched cash, a zero fee, and nothing for both the return code and the model.
Arguments
w::VecNum: Target weights of this side, non-negative.p::VecNum: Asset prices of this side, in the same order asw.cash::Number: Cash allocated to this side.bgt::Number: Budget of this side, used to rescale the realised weights.sf::Option{<:NamedTuple}: This side's charge, ofallocation_side_fees, ornothing.da::DiscreteAllocation: Allocator carrying the solvers, the scales and the formulationwf.str_names::Bool = false: Whether to give the JuMP variables string names.
Returns
shares::VecNum: Share count per asset, rounded toInt.cost::VecNum:shares .* p.aw::VecNum: Realised weights, rescaled to sum tobgt. All zero when nothing was bought.acash::Number: Cash left over,cashless the cost of the shares and the fee.fee::Number: The fee this side paid over the whole horizon.res::OptimisationReturnCode: AnOptimisationSuccessor anOptimisationFailurecarrying the solver trials.model::JuMP.Model: The solved model.
Details
- The share vector is declared integer and non-negative, so a short side must be passed with its weights already negated.
set_discrete_error!adds the one constraint thatda.wfselects. Everything else in the model is common to the four formulations.set_allocation_fees!adds the fee. It enters the budget constraint and never the objective.- The fee this verb reports is
allocation_feeof the realised shares, not the value of the model's own expression. The turnover term of that expression is an epigraph, and only the budget pushes it down, so a solver leaves it slack whenever the budget does not bind. A slack epigraph overstates the fee, so the allocation the model bought is affordable under the exact charge. sharesis read back withround(Int, ...), because a MIP solver returns an integer only to within its own tolerance. A fee larger than the cash makes the budget infeasible, and an infeasible model holds no finite value, so the book is then read as empty andrescarries the failure.
Related
PortfolioOptimisers.set_discrete_error! — Function
set_discrete_error!(model::JuMP.Model, w::VecNum, p::VecNum, cash::Number,
wf::JuMPWeightFinaliserFormulation) -> NothingBound the model's auxiliary variable u by the allocation error that wf selects.
Adds the one constraint that separates the four formulations of DiscreteAllocation. The model already holds the share vector x and the auxiliary variable u; this method adds the cone that ties them together. The objective and the cash constraint are set by finite_sub_allocation and do not depend on wf.
Arguments
model::JuMP.Model: Model holdingx,uand the constraint scale.w::VecNum: Target weights of this sub-problem.p::VecNum: Asset prices, in the same order asw.cash::Number: Cash allocated to this sub-problem.wf::JuMPWeightFinaliserFormulation: Selects the error. See the table inDiscreteAllocation.
Returns
nothing.
Details
- The absolute formulations bound the error
w * cash - x .* p; the relative ones bound(x * cash) ⊘ (w .* p) .- 1. - The unsquared formulations use a
JuMP.MOI.NormOneCone; the squared ones use aJuMP.SecondOrderCone, which bounds the $\ell_2$ norm itself rather than its square. - The relative formulations replace a zero target weight with
eps(eltype(w))on a copy ofw, so the caller's vector is untouched and the division is defined.
Related
PortfolioOptimisers.set_allocation_fees! — Function
set_allocation_fees!(model::JuMP.Model, p::VecNum, cash::Number, sf::Option{<:NamedTuple})Write one side's fee in the allocation model's own variables, and return it.
A fee is a cost of the portfolio the allocator actually buys. The model holds the share vector x and the prices p, so x .* p is the money in each position exactly. Every term is written against that money, and no weight and no price appears on its own.
sf is one side's charge, of allocation_side_fees. A nothing sf writes nothing and returns a zero expression, so the caller needs no branch.
Mathematical definition
\[\begin{align} \boldsymbol{m} &= \boldsymbol{x} \odot \boldsymbol{p}\,, \\ t_{i} &\geq \lvert m_{i} - m_{0,i} \rvert\,, \\ b_{i} &\leq x_{i} \leq \left\lfloor C / p_{i} \right\rfloor b_{i}\,, \\ F(\boldsymbol{x}) &= T \left( \boldsymbol{f}_{\text{p}}^\intercal \boldsymbol{m} + \boldsymbol{f}_{\text{Tn}}^\intercal \boldsymbol{t} \right) + \boldsymbol{f}_{\text{f}}^\intercal \boldsymbol{b} + F_{\text{lq}}\,. \end{align}\]
Where:
- $\boldsymbol{m}$: Money in each position.
- $\boldsymbol{m}_{0}$: Money in each position before the trade,
sf.prev_money. - $\boldsymbol{t}$: Epigraph of the money traded.
- $\boldsymbol{b}$: Binary saying whether the position is held at all.
- $C$: Cash allocated to this sub-problem.
- $T$: Horizon, in periods.
- $\boldsymbol{f}_{\text{p}},\, \boldsymbol{f}_{\text{Tn}},\, \boldsymbol{f}_{\text{f}}$: Proportional, turnover and fixed rates of this side.
- $F_{\text{lq}}$: Forced exit of
allocation_liquidation_fee,sf.liq. It is a constant, because the assets it sells are not among the share counts this model solves for.
The rates l, s and tn charge on each of the T periods, and the fixed amounts fl and fs charge one time for the whole horizon. That is the rule calc_total_fees states, written in the model's own variables.
A binary is emitted only when the side states a fixed fee, so a problem that states none keeps the variable count it had. x is integer and non-negative, so b <= x and x <= ub * b make b the indicator of x > 0 exactly.
Arguments
model::JuMP.Model: The JuMP optimisation model.p::VecNum: Asset prices of this side.cash::Number: Cash allocated to this side. It bounds the shares a binary can switch on.sf::Option{<:NamedTuple}: This side's charge, ornothing.
Returns
fee: The fee expression. It is registered asmodel[:fee].
Related