Fees Constraints: private API
PortfolioOptimisers.add_to_fees! — Function
add_to_fees!(
model::Model,
expr::Union{Number, AbstractJuMPScalar}
)
Accumulate a JuMP expression into the :fees expression of the optimisation model.
Creates the :fees expression if it does not yet exist; otherwise adds expr to it in place.
Arguments
model::JuMP.Model: The JuMP optimisation model.expr: The fee expression to accumulate. A plain number is accepted beside a JuMP scalar, because a charge that does not depend on the decision variables — the forced liquidation ofset_liquidation_fees!— is a constant under any objective whosekis not a variable.
Returns
nothing.
Related
PortfolioOptimisers.add_to_one_time_fees! — Function
add_to_one_time_fees!(
model::Model,
expr::Union{Number, AbstractJuMPScalar}
)
Accumulate a one-off fee expression into the model's :one_time_fees expression.
The twin of add_to_fees!, for the terms that are charged one time for the whole holding period rather than on every observation. Only the two fixed fees reach it, because l, s and tn are rates per period. set_net_portfolio_returns! subtracts this expression from the first observation alone, and add_fees_to_ret! divides it by the observation count, because an expected return is a per period number.
Arguments
model::JuMP.Model: The JuMP optimisation model.expr: The fee expression to accumulate.
Returns
nothing.
Related
PortfolioOptimisers.charge_one_time_fees — Function
charge_one_time_fees(model::JuMP.Model, net, one_time, T::Number,
fa::Option{<:AbstractFeeAmortisation})Lay the model's one-off fee expression onto a net return series, on the clock the fee states.
The model's twin of charge_fees, and it states the same rule. The two fixed fees are charged one time for the whole holding period, so a nothing or FirstObservationFees clock subtracts them from the first observation alone, and an AmortisedFees spreads them evenly over T, the observation count of the fit.
Algorithm
- On a
nothingorFirstObservationFeesfa, subtractone_timefrom the first entry ofnetand leave the rest. - On an
AmortisedFeesfa, build the shareone_time / Tonce and subtract it from every entry ofnet.
Arguments
model::JuMP.Model: The JuMP optimisation model.net: The net return expression, already charged the per period terms.one_time: The model's:one_time_feesexpression.T: Observation count of the fit, fromget_T.fa: The fee's clock, from:fee_fa.
Returns
- The net return expression, charged the one-off terms.
Related
PortfolioOptimisers.set_turnover_fees! — Function
set_turnover_fees!(args...)
Add a turnover-based transaction fee expression to the JuMP optimisation model.
The fall-through method does nothing. The concrete method computes val' * |w - wt| via NormOneCone constraints and accumulates the result into the model's :fees expression via add_to_fees!.
Mathematical definition
\[\begin{align} t_{ftn,i} &\geq |w_i - w_{t,i}\, k|\,, \\ f_{tn} &= \boldsymbol{v}^\intercal \boldsymbol{t}_{ftn}\,. \end{align}\]
Where:
- $w_i$: Portfolio weight for asset $i$.
- $w_{t,i}$: Benchmark weight for asset $i$.
- $k$: Budget scaling / homogenisation variable.
- $\boldsymbol{v}$: Per-asset fee rate vector.
- $\boldsymbol{t}_{ftn}$: Auxiliary absolute-deviation variable vector.
- $f_{tn}$: Total turnover fee.
Arguments
model::JuMP.Model: The JuMP optimisation model.tn::Turnover: Turnover specification containing benchmark weightswand per-unit feeval.
Returns
nothing.
Related
PortfolioOptimisers.set_non_fixed_fees! — Function
set_non_fixed_fees!(args...)
Add all non-fixed (proportional and turnover) fee expressions to the JuMP optimisation model.
The fall-through method does nothing. The concrete method delegates to set_long_non_fixed_fees!, set_short_non_fixed_fees!, and set_turnover_fees!.
Arguments
model::JuMP.Model: The JuMP optimisation model.fees::Fees: Fee specification containing long, short, and turnover fee rates.
Returns
nothing.
Related
PortfolioOptimisers.set_long_non_fixed_fees! — Function
set_long_non_fixed_fees!(args...)
Add proportional long-side fee expression to the JuMP optimisation model.
The fall-through method does nothing. The concrete method adds fl' * lw to the model's :fees expression via add_to_fees!.
Mathematical definition
\[\begin{align} f_l &= \boldsymbol{f}_l^\intercal \boldsymbol{lw}\,. \end{align}\]
Where:
- $f_l$: Total long-side fee.
- $\boldsymbol{f}_l$: Per-asset long-side fee rate vector.
- $\boldsymbol{lw}$: Long-weight vector.
Arguments
model::JuMP.Model: The JuMP optimisation model.fl: Long-side fee rate(s). Accepts a scalarNumberor aVecNum.
Returns
nothing.
Related
PortfolioOptimisers.set_short_non_fixed_fees! — Function
set_short_non_fixed_fees!(args...)
Add proportional short-side fee expression to the JuMP optimisation model.
The fall-through method does nothing. The concrete method adds fs' * sw to the model's :fees expression via add_to_fees!. Does nothing when no short-weight variable :sw exists in the model.
Mathematical definition
\[\begin{align} f_s &= \boldsymbol{f}_s^\intercal \boldsymbol{sw}\,. \end{align}\]
Where:
- $f_s$: Total short-side fee.
- $\boldsymbol{f}_s$: Per-asset short-side fee rate vector.
- $\boldsymbol{sw}$: Short-weight vector.
Arguments
model::JuMP.Model: The JuMP optimisation model.fs: Short-side fee rate(s). Accepts a scalarNumberor aVecNum.
Returns
nothing.
Related
PortfolioOptimisers.set_fixed_fees! — Function
set_fixed_fees!(
model::Model,
sp::AbstractMIPSpace,
ind::AbstractMIPIndicators,
ffl::Union{Nothing, Number, AbstractVector{<:Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}}},
ffs::Union{Nothing, Number, AbstractVector{<:Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}}},
ffl_flag::Bool,
ffs_flag::Bool
)
Add fixed-fee expressions to the JuMP optimisation model.
A fixed fee is charged per position held, whatever its size, so unlike the proportional fees above it cannot be written against the weights — it needs a binary saying whether the position is there at all. That is the only reason this one takes an indicator bundle, and the only reason a MIP builder has to run before it.
Mathematical definition
\[\begin{align} f_{fl} &= \boldsymbol{f}_{fl}^\intercal \boldsymbol{b}^l\,, & f_{fs} &= \boldsymbol{f}_{fs}^\intercal \boldsymbol{b}^s\,. \end{align}\]
Where:
- $\boldsymbol{b}^l$, $\boldsymbol{b}^s$: Long and short binaries (
long_bin,short_bin). These are the binaries themselves, never the gates: a fee is incurred by the decision to hold, which is what the bit records, and the gates relax to continuous variables when the budget is free. - $\boldsymbol{f}_{fl}$, $\boldsymbol{f}_{fs}$: Long and short fixed-fee rates.
Under a long-only builder the held bit is the long bit (HeldIndicators), and there is no short side to charge.
Arguments
model::JuMP.Model: The JuMP optimisation model.sp::AbstractMIPSpace: Weight space the fees are charged in.ind::AbstractMIPIndicators: Indicator bundle supplying the binaries.ffl::Option{<:Num_VecNum}: Long-side fixed fee rate(s).ffs::Option{<:Num_VecNum}: Short-side fixed fee rate(s).ffl_flag::Bool: Whether to add the long fixed-fee expression.ffs_flag::Bool: Whether to add the short fixed-fee expression.
Returns
nothing.
Related
PortfolioOptimisers.set_liquidation_fees! — Function
set_liquidation_fees!(::JuMP.Model, ::Nothing)
set_liquidation_fees!(model::JuMP.Model, lq::Turnover)Add the proportional cost of a forced exit to the model's :fees expression.
lq prices the positions that leave the Investable Mask, and those assets are not in the model's w: the optimisation reduced them away at its entry. The charge is therefore a constant, not a function of the decision variables, and it needs no auxiliary variable and no norm constraint — which is what separates it from set_turnover_fees!, whose |w - wt * k| does depend on w.
The constant is multiplied by the homogenising variable k, exactly as the turnover term is, so a ratio objective sees the charge in the same units as every other fee and the exit moves the argmin rather than riding outside the programme.
lq is a rate per period, so the charge joins :fees beside l, s and tn through add_to_fees!, and no clock reaches it.
Algorithm
- On a
nothinglq, do nothing. No asset left the universe. - Otherwise read
k, the homogenising variable. - Compute the constant through
calc_liquidation_fees, which reads the carrier alone: a forced exit trades to zero, so the charge is the rate timesabs.(lq.w). - Register
constant * kand add it to:feesthroughadd_to_fees!.
Arguments
model: JuMP model.lq: The proportional liquidation carrier, ornothing.
Returns
nothing: The model is modified in place.
Related
PortfolioOptimisers.set_fixed_liquidation_fees! — Function
set_fixed_liquidation_fees!(::JuMP.Model, ::Nothing, ::NamedTuple)
set_fixed_liquidation_fees!(model::JuMP.Model, flq::Turnover, kwargs::NamedTuple)Add the fixed cost of a forced exit to the model's :one_time_fees expression.
The fixed twin of set_liquidation_fees!, and a constant for the same reason: the liquidated assets are not in w. Unlike set_fixed_fees! it therefore needs no binary indicator, because whether each position is held is already known from flq.w rather than decided by the programme.
flq is a currency amount charged one time for the whole holding period, so it joins :one_time_fees beside fl and fs through add_to_one_time_fees!, and charge_one_time_fees then lands it on the clock fees.fa names.
Algorithm
- On a
nothingflq, do nothing. - Otherwise read
k, the homogenising variable. - Compute the constant through
calc_fixed_liquidation_fees, which charges both the liquidated long and the liquidated short side. - Register
constant * kand add it to:one_time_feesthroughadd_to_one_time_fees!.
Arguments
model: JuMP model.flq: The fixed liquidation carrier, ornothing.kwargs: Forwarded toisapproxto decide how near zero counts as zero.
Returns
nothing: The model is modified in place.
Related