Portfolio and asset fees
In active, and small quantity investing, fees can be a non-negligible factor that affects portfolio returns. PortfolioOptimisers.jl has the capability of including a variety of fees.
PortfolioOptimisers.FeesEstimator Type
struct FeesEstimator{T1, T2, T3, T4, T5, T6, T7, T8, T9, T10} <: AbstractEstimator
tn::T1
l::T2
s::T3
fl::T4
fs::T5
dl::T6
ds::T7
dfl::T8
dfs::T9
kwargs::T10
endEstimator for portfolio transaction fees constraints.
FeesEstimator specifies transaction fee constraints for each asset in a portfolio, including turnover fees, long/short proportional fees, and long/short fixed fees. Supports asset-specific fees via dictionaries, pairs, or vectors of pairs.
This estimator can be converted into a concrete Fees constraint using the fees_constraints function, which maps the estimator's specifications to the assets in a given AssetSets object.
Warning
The turnover and proportional fees must match the periodicity of the returns series, and the fixed fees must be divided by the portfolio's holding period. The units of the fees and returns must also be consistent.
Fields
tn: Turnover estimator or result.l: Long proportional fees.s: Short proportional fees.fl: Long fixed fees.fs: Short fixed fees.dl: Default long proportional fees.ds: Default short proportional fees.dfl: Default long fixed fees.dfs: Default short fixed fees.kwargs: Named tuple of keyword arguments for deciding how small an asset weight has to be before being considered zero.
Constructor
FeesEstimator(; tn::Option{<:TnE_Tn} = nothing, l::Option{<:EstValType} = nothing,
s::Option{<:EstValType} = nothing, fl::Option{<:EstValType} = nothing,
fs::Option{<:EstValType} = nothing, dl::Option{<:Number} = nothing,
ds::Option{<:Number} = nothing, dfl::Option{<:Number} = nothing,
dfs::Option{<:Number} = nothing, kwargs::NamedTuple = (; atol = 1e-8))Keyword arguments correspond to the fields above.
Validation
l,s,fl,fs,dl,ds,dfl,dfsare validated withassert_nonempty_nonneg_finite_val.
Examples
julia> FeesEstimator(; tn = TurnoverEstimator([0.2, 0.3, 0.5], Dict("A" => 0.1)),
l = Dict("A" => 0.001, "B" => 0.002), s = ["A" => 0.001, "B" => 0.002],
fl = Dict("A" => 5.0), fs = ["B" => 10.0])
FeesEstimator
tn ┼ TurnoverEstimator
│ w ┼ Vector{Float64}: [0.2, 0.3, 0.5]
│ val ┼ Dict{String, Float64}: Dict("A" => 0.1)
│ dval ┴ nothing
l ┼ Dict{String, Float64}: Dict("B" => 0.002, "A" => 0.001)
s ┼ Vector{Pair{String, Float64}}: ["A" => 0.001, "B" => 0.002]
fl ┼ Dict{String, Float64}: Dict("A" => 5.0)
fs ┼ Vector{Pair{String, Float64}}: ["B" => 10.0]
dl ┼ nothing
ds ┼ nothing
dfl ┼ nothing
dfs ┼ nothing
kwargs ┴ @NamedTuple{atol::Float64}: (atol = 1.0e-8,)Related
sourcePortfolioOptimisers.Fees Type
struct Fees{T1, T2, T3, T4, T5, T6} <: AbstractResult
tn::T1
l::T2
s::T3
fl::T4
fs::T5
kwargs::T6
endContainer for portfolio transaction fee constraints.
Fees stores transaction fee constraints for each asset in a portfolio, including turnover fees, long/short proportional fees, and long/short fixed fees. Fixed fees do not depend on the value of the asset weights, only whether it is positive or negative–-up to a tolerance defined by how close the weight is to zero defined by isapprox and the kwargs field.
Fee values can be specified as scalars (applied to all assets) or as vectors of per-asset values. The portfolio fees are computed by calc_fees and asset fees by calc_asset_fees.
Warning
The turnover and proportional fees must match the periodicity of the returns series, and the fixed fees must be divided by the portfolio's holding period. The units of the fees and returns must also be consistent.
Portfolio fees
For non-finite optimisations, the total portfolio transaction fees are computed as:
The finite optimisations use fees somewhat differently because they use a finite amount of capital as well as asset prices to compute the actual fees incurred when buying or selling assets. As such, these fees require a vector of asset prices to compute the actual fees incurred.
This method lets us automatically adjust the available cash amount during the optimisation so that fees are discounted from the available cash. It also lets us account for the budget constraints properly when fees are involved.
Per asset fees
It is also possible to compute per-asset fees incurred using the same definitions as above, but replacing the dot products with elementwise (Hadamard) products.
The finite optimisation uses fees somewhat differently because it uses a finite amount of capital and utilises the asset prices to compute the actual fees incurred when buying or selling assets. As such, these fees require a vector of asset prices to compute the actual fees incurred.
Where:
: Portfolio fee. : N × 1per asset vector of portfolio fees.: N × 1asset price vector.: N × 1per asset fee vector. If it is a scalar, it is broadcasted to all assets.: N × 1turnover vector as defined inTurnover. The benchmark weight vector is encoded in thewfield of the turnover object and the new weight vector is the portfolio weight vector.: Superscripts denote long and short fees respectively. This is because brokers sometimes charge different fees for long and short positions. : Subscripts for total, turnover, proportional, and fixed fees respectively. The turnover fee is encoded an instance of Turnover, wherevalis the per asset fee.: Elementwise (Hadamard) indicator function returning 1when the condition is true,0otherwise. This activates long or short fees based on whether the asset weight is non-negative or otherwise.: Elementwise (Hadamard) product.
Fields
tn: Turnover constraint result.l: Long proportional fees.s: Short proportional fees.fl: Long fixed fees.fs: Short fixed fees.kwargs: Named tuple of keyword arguments for deciding how small an asset weight has to be before being considered zero.
Constructor
Fees(; tn::Option{<:Turnover} = nothing, l::Option{<:Num_VecNum} = nothing,
s::Option{<:Num_VecNum} = nothing, fl::Option{<:Num_VecNum} = nothing,
fs::Option{<:Num_VecNum} = nothing, kwargs::NamedTuple = (; atol = 1e-8))Validation
l,s,fl,fsare validated withassert_nonempty_nonneg_finite_val.
Examples
julia> Fees(; tn = Turnover([0.2, 0.3, 0.5], [0.1, 0.0, 0.0]), l = [0.001, 0.002, 0.0],
s = [0.001, 0.002, 0.0], fl = [5.0, 0.0, 0.0], fs = [0.0, 10.0, 0.0])
Fees
tn ┼ Turnover
│ w ┼ Vector{Float64}: [0.2, 0.3, 0.5]
│ val ┴ Vector{Float64}: [0.1, 0.0, 0.0]
l ┼ Vector{Float64}: [0.001, 0.002, 0.0]
s ┼ Vector{Float64}: [0.001, 0.002, 0.0]
fl ┼ Vector{Float64}: [5.0, 0.0, 0.0]
fs ┼ Vector{Float64}: [0.0, 10.0, 0.0]
kwargs ┴ @NamedTuple{atol::Float64}: (atol = 1.0e-8,)Related
PortfolioOptimisers.FeesE_Fees Type
const FeesE_Fees = Union{<:Fees, <:FeesEstimator}Union type for fee constraint objects and estimators.
Related
sourcePortfolioOptimisers.fees_constraints Function
fees_constraints(fees::FeesEstimator, sets::AssetSets; datatype::DataType = Float64,
strict::Bool = false)Generate portfolio transaction fee constraints from a FeesEstimator and asset set.
fees_constraints constructs a Fees object representing transaction fee constraints for the assets in sets, using the specifications in fees. Supports asset-specific turnover, long/short proportional fees, and long/short fixed fees via dictionaries, pairs, or vectors of pairs, with flexible assignment and validation.
Arguments
fees:FeesEstimatorspecifying turnover, proportional, and fixed fee values.sets:AssetSetscontaining asset names or indices.datatype: Output data type for fee values.strict: Iftrue, enforces strict matching between assets and fee values (throws error on mismatch); iffalse, issues a warning.
Returns
fe::Fees: Object containing turnover, proportional, and fixed fee values aligned withsets.
Details
Fee values are extracted and mapped to assets using
estimator_to_val.If a fee value is missing for an asset, assigns zero unless
strictistrue.Turnover constraints are generated using
turnover_constraints.
Examples
julia> sets = AssetSets(; dict = Dict("nx" => ["A", "B", "C"]));
julia> fees = FeesEstimator(; tn = TurnoverEstimator([0.2, 0.3, 0.5], Dict("A" => 0.1), 0.0),
l = Dict("A" => 0.001, "B" => 0.002), s = ["A" => 0.001, "B" => 0.002],
fl = Dict("A" => 5.0), fs = ["B" => 10.0]);
julia> fees_constraints(fees, sets)
Fees
tn ┼ Turnover
│ w ┼ Vector{Float64}: [0.2, 0.3, 0.5]
│ val ┴ Vector{Float64}: [0.1, 0.0, 0.0]
l ┼ Vector{Float64}: [0.001, 0.002, 0.0]
s ┼ Vector{Float64}: [0.001, 0.002, 0.0]
fl ┼ Vector{Float64}: [5.0, 0.0, 0.0]
fs ┼ Vector{Float64}: [0.0, 10.0, 0.0]
kwargs ┴ @NamedTuple{atol::Float64}: (atol = 1.0e-8,)Related
sourcefees_constraints(fees::Option{<:Fees}, args...; kwargs...)Propagate or pass through portfolio transaction fee constraints.
fees_constraints returns the input Fees object or nothing unchanged. This method is used to propagate already constructed fee constraints or missing constraints, enabling composability and uniform interface handling in constraint generation workflows.
Arguments
fees: An existingFeesobject ornothing.args...: Additional positional arguments (ignored).kwargs...: Additional keyword arguments (ignored).
Returns
fe::Option{<:Fees}: The input constraint object, unchanged.
Examples
julia> fees = Fees(; tn = Turnover([0.2, 0.3, 0.5], [0.1, 0.0, 0.0]), l = [0.001, 0.002, 0.0]);
julia> fees_constraints(fees)
Fees
tn ┼ Turnover
│ w ┼ Vector{Float64}: [0.2, 0.3, 0.5]
│ val ┴ Vector{Float64}: [0.1, 0.0, 0.0]
l ┼ Vector{Float64}: [0.001, 0.002, 0.0]
s ┼ nothing
fl ┼ nothing
fs ┼ nothing
kwargs ┴ @NamedTuple{atol::Float64}: (atol = 1.0e-8,)
julia> fees_constraints(nothing)Related
sourcePortfolioOptimisers.fees_view Function
fees_view(fees::FeesEstimator, i)Create a view of a FeesEstimator for a subset of assets.
Returns a new FeesEstimator with all fee fields restricted to the indices or assets specified by i. The default fee values and keyword arguments are propagated unchanged.
Arguments
fees: Instance ofFeesEstimator.i: Index or indices specifying the subset of assets.
Returns
fe::FeesEstimator: New estimator with fields restricted to the specified subset.
Details
Uses
turnover_viewto subset the turnover estimator/result.Uses
nothing_scalar_array_viewto subset proportional and fixed fee fields.Propagates default fee values and keyword arguments unchanged.
Enables composable processing of asset subsets for fee constraints.
Examples
julia> fees = FeesEstimator(; tn = TurnoverEstimator([0.2, 0.3, 0.5], Dict("A" => 0.1)),
l = Dict("A" => 0.001, "B" => 0.002), s = ["A" => 0.001, "B" => 0.002],
fl = Dict("A" => 5.0), fs = ["B" => 10.0]);
julia> PortfolioOptimisers.fees_view(fees, 1:2)
FeesEstimator
tn ┼ TurnoverEstimator
│ w ┼ SubArray{Float64, 1, Vector{Float64}, Tuple{UnitRange{Int64}}, true}: [0.2, 0.3]
│ val ┼ Dict{String, Float64}: Dict("A" => 0.1)
│ dval ┴ nothing
l ┼ Dict{String, Float64}: Dict("B" => 0.002, "A" => 0.001)
s ┼ Vector{Pair{String, Float64}}: ["A" => 0.001, "B" => 0.002]
fl ┼ Dict{String, Float64}: Dict("A" => 5.0)
fs ┼ Vector{Pair{String, Float64}}: ["B" => 10.0]
dl ┼ nothing
ds ┼ nothing
dfl ┼ nothing
dfs ┼ nothing
kwargs ┴ @NamedTuple{atol::Float64}: (atol = 1.0e-8,)Related
sourcefees_view(::Nothing, ::Any)Return nothing when no fee estimator or constraint is provided.
This method is used as a fallback for missing fee estimators or constraints, ensuring composability and uniform interface handling in fee constraint processing workflows.
Arguments
::Nothing: Indicates absence of a fee estimator or constraint.::Any: Index or argument (ignored).
Returns
nothing.
Related
sourcefees_view(fees::Fees, i)Create a view of a Fees constraint for a subset of assets.
Returns a new Fees object with all fee fields restricted to the indices or assets specified by i. The keyword arguments are propagated unchanged.
Arguments
fees: AFeesconstraint object containing turnover, proportional, and fixed fee values.i: Index or indices specifying the subset of assets.
Returns
fe::Fees: New constraint object with fields restricted to the specified subset.
Details
Uses
turnover_viewto subset the turnover constraint.Uses
nothing_scalar_array_viewto subset proportional and fixed fee fields.Propagates keyword arguments unchanged.
Enables composable processing of asset subsets for fee constraints.
Examples
julia> fees = Fees(; tn = Turnover([0.2, 0.3, 0.5], [0.1, 0.0, 0.0]), l = [0.001, 0.002, 0.0],
s = [0.001, 0.002, 0.0], fl = [5.0, 0.0, 0.0], fs = [0.0, 10.0, 0.0]);
julia> PortfolioOptimisers.fees_view(fees, 1:2)
Fees
tn ┼ Turnover
│ w ┼ SubArray{Float64, 1, Vector{Float64}, Tuple{UnitRange{Int64}}, true}: [0.2, 0.3]
│ val ┴ SubArray{Float64, 1, Vector{Float64}, Tuple{UnitRange{Int64}}, true}: [0.1, 0.0]
l ┼ SubArray{Float64, 1, Vector{Float64}, Tuple{UnitRange{Int64}}, true}: [0.001, 0.002]
s ┼ SubArray{Float64, 1, Vector{Float64}, Tuple{UnitRange{Int64}}, true}: [0.001, 0.002]
fl ┼ SubArray{Float64, 1, Vector{Float64}, Tuple{UnitRange{Int64}}, true}: [5.0, 0.0]
fs ┼ SubArray{Float64, 1, Vector{Float64}, Tuple{UnitRange{Int64}}, true}: [0.0, 10.0]
kwargs ┴ @NamedTuple{atol::Float64}: (atol = 1.0e-8,)Related
sourcePortfolioOptimisers.calc_fees Function
calc_fees(w::VecNum, p::VecNum, ::Nothing, ::Function)
calc_fees(w::VecNum, p::VecNum, fees::Number, op::Function)
calc_fees(w::VecNum, p::VecNum, fees::VecNum, op::Function)Compute the actual proportional fees for portfolio weights and prices.
Arguments
w: Portfolio weights.p: Asset prices.fees: Scalar fee value.nothing: No proportional fee, returns zero.Number: Single fee applied to all relevant assets.VecNum: Vector of fee values per asset.
op: Function to select assets,.>=for long,<for short (ignored iffeesisnothing).
Returns
val::Number: Total actual proportional fee.
Examples
julia> calc_fees([0.1, 0.2], [100, 200], 0.01, .>=)
0.5Related
sourcecalc_fees(w::VecNum, p::VecNum, ::Nothing)
calc_fees(w::VecNum, p::VecNum, tn::Turnover)Compute the actual turnover fees for portfolio weights and prices.
Arguments
w: Portfolio weights.p: Asset prices.tn: Turnover structure.nothing: No turnover fee, returns zero.tn.val::Number: Single turnover fee applied to all assets.tn.val::VecNum: Vector of turnover fees per asset.
Returns
val::Number: Actual turnover fee.
Examples
julia> calc_fees([0.1, 0.2], [100, 200], Turnover([0.0, 0.0], 0.01))
0.5Related
sourcecalc_fees(w::VecNum, p::VecNum, fees::Fees)Compute total actual fees for portfolio weights and prices.
Sums actual proportional, fixed, and turnover fees for all assets.
Arguments
w: Portfolio weights.p: Asset prices.fees:Feesstructure.
Returns
val::Number: Total actual fees.
Examples
julia> fees = Fees(; l = [0.01, 0.02], s = [0.01, 0.02], fl = [5.0, 0.0], fs = [0.0, 10.0]);
julia> calc_fees([0.1, -0.2], [100, 200], fees)
15.9Related
sourcecalc_fees(w::VecNum, ::Nothing, ::Function)
calc_fees(w::VecNum, fees::Number, op::Function)
calc_fees(w::VecNum, fees::VecNum, op::Function)Compute the proportional fees for portfolio weights and prices.
Arguments
w: Portfolio weights.fees: Scalar fee value.nothing: No proportional fee, returns zero.Number: Single fee applied to all relevant assets.VecNum: Vector of fee values per asset.
op: Function to select assets,.>=for long,<for short (ignored iffeesisnothing).
Returns
val::Number: Total proportional fee.
Examples
julia> calc_fees([0.1, 0.2], 0.01, .>=)
0.003Related
sourcecalc_fees(w::VecNum, ::Nothing)
calc_fees(w::VecNum, tn::Turnover)Compute the turnover fees for portfolio weights and prices.
Arguments
w: Portfolio weights.tn: Turnover structure.nothing: No turnover fee, returns zero.tn.val::Number: Single turnover fee applied to all assets.tn.val::VecNum: Vector of turnover fees per asset.
Returns
val::Number: Turnover fee.
Examples
julia> calc_fees([0.8, 0.2], Turnover([0.0, 0.0], 0.02))
0.02Related
sourcecalc_fees(w::VecNum, fees::Fees)Compute total fees for portfolio weights and prices.
Sums proportional, fixed, and turnover fees for all assets.
Arguments
w: Portfolio weights.p: Asset prices.fees:Feesstructure.
Returns
val::Number: Total fees.
Examples
julia> fees = Fees(; l = [0.01, 0.02], s = [0.01, 0.02], fl = [5.0, 0.0], fs = [0.0, 10.0]);
julia> calc_fees([0.1, -0.2], fees)
15.004999999999999Related
sourcePortfolioOptimisers.calc_fixed_fees Function
calc_fixed_fees(w::VecNum, ::Nothing, kwargs::NamedTuple, ::Function)
calc_fixed_fees(w::VecNum, fees::Number, kwargs::NamedTuple, op::Function)
calc_fixed_fees(w::VecNum, fees::VecNum, kwargs::NamedTuple, op::Function)Compute the fixed portfolio fees for assets that have been allocated.
Arguments
w: Portfolio weights.fees: Scalar fee value.nothing: No proportional fee, returns zero.Number: Single fee applied to all relevant assets.VecNum: Vector of fee values per asset.
kwargs: Named tuple of keyword arguments for deciding how small an asset weight has to be before being considered zero.op: Function to select assets,.>=for long,<for short (ignored iffeesisnothing).
Returns
val::Number: Total fixed fee.
Examples
julia> calc_fixed_fees([0.1, 0.2], 0.01, (; atol = 1e-6), .>=)
0.02Related
sourcePortfolioOptimisers.calc_asset_fees Function
calc_asset_fees(w::VecNum, p::VecNum, ::Nothing, ::Function)
calc_asset_fees(w::VecNum, p::VecNum, fees::Number, op::Function)
calc_asset_fees(w::VecNum, p::VecNum, fees::VecNum, op::Function)Compute the actual proportional per asset fees for portfolio weights and prices.
Arguments
w: Portfolio weights.p: Asset prices.fees: Scalar fee value.nothing: No proportional fee, returns zero.Number: Single fee applied to all relevant assets.VecNum: Vector of fee values per asset.
op: Function to select assets,.>=for long,<for short (ignored iffeesisnothing).
Returns
val::VecNum: Total actual proportional per asset fee.
Examples
julia> calc_asset_fees([0.1, 0.2], [100, 200], 0.01, .>=)
2-element Vector{Float64}:
0.1
0.4Related
sourcecalc_asset_fees(w::VecNum, p::VecNum, ::Nothing)
calc_asset_fees(w::VecNum, p::VecNum, tn::Turnover)Compute the actual per asset turnover fees for portfolio weights and prices.
Arguments
w: Portfolio weights.p: Asset prices.tn: Turnover structure.nothing: No turnover fee, returns zero.tn.val::Number: Single turnover fee applied to all assets.tn.val::VecNum: Vector of turnover fees per asset.
Returns
val::VecNum: Actual per asset turnover fee.
Examples
julia> calc_asset_fees([0.1, 0.2], [100, 200], Turnover([0.0, 0.0], 0.01))
2-element Vector{Float64}:
0.1
0.4Related
sourcecalc_asset_fees(w::VecNum, p::VecNum, fees::Fees)Compute total actual per asset fees for portfolio weights and prices.
Sums actual proportional, fixed, and turnover fees for all assets.
Arguments
w: Portfolio weights.p: Asset prices.fees:Feesstructure.
Returns
val::VecNum: Total actual per asset fees.
Examples
julia> fees = Fees(; l = [0.01, 0.02], s = [0.01, 0.02], fl = [5.0, 0.0], fs = [0.0, 10.0]);
julia> calc_asset_fees([0.1, -0.2], [100, 200], fees)
2-element Vector{Float64}:
5.1
10.8Related
sourcecalc_asset_fees(w::VecNum, ::Nothing, ::Function)
calc_asset_fees(w::VecNum, fees::Number, op::Function)
calc_asset_fees(w::VecNum, fees::VecNum, op::Function)Compute the proportional per asset fees for portfolio weights and prices.
Arguments
w: Portfolio weights.fees: Scalar fee value.nothing: No proportional fee, returns zero.Number: Single fee applied to all relevant assets.VecNum: Vector of fee values per asset.
op: Function to select assets,.>=for long,<for short (ignored iffeesisnothing).
Returns
val::VecNum: Total proportional per asset fee.
Examples
julia> calc_asset_fees([0.1, 0.2], 0.01, .>=)
2-element Vector{Float64}:
0.001
0.002Related
sourcecalc_asset_fees(w::VecNum, ::Nothing)
calc_asset_fees(w::VecNum, tn::Turnover)Compute the per asset turnover fees for portfolio weights and prices.
Arguments
w: Portfolio weights.tn: Turnover structure.nothing: No turnover fee, returns zero.tn.val::Number: Single turnover fee applied to all assets.tn.val::VecNum: Vector of turnover fees per asset.
Returns
val::VecNum: Per asset turnover fee.
Examples
julia> calc_asset_fees([0.1, 0.2], Turnover([0.0, 0.0], 0.01))
2-element Vector{Float64}:
0.001
0.002Related
sourcecalc_asset_fees(w::VecNum, fees::Fees)Compute total per asset fees for portfolio weights and prices.
Sums proportional, fixed, and turnover fees for all assets.
Arguments
w: Portfolio weights.p: Asset prices.fees:Feesstructure.
Returns
val::VecNum: Total per asset fees.
Examples
julia> fees = Fees(; l = [0.01, 0.02], s = [0.01, 0.02], fl = [5.0, 0.0], fs = [0.0, 10.0]);
julia> calc_asset_fees([0.1, -0.2], fees)
2-element Vector{Float64}:
5.001
10.004Related
sourcePortfolioOptimisers.calc_asset_fixed_fees Function
calc_asset_fixed_fees(w::VecNum, ::Nothing, kwargs::NamedTuple, ::Function)
calc_asset_fixed_fees(w::VecNum, fees::Number, kwargs::NamedTuple, op::Function)
calc_asset_fixed_fees(w::VecNum, fees::VecNum, kwargs::NamedTuple, op::Function)Compute the per asset fixed portfolio fees for assets that have been allocated.
Arguments
w: Portfolio weights.fees: Scalar fee value.nothing: No proportional fee, returns zero.Number: Single fee applied to all relevant assets.VecNum: Vector of fee values per asset.
kwargs: Named tuple of keyword arguments for deciding how small an asset weight has to be before being considered zero.op: Function to select assets,.>=for long,<for short (ignored iffeesisnothing).
Returns
val::VecNum: Total per asset fixed fee.
Examples
julia> calc_asset_fixed_fees([0.1, 0.2], 0.01, (; atol = 1e-6), .>=)
2-element Vector{Float64}:
0.01
0.01Related
source