Portfolio and asset fees: private API
PortfolioOptimisers.AbstractFeeAmortisation — Type
abstract type AbstractFeeAmortisation <: AbstractAlgorithmSupertype for the algorithms that name the clock a one-off fee charge falls on.
Fees and FeesEstimator each carry this family in their fa field, bound to Option{<:AbstractFeeAmortisation}. The field decides where the two fixed charges fl and fs land on a return series, and it reaches no other term, because l, s and tn are rates per period and charge on every observation. The family has two leaves. FirstObservationFees charges the two fixed amounts one time, on the first observation, and AmortisedFees spreads them evenly over a horizon instead. A nothing fa is the default, and it names the first-observation clock.
Every site that reads the field dispatches on the leaf it holds rather than on this supertype, so a third clock added to the family gets a MethodError until its own methods are written. The supertype names the question, and it decides no answer.
The cross-validation schemes carry the same family in a field of the same name, where it overrides the fee's own clock for a fold's realised series. fold_evaluation reads it, and there a nothing inherits the fee's clock rather than naming one.
Related
PortfolioOptimisers.FeesE_Fees — Type
const FeesE_Fees = Union{<:Fees, <:FeesEstimator}Union type for fee constraint objects and estimators.
There is no vector counterpart, and fees_constraints has no vector method. Fees already sums every fee component into one object, so an optimiser holds exactly one. See RkbE_Rkb for why some constraint families are singular and others are not.
Related
PortfolioOptimisers.calc_periodic_fees — Function
calc_periodic_fees(w::VecNum, fees::Fees)Charge the terms of a fee that fall on every observation.
l, s and tn are rates per period, so each of them charges one time per observation of a return series. fees.fa reaches none of the three. calc_fees adds this number to the one-off terms of calc_one_off_fees, and calc_total_fees multiplies it by the horizon.
Algorithm
- Charge the long proportional term, the call of
calc_feesonfees.lunder.>=. - Charge the short proportional term, the negated call of the same name on
fees.sunder.<.wis negative on that side, so the negation is what makes the term a positive charge. - Charge the turnover term, the call of
calc_feesonfees.tn. - Return the sum of the three terms.
Arguments
w: Portfolio weights.fees:Feesstructure.
Returns
val::Number: The per period charge, before the one-off terms.
Examples
julia> fees = Fees(; l = 0.01, fl = 5.0, tn = Turnover(; w = [0.0, 0.0], val = 0.002));julia> PortfolioOptimisers.calc_periodic_fees([0.5, 0.5], fees)0.012Related
PortfolioOptimisers.calc_one_off_fees — Function
calc_one_off_fees(w::VecNum, fees::Fees)Charge the terms of a fee that fall one time over a holding period.
fl and fs are currency amounts charged one time for the whole holding period, and they are the only terms fees.fa reaches. The method carries no price, because a fixed fee is a currency amount already.
Algorithm
- Charge the long fixed term, the call of
calc_fixed_feesonfees.flunder.>=. - Charge the short fixed term, the call of the same name on
fees.fsunder.<. - Return the sum of the two terms.
Arguments
w: Portfolio weights.fees:Feesstructure.
Returns
val::Number: The charge of one holding period.
Examples
julia> fees = Fees(; l = 0.01, fl = 5.0, tn = Turnover(; w = [0.0, 0.0], val = 0.002));julia> PortfolioOptimisers.calc_one_off_fees([0.5, 0.5], fees)10.0Related
PortfolioOptimisers.calc_asset_periodic_fees — Function
calc_asset_periodic_fees(w::VecNum, fees::Fees)Split over the assets the terms of a fee that fall on every observation.
The per asset twin of calc_periodic_fees. Its entries sum to that number, up to the order of summation.
Algorithm
- Charge the long proportional term, the call of
calc_asset_feesonfees.lunder.>=. - Charge the short proportional term, the negated call of the same name on
fees.sunder.<. - Charge the turnover term, the call of
calc_asset_feesonfees.tn. - Charge the proportional forced exit, the call of
calc_asset_liquidation_feesonfees.lq.lqis a rate, so it falls on every period besidel,sandtn, and no clock reaches it. - Return the pair: the elementwise sum of the three vectors of steps 1 to 3, and the vector of step 4.
Arguments
w: Portfolio weights.fees:Feesstructure.
Returns
The verb returns a pair, one entry per axis of a reduced Fees.
investable::VecNum: The per period charge of each asset that stayed, froml,sandtn, which were sliced to the Investable Mask.liquidation::VecNum: The per period charge of each asset that left, fromlq, which was sliced to the mask's complement. Empty whenlqis unset, because the axis has no length to build a vector of zeros from.
Examples
julia> fees = Fees(; l = 0.01, fl = 5.0, tn = Turnover(; w = [0.0, 0.0], val = 0.002));julia> PortfolioOptimisers.calc_asset_periodic_fees([0.5, 0.5], fees)([0.006, 0.006], Float64[])Related
PortfolioOptimisers.calc_asset_one_off_fees — Function
calc_asset_one_off_fees(w::VecNum, fees::Fees)Split over the assets the terms of a fee that fall one time over a holding period.
The per asset twin of calc_one_off_fees. Its entries sum to that number, up to the order of summation.
Algorithm
- Charge the long fixed term, the call of
calc_asset_fixed_feesonfees.flunder.>=. - Charge the short fixed term, the call of the same name on
fees.fsunder.<. - Charge the fixed forced exit, the call of
calc_asset_fixed_liquidation_feesonfees.flq.flqis a currency amount charged one time, so it falls on the clockfees.fanames, besideflandfs. - Return the pair: the elementwise sum of the two vectors of steps 1 and 2, and the vector of step 3.
Arguments
w: Portfolio weights.fees:Feesstructure.
Returns
The verb returns a pair, one entry per axis of a reduced Fees.
investable::VecNum: The one-off charge of each asset that stayed, fromflandfs, which were sliced to the Investable Mask.liquidation::VecNum: The one-off charge of each asset that left, fromflq, which was sliced to the mask's complement. Empty whenflqis unset, because the axis has no length to build a vector of zeros from.
Examples
julia> fees = Fees(; l = 0.01, fl = 5.0, tn = Turnover(; w = [0.0, 0.0], val = 0.002));julia> PortfolioOptimisers.calc_asset_one_off_fees([0.5, 0.5], fees)([5.0, 5.0], Float64[])Related
PortfolioOptimisers.calc_liquidation_fees — Function
calc_liquidation_fees(::Nothing)
calc_liquidation_fees(lq::Turnover{<:Any, <:Number})
calc_liquidation_fees(lq::Turnover{<:Any, <:VecNum})Charge the proportional cost of the positions a forced exit sells.
lq lives on the complement of the Investable Mask, so its entries are the assets that left, never the assets the programme holds. A forced exit is a trade to zero, so the target weight of every entry is zero, the turnover |target - lq.w| is abs.(lq.w), and the charge is the rate times the absolute previous weight. The carrier is the whole of the input: the verb needs no weight vector and takes none.
The charge is a rate, so it falls on every period beside l, s and tn, and calc_periodic_fees adds it there.
Algorithm
- On a
nothinglq, returnfalse. Nothing left the universe, so nothing is owed, and aBoolzero adds to a charge of any element type without widening it. - On a scalar rate, return
lq.val * sum(abs, lq.w). - On a per asset rate, return
dot(lq.val, abs.(lq.w)).
Arguments
lq: The proportional liquidation carrier, ornothing.
Returns
val::Number: The proportional charge of the forced exit.
Examples
julia> PortfolioOptimisers.calc_liquidation_fees(nothing)falsejulia> PortfolioOptimisers.calc_liquidation_fees(Turnover(; w = [0.25], val = [0.01]))0.0025Related
PortfolioOptimisers.calc_fixed_liquidation_fees — Function
calc_fixed_liquidation_fees(::Nothing, ::NamedTuple)
calc_fixed_liquidation_fees(flq::Turnover, kwargs::NamedTuple)Charge the fixed cost of the positions a forced exit sells.
The fixed twin of calc_liquidation_fees, and it takes no weight vector for the same reason. flq lives on the complement of the Investable Mask, and its amount is charged once for each entry whose absolute previous weight is not isapprox to zero under kwargs, the threshold fl and fs already use.
A liquidated short is a trade as much as a liquidated long, so both sides are charged. The verb therefore calls calc_fixed_fees twice against flq.w, once under .>= and once under .<, which is the pattern calc_one_off_fees spells for fl and fs with one rate serving both sides. The two selections are disjoint, so no entry is charged twice.
The charge is a currency amount, so it falls one time for the whole holding period beside fl and fs, on the clock fees.fa names.
Algorithm
- On a
nothingflq, returnfalse, the sameBoolzerocalc_liquidation_feesreturns. - Otherwise charge
calc_fixed_feesonflq.wandflq.valunder.>=, the liquidated long positions. - Charge the same under
.<, the liquidated short positions. - Return the sum of the two.
Arguments
flq: The fixed liquidation carrier, ornothing.kwargs: Forwarded toisapproxto decide how near zero counts as zero.
Returns
val::Number: The fixed charge of the forced exit.
Examples
julia> PortfolioOptimisers.calc_fixed_liquidation_fees(nothing, (; atol = 1e-8))falsejulia> PortfolioOptimisers.calc_fixed_liquidation_fees(Turnover(; w = [0.25, -0.4], val = [5.0, 7.0]), (; atol = 1e-8))12.0Related
PortfolioOptimisers.calc_asset_liquidation_fees — Function
calc_asset_liquidation_fees(w::VecNum, ::Nothing)
calc_asset_liquidation_fees(w::VecNum, lq::Turnover)Split the proportional cost of a forced exit over the assets that left.
The per asset twin of calc_liquidation_fees. Its entries sum to that number, and they sit on the complement of the Investable Mask: one entry per asset that left, in the order the carrier holds them, so each charge names the asset that caused it.
nothing gives an empty vector rather than a vector of zeros, because the complement is empty when nothing exited. That keeps the two axes honest: a reader can tell "no asset left" from "an asset left and owed nothing".
Algorithm
- On a
nothinglq, return an empty vector in the element type ofw. - Otherwise call
calc_asset_feesonlqagainst a zero vector the length oflq.w, givinglq.val .* abs.(lq.w)elementwise.
Arguments
w: Portfolio weights, read for their element type.lq: The proportional liquidation carrier, ornothing.
Returns
val::VecNum: The proportional charge per liquidated asset.
Examples
julia> PortfolioOptimisers.calc_asset_liquidation_fees([0.5, 0.5], Turnover(; w = [-0.4, 0.25], val = [0.002, 0.010]))2-element Vector{Float64}: 0.0008 0.0025Related
PortfolioOptimisers.calc_asset_fixed_liquidation_fees — Function
calc_asset_fixed_liquidation_fees(w::VecNum, ::Nothing, ::NamedTuple)
calc_asset_fixed_liquidation_fees(w::VecNum, flq::Turnover, kwargs::NamedTuple)Split the fixed cost of a forced exit over the assets that left.
The per asset twin of calc_fixed_liquidation_fees, on the complement axis, and empty when nothing exited. Both liquidated sides are charged, so the two selections of calc_asset_fixed_fees are summed; they are disjoint, so no entry is charged twice.
Algorithm
- On a
nothingflq, return an empty vector in the element type ofw. - Otherwise charge
calc_asset_fixed_feesonflq.wandflq.valunder.>=and again under.<, and return the elementwise sum.
Arguments
w: Portfolio weights, read for their element type.flq: The fixed liquidation carrier, ornothing.kwargs: Forwarded toisapproxto decide how near zero counts as zero.
Returns
val::VecNum: The fixed charge per liquidated asset.
Examples
julia> PortfolioOptimisers.calc_asset_fixed_liquidation_fees([0.5, 0.5], Turnover(; w = [-0.4, 0.25], val = [7.0, 5.0]), (; atol = 1e-8))2-element Vector{Float64}: 7.0 5.0Related
PortfolioOptimisers.add_liquidation_terms — Function
add_liquidation_terms(a::VecNum, b::VecNum)Add the two terms of the liquidation axis, either of which may be unset.
lq and flq are set independently, and the verb that prices an unset carrier returns an empty vector rather than a vector of zeros, because it holds no length to build one from: the axis is the complement of the Investable Mask, and the verb reads no mask. So a Fees that sets flq and no lq gives one term spanning the complement and one spanning nothing, and adding them elementwise would raise a DimensionMismatch on a fee the caller set correctly.
An empty term is a term that charges nothing, so the sum is the other term. Two set terms span the same complement and add elementwise. The investable axis needs no such verb: its terms are built from w, so they always span it.
Arguments
a: One term of the axis.b: The other term of the axis.
Returns
val::VecNum: The two terms summed, or whichever of them is set.
Related
PortfolioOptimisers.override_fee_amortisation — Function
override_fee_amortisation(fees::Option{<:Fees}, fa::Nothing)
override_fee_amortisation(fees::Nothing, fa::AbstractFeeAmortisation)
override_fee_amortisation(fees::Fees, fa::AbstractFeeAmortisation)Return the fee a report charges, from the fee a fit saw and the clock the report states.
A Fees answers one question with its fa field: the clock the two fixed charges fl and fs fall on. A cross-validation scheme asks a second question with a field of the same name, and the two answers need not agree. The optimiser prices a fixed fee the way the objective must, and the report charges it the way a fund saw it. This verb resolves the pair, and it reaches the fold's realised series alone.
A nothing fa on the scheme inherits, so the fee comes back unchanged and no object is built. A stated fa rebuilds the fee with that clock and leaves every other field of it alone. The rate fields l, s and tn charge on every observation whatever the clock is, so the override moves no number of theirs.
Algorithm
- On a
nothingfa, returnfeesunchanged, whether it is aFeesornothing. - On a stated
faand anothingfees, returnnothing. There is no fee to charge, so there is no clock to state. - On a stated
faand aFees, rebuild the fee with thatfaand its own five fee fields andkwargs.
Arguments
fees: The fee the fit saw, ornothing.fa: The clock the scheme states, ornothingto inherit the fee's own.
Returns
Option{<:Fees}: The fee the report charges.
Related
PortfolioOptimisers.two_axis_fees_view — Function
two_axis_fees_view(fees::Fees, i, n::Integer)Sub-select a resolved fee to the assets an optimisation keeps, on both of its axes, from the index it keeps and the width of the unreduced universe.
This is the one place a Fees is split across its two axes. tn, l, s, fl and fs price the positions the portfolio holds, so they live on the investable axis and are sliced at i. lq and flq price the positions it is forced to sell, so they live on the complement of that axis and are sliced at the assets i leaves out. The complement is derived and never stored, from i and n.
The width is a number rather than a matrix because the two callers hold different things. port_opt_view is handed the unreduced returns matrix by the fit's door and reads size(X, 2) off it. investable_fees_view is also reached by result_investable_view, which meets a caller's full-universe fee beside a result whose own prior is already reduced, so no full-width matrix exists there and the width is the mask's own length.
The view writes no imsk: it returns an unmarked fee. The view is a slice, and a cluster of a nested optimiser takes it as the Investable Mask door does, so a mark written here would make an inner fit read a cluster's complement as the assets that left. The door, investable_fees_view, is the one verb that marks a fee, and it does so after this view returns.
Algorithm
- Build
j, the complement ofiin1:n. - Slice
tnatithroughport_opt_view, whose@vproptags takewand a vectorvaland leave a scalar or dictionaryvalalone. - Slice
l,s,flandfsatithroughnothing_scalar_array_view. - On an empty
jno asset left the universe, so setlqandflqtonothing: there is nothing to liquidate, and aTurnoverrefuses an emptywin any case. Otherwise slice both atj, by the same verb as step 2. - Rebuild through the keyword constructor, carrying
faandkwargsunchanged andimskasnothing.
Arguments
fees: The fee to reduce.i: Indices of the assets the optimisation keeps.n: The width of the unreduced universe.
Returns
fees::Fees: The fee on the two reduced axes, unmarked.
Related
PortfolioOptimisers.strip_liquidation_carriers — Function
strip_liquidation_carriers(fees, imsk)Drop the two liquidation carriers when no asset left the universe.
A caller states lq and flq over the full universe, because they cannot know in advance which asset will delist. port_opt_view narrows them to the complement of the Investable Mask at the door — but a window in which every asset is investable derives no mask at all, and the nothing sentinel short-circuits the door precisely so that the all-investable path allocates nothing. The carriers would then survive at full width and be charged in full, for assets that never left.
This verb closes that gap explicitly rather than by taking the view on the common path, so the nothing mask stays allocation-free. A BitVector mask means the door has already run and the carriers are on the right axis, so the fee is returned untouched. A nothing mask means nothing exited, so both carriers go. A fee that carries neither is returned untouched under either mask, so a caller who states no liquidation pays for nothing.
The verb reads no mark. It drops the carriers of whatever fee it is handed under a nothing mask, which is what a hierarchical fit asks of it when it prices a cluster's risk from a fee the door has already reduced: the exit rides on the result alone, so no sub-problem may charge it. The door that must tell a caller's full-universe carrier from a result's reduced one is investable_fees_view, which consults fees.imsk and reaches this verb for the unmarked fee alone. The mark is carried through, so a reduced fee stays reduced with its carriers gone.
Algorithm
- On a
nothingfees, or aBitVectorimsk, returnfeesunchanged. A statednothingfee under a derived mask satisfies both, so a third method names that pair and breaks the ambiguity. - On a
nothingimskwith both carriers alreadynothing, returnfeesunchanged, so the common case allocates nothing. - Otherwise rebuild the fee with
lqandflqset tonothing, carrying every other field through.
Arguments
fees: The fee to strip, ornothing.imsk: The Investable Mask, ornothingwhen every asset is investable.
Returns
fees::typeof(fees): The fee, with no liquidation carrier when none can apply.
Related
PortfolioOptimisers.investable_fees_view — Function
investable_fees_view(fees::Nothing, imsk, X)
investable_fees_view(fees::Fees, imsk::Nothing, X)
investable_fees_view(fees::Fees, imsk::BitVector, X::MatNum)
investable_fees_view(fees::Fees, imsk::BitVector, n::Integer)Place a fee resolved over the caller's universe onto the axes an Investable Mask leaves, and mark it with that mask.
A fee is resolved before the door, against the sets the caller stated, and this verb takes it through. That order is what lets a name-keyed fee name an asset the data later delists: after the door sets holds the investable names alone, so the name is gone and strict refuses it, and a carrier keyed by name cannot resolve at all, because its w already sits on the complement while sets sits on the mask. Resolving first and viewing after removes both, and needs no new arithmetic: port_opt_view already splits a resolved Fees across its two axes.
The two things a mask can be are the two arms. A BitVector means assets left, so the view runs at findall(imsk) and derives the complement from the width of the unreduced universe, read off X at a fit site or stated as n by a consumer that holds no full-width matrix: a result-taking verb meets a caller's fee beside a result whose own prior is already reduced, and the width it has is length(imsk). A nothing mask means every asset is investable, so there is no complement to slice to and strip_liquidation_carriers drops both carriers instead, which is what keeps that path allocation-free.
This is the one verb that writes fees.imsk, and it reads the mark before it acts, so it is idempotent. A caller states the two carriers over the full universe, and a result carries them on the complement of its mask; the two are indistinguishable by width, and the same door serves both. expected_risk(r, w, pr, fees) on an all-investable prior meets the caller's fee under a nothing mask, and must drop the carriers or it charges the whole book as a forced exit on every period; expected_risk(r, res) on a reduced result meets the result's fee under the same nothing mask, because the reduced prior carries no NaN, and must charge the exit the carriers hold. The mark is what tells them apart: an unmarked fee is a caller's statement and takes the arm the mask names, a fee marked with this mask has been through this door and is returned as it is, and a fee marked with another mask is refused, because its carriers hold no rate for an asset that other reduction kept.
Algorithm
- On a
nothingfee, returnnothing. - On a marked fee, return it untouched when the mark is
imsk, or the mark is aBitVectorandimskisnothing; raise anArgumentErrornaming both masks when the mark is aBitVectorother thanimsk. - On an unmarked fee and a
nothingmask, hand the fee tostrip_liquidation_carriers, which drops both carriers and returns a fee carrying neither untouched. - On an unmarked fee and a
BitVectormask, taketwo_axis_fees_viewatfindall(imsk)over the width of the unreduced universe,size(X, 2)orn, which slices the five per-asset fields to the mask and the two carriers to its complement, and rebuild it withimskas its mark.
Arguments
fees: The fee resolved over the caller's full universe, a fee this verb already reduced, ornothing.imsk: The Investable Mask, ornothingwhen every asset is investable.X: The unreduced returns matrix. Only its width is read, to derive the complement.n: The width of the unreduced universe, for a caller that holds no matrix of it.
Validation
- A fee marked with a
BitVectorother thanimskis refused with anArgumentErrornaming the two masks. Its carriers were sliced to the complement of its own mask, so they hold no rate for an asset the caller's mask says left, and charging nothing for it would understate the return. Lift it withlift_feesand reduce the caller's statement instead.
Returns
Option{<:Fees}: The fee on the axes the mask leaves, marked with the mask.
Related
PortfolioOptimisers.mark_fees — Function
mark_fees(fees::Nothing, imsk)
mark_fees(fees::Fees, imsk::Nothing)
mark_fees(fees::Fees, imsk::BitVector)Reconcile a fee with the Investable Mask a caller states beside it, so the fee is the one source of the axes it is on.
A FiniteAllocationInput takes a fee and a mask as two keywords, and a caller may state either without the other: a result hands both, a caller who reduced a fee by hand states the mask alone, and a caller who passes a result's fee states no mask. lift_fees reads the fee's own imsk, so the two must agree before the input is built. A stated mask marks an unmarked fee, a marked fee supplies a missing mask, and a pair that disagrees is refused.
Algorithm
- On a
nothingfee, return it with the stated mask. - On a
nothingmask, return the fee with its own mark, which may benothing. - On an unmarked fee and a
BitVectormask, rebuild the fee with that mask as its mark. - On a marked fee and a
BitVectormask, raise anArgumentErrornaming both when they differ, and return the pair otherwise.
Arguments
fees: The fee, ornothing.imsk: The Investable Mask the caller states, ornothing.
Validation
- A fee marked with a
BitVectorother thanimskis refused with anArgumentErrornaming the two masks.
Returns
(fees, imsk): The fee, marked with the mask when there is one, and the mask.
Related
PortfolioOptimisers.lift_fees — Function
lift_fees(fees::Nothing)
lift_fees(fees::Fees)Put a fee a door reduced back onto the full universe, on both of its axes.
The inverse of investable_fees_view. Reducing an optimisation to its Investable Mask and expanding the solved weights back to the caller's universe leaves a result that pairs a full-length w with a fee that spans two reduced axes: tn, l, s, fl and fs on the investable assets, lq and flq on the complement. A consumer that indexes the fee by the full-length weights meets a four-element field and a five-element selector, which is the defect of #914.
This verb closes the gap by moving the fee onto the axis the weights already live on. The mask it lifts at is the one the fee carries in imsk, which the door wrote when it reduced the fee, so the fee is the one source of the axes it is on. Every per-asset field comes back at length(imsk), zero-filled where it says nothing: the five holding fields carry a zero at each asset that left, and the two carriers carry a zero at each asset that stayed. A zero rate charges nothing and a zero reference weight trades nothing, so the lift moves no number the reduced fee already charged. A scalar rate applies to every asset whatever the axis is, so it is carried through untouched. The lifted fee is unmarked, because it is on the full universe again.
Algorithm
- On a
nothingfees, or an unmarked one, returnfeesunchanged. An unmarked fee is a caller's statement on the full universe, or the fee of an optimisation that reduced on nothing, and either is on the full universe already. - Lift
tnatfees.imskwithlift_turnover, andl,s,flandfswithlift_fee_rate. - Lift
lqandflqat.!fees.imsk, the complement the two carriers live on, by the same verb as step 2. - Rebuild through the keyword constructor, carrying
faandkwargsunchanged andimskasnothing.
Arguments
fees: The fee to lift, ornothing.
Returns
fees::typeof(fees): The fee on the full universe, unmarked.
Examples
julia> fees = Fees(; l = [0.001, 0.002], lq = Turnover(; w = [0.25], val = [0.01]), imsk = BitVector([true, false, true]));julia> PortfolioOptimisers.lift_fees(fees).l3-element Vector{Float64}: 0.001 0.0 0.002julia> PortfolioOptimisers.lift_fees(fees).lq.w3-element Vector{Float64}: 0.0 0.25 0.0julia> PortfolioOptimisers.lift_fees(fees).imskRelated
PortfolioOptimisers.lift_fee_rate — Function
lift_fee_rate(x::Nothing, ::BitVector)
lift_fee_rate(x::Number, ::BitVector)
lift_fee_rate(x::VecNum, imsk::BitVector)Put one per-asset fee rate back onto the full universe, zero where the mask is false.
The per-field step of lift_fees. A nothing field states no fee and a scalar field states one rate for every asset, so neither carries an axis and both are returned untouched. A vector field carries one entry per asset of the reduced axis, so it is expanded with expand_investable_weights.
Arguments
x: The rate to lift:nothing, a scalar, or one entry per asset of the reduced axis.imsk: The mask the rate was reduced on.
Returns
x: The rate on the full universe.
Related
PortfolioOptimisers.lift_turnover — Function
lift_turnover(tn::Nothing, ::BitVector)
lift_turnover(tn::Turnover, imsk::BitVector)Put a turnover carrier back onto the full universe, zero where the mask is false.
The nested step of lift_fees. A Turnover holds a reference weight per asset and a rate that is either a scalar or one entry per asset, so w always expands and val expands through lift_fee_rate. A zero reference weight trades nothing, so an asset the mask leaves out is charged nothing.
Arguments
tn: The carrier to lift, ornothing.imsk: The mask the carrier was reduced on.
Returns
tn: The carrier on the full universe, ornothing.
Related