Portfolio and asset fees: private API

PortfolioOptimisers.AbstractFeeAmortisationType
abstract type AbstractFeeAmortisation <: AbstractAlgorithm

Supertype 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

source
PortfolioOptimisers.calc_periodic_feesFunction
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

  1. Charge the long proportional term, the call of calc_fees on fees.l under .>=.
  2. Charge the short proportional term, the negated call of the same name on fees.s under .<. w is negative on that side, so the negation is what makes the term a positive charge.
  3. Charge the turnover term, the call of calc_fees on fees.tn.
  4. Return the sum of the three terms.

Arguments

  • w: Portfolio weights.
  • fees: Fees structure.

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.012

Related

source
PortfolioOptimisers.calc_one_off_feesFunction
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

  1. Charge the long fixed term, the call of calc_fixed_fees on fees.fl under .>=.
  2. Charge the short fixed term, the call of the same name on fees.fs under .<.
  3. Return the sum of the two terms.

Arguments

  • w: Portfolio weights.
  • fees: Fees structure.

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.0

Related

source
PortfolioOptimisers.calc_asset_periodic_feesFunction
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

  1. Charge the long proportional term, the call of calc_asset_fees on fees.l under .>=.
  2. Charge the short proportional term, the negated call of the same name on fees.s under .<.
  3. Charge the turnover term, the call of calc_asset_fees on fees.tn.
  4. Charge the proportional forced exit, the call of calc_asset_liquidation_fees on fees.lq. lq is a rate, so it falls on every period beside l, s and tn, and no clock reaches it.
  5. 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: Fees structure.

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, from l, s and tn, which were sliced to the Investable Mask.
  • liquidation::VecNum: The per period charge of each asset that left, from lq, which was sliced to the mask's complement. Empty when lq is 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

source
PortfolioOptimisers.calc_asset_one_off_feesFunction
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

  1. Charge the long fixed term, the call of calc_asset_fixed_fees on fees.fl under .>=.
  2. Charge the short fixed term, the call of the same name on fees.fs under .<.
  3. Charge the fixed forced exit, the call of calc_asset_fixed_liquidation_fees on fees.flq. flq is a currency amount charged one time, so it falls on the clock fees.fa names, beside fl and fs.
  4. 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: Fees structure.

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, from fl and fs, which were sliced to the Investable Mask.
  • liquidation::VecNum: The one-off charge of each asset that left, from flq, which was sliced to the mask's complement. Empty when flq is 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

source
PortfolioOptimisers.calc_liquidation_feesFunction
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

  1. On a nothing lq, return false. Nothing left the universe, so nothing is owed, and a Bool zero adds to a charge of any element type without widening it.
  2. On a scalar rate, return lq.val * sum(abs, lq.w).
  3. On a per asset rate, return dot(lq.val, abs.(lq.w)).

Arguments

  • lq: The proportional liquidation carrier, or nothing.

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.0025

Related

source
PortfolioOptimisers.calc_fixed_liquidation_feesFunction
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

  1. On a nothing flq, return false, the same Bool zero calc_liquidation_fees returns.
  2. Otherwise charge calc_fixed_fees on flq.w and flq.val under .>=, the liquidated long positions.
  3. Charge the same under .<, the liquidated short positions.
  4. Return the sum of the two.

Arguments

  • flq: The fixed liquidation carrier, or nothing.
  • kwargs: Forwarded to isapprox to 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.0

Related

source
PortfolioOptimisers.calc_asset_liquidation_feesFunction
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

  1. On a nothing lq, return an empty vector in the element type of w.
  2. Otherwise call calc_asset_fees on lq against a zero vector the length of lq.w, giving lq.val .* abs.(lq.w) elementwise.

Arguments

  • w: Portfolio weights, read for their element type.
  • lq: The proportional liquidation carrier, or nothing.

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.0025

Related

source
PortfolioOptimisers.calc_asset_fixed_liquidation_feesFunction
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

  1. On a nothing flq, return an empty vector in the element type of w.
  2. Otherwise charge calc_asset_fixed_fees on flq.w and flq.val under .>= and again under .<, and return the elementwise sum.

Arguments

  • w: Portfolio weights, read for their element type.
  • flq: The fixed liquidation carrier, or nothing.
  • kwargs: Forwarded to isapprox to 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.0

Related

source
PortfolioOptimisers.add_liquidation_termsFunction
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

source
PortfolioOptimisers.override_fee_amortisationFunction
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

  1. On a nothing fa, return fees unchanged, whether it is a Fees or nothing.
  2. On a stated fa and a nothing fees, return nothing. There is no fee to charge, so there is no clock to state.
  3. On a stated fa and a Fees, rebuild the fee with that fa and its own five fee fields and kwargs.

Arguments

  • fees: The fee the fit saw, or nothing.
  • fa: The clock the scheme states, or nothing to inherit the fee's own.

Returns

  • Option{<:Fees}: The fee the report charges.

Related

source
PortfolioOptimisers.two_axis_fees_viewFunction
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

  1. Build j, the complement of i in 1:n.
  2. Slice tn at i through port_opt_view, whose @vprop tags take w and a vector val and leave a scalar or dictionary val alone.
  3. Slice l, s, fl and fs at i through nothing_scalar_array_view.
  4. On an empty j no asset left the universe, so set lq and flq to nothing: there is nothing to liquidate, and a Turnover refuses an empty w in any case. Otherwise slice both at j, by the same verb as step 2.
  5. Rebuild through the keyword constructor, carrying fa and kwargs unchanged and imsk as nothing.

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

source
PortfolioOptimisers.strip_liquidation_carriersFunction
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

  1. On a nothing fees, or a BitVector imsk, return fees unchanged. A stated nothing fee under a derived mask satisfies both, so a third method names that pair and breaks the ambiguity.
  2. On a nothing imsk with both carriers already nothing, return fees unchanged, so the common case allocates nothing.
  3. Otherwise rebuild the fee with lq and flq set to nothing, carrying every other field through.

Arguments

  • fees: The fee to strip, or nothing.
  • imsk: The Investable Mask, or nothing when every asset is investable.

Returns

  • fees::typeof(fees): The fee, with no liquidation carrier when none can apply.

Related

source
PortfolioOptimisers.investable_fees_viewFunction
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

  1. On a nothing fee, return nothing.
  2. On a marked fee, return it untouched when the mark is imsk, or the mark is a BitVector and imsk is nothing; raise an ArgumentError naming both masks when the mark is a BitVector other than imsk.
  3. On an unmarked fee and a nothing mask, hand the fee to strip_liquidation_carriers, which drops both carriers and returns a fee carrying neither untouched.
  4. On an unmarked fee and a BitVector mask, take two_axis_fees_view at findall(imsk) over the width of the unreduced universe, size(X, 2) or n, which slices the five per-asset fields to the mask and the two carriers to its complement, and rebuild it with imsk as its mark.

Arguments

  • fees: The fee resolved over the caller's full universe, a fee this verb already reduced, or nothing.
  • imsk: The Investable Mask, or nothing when 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 BitVector other than imsk is refused with an ArgumentError naming 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 with lift_fees and reduce the caller's statement instead.

Returns

  • Option{<:Fees}: The fee on the axes the mask leaves, marked with the mask.

Related

source
PortfolioOptimisers.mark_feesFunction
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

  1. On a nothing fee, return it with the stated mask.
  2. On a nothing mask, return the fee with its own mark, which may be nothing.
  3. On an unmarked fee and a BitVector mask, rebuild the fee with that mask as its mark.
  4. On a marked fee and a BitVector mask, raise an ArgumentError naming both when they differ, and return the pair otherwise.

Arguments

  • fees: The fee, or nothing.
  • imsk: The Investable Mask the caller states, or nothing.

Validation

  • A fee marked with a BitVector other than imsk is refused with an ArgumentError naming the two masks.

Returns

  • (fees, imsk): The fee, marked with the mask when there is one, and the mask.

Related

source
PortfolioOptimisers.lift_feesFunction
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

  1. On a nothing fees, or an unmarked one, return fees unchanged. 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.
  2. Lift tn at fees.imsk with lift_turnover, and l, s, fl and fs with lift_fee_rate.
  3. Lift lq and flq at .!fees.imsk, the complement the two carriers live on, by the same verb as step 2.
  4. Rebuild through the keyword constructor, carrying fa and kwargs unchanged and imsk as nothing.

Arguments

  • fees: The fee to lift, or nothing.

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).imsk

Related

source
PortfolioOptimisers.lift_fee_rateFunction
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

source
PortfolioOptimisers.lift_turnoverFunction
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, or nothing.
  • imsk: The mask the carrier was reduced on.

Returns

  • tn: The carrier on the full universe, or nothing.

Related

source