Asset turnover

The turnover is used to measure the absolute weight change between the current weights and benchmark weights. They can be used to compute fees, or as a constraint. It can also be used as a risk measure, but we will detail that use in Risk Measures

PortfolioOptimisers.TurnoverEstimatorType
struct TurnoverEstimator{__T_w, __T_val, __T_dval, __T_fixed} <: AbstractEstimator

Names the per-asset turnover bounds, for turnover_constraints to align to a universe.

val accepts a dictionary, a pair, or a vector of pairs keyed by asset or group name, and dval fills every asset the keys miss. turnover_constraints resolves the names against a UniverseSets and returns a Turnover, whose val is a plain per-asset vector.

As on Turnover, the w field holds the reference weights, not the candidate weights.

Fields

  • w: Reference portfolio weight vector. Deviations are measured against it, and it is never the candidate weight vector an optimiser solves for.
  • val: Default value to use for the estimator. If nothing, the estimator provides the default value.
  • dval: Default value for assets not specified in val.
  • fixed: Whether the estimator is fixed and does not update with new weights.

Constructors

TurnoverEstimator(;    w::VecNum,    val::EstValType{<:VectorAbstractEstimatorValueAlgorithm},    dval::Option{<:Number} = nothing,    fixed::Bool = false) -> TurnoverEstimator

Keywords correspond to the struct's fields.

Validation

  • w, through assert_nonempty_finite_val: !isempty(w) and all(isfinite, w).

  • val, through assert_nonempty_nonneg_finite_val:

    • AbstractDict: !isempty(val), all(isfinite, values(val)) and all(x -> x >= 0, values(val)).
    • Vector of pairs: !isempty(val), all(isfinite, getindex.(val, 2)) and all(x -> x[2] >= 0, val).
    • Pair: isfinite(val[2]) and val[2] >= 0.
  • dval: if not nothing, dval >= 0. An infinity is admitted here where a named cap in val is not: dval is the cap of every asset val does not name, and +Inf is how it leaves them uncapped, dval = nothing filling zero and freezing them instead.

View parameters

When port_opt_view is called on this type, the following @vprop-tagged fields are automatically subset to the selected indices:

Examples

julia> TurnoverEstimator(; w = [0.2, 0.3, 0.5], val = Dict("A" => 0.1, "B" => 0.2), dval = 0.0)TurnoverEstimator      w ┼ Vector{Float64}: [0.2, 0.3, 0.5]    val ┼ Dict{String, Float64}: Dict("B" => 0.2, "A" => 0.1)   dval ┼ Float64: 0.0  fixed ┴ Bool: false

Related

source
PortfolioOptimisers.TurnoverType
struct Turnover{__T_w, __T_val, __T_fixed} <: AbstractResult

Bounds the per-asset weight change against a reference portfolio.

Warning

The w field holds the reference weights, not the candidate weights. The candidate is the optimiser's own weight variable, which no field carries. factory(tn, w) replaces the reference, which is how the previous rebalance becomes the next reference.

val is the bound. Fees reuses the same type with val read as a per-asset fee rate instead, so read the meaning of val off the type that holds the Turnover.

Mathematical definition

\[\begin{align} \boldsymbol{Tn}(\boldsymbol{w}) &\coloneqq \lvert \boldsymbol{w} - \boldsymbol{w}_{0} \rvert\,, \\ \boldsymbol{Tn}(\boldsymbol{w}) &\leq \boldsymbol{\delta}\,. \end{align}\]

Where:

  • $\boldsymbol{Tn}(\boldsymbol{w})$: N × 1 turnover vector.
  • $\boldsymbol{w}$: N × 1 vector of candidate portfolio weights.
  • $\boldsymbol{w}_{0}$: N × 1 vector of reference portfolio weights, the w field.
  • $\boldsymbol{\delta}$: N × 1 vector of maximum turnover, the val field. A scalar val broadcasts to every asset.
  • $\lvert \cdot \rvert$: Element-wise absolute value.

Fields

  • w: Reference portfolio weight vector. Deviations are measured against it, and it is never the candidate weight vector an optimiser solves for.
  • val: Default value to use for the estimator. If nothing, the estimator provides the default value.
  • fixed: Whether the estimator is fixed and does not update with new weights.

Constructors

Turnover(;    w::VecNum,    val::Num_VecNum = 0.0,    fixed::Bool = false) -> Turnover

Keywords correspond to the struct's fields.

Validation

The rules are listed in the order of the raises, so the first rule a value breaks is the one it is told about.

  • w, through assert_nonempty_finite_val: !isempty(w) and all(isfinite, w).

  • val, through assert_nonempty and assert_nonneg:

    • AbstractVector: !isempty(val) and all(x -> x >= 0, val).
    • Number: val >= 0.

    A cap of +Inf is an uncapped asset, so val is not held to finiteness. A NaN and a -Inf are refused, neither being >= 0.

  • length(w) == length(val) when val is an AbstractVector, raising a DimensionMismatch. This rule is checked last.

View parameters

When port_opt_view is called on this type, the following @vprop-tagged fields are automatically subset to the selected indices:

Examples

julia> Turnover(; w = [0.2, 0.3, 0.5], val = [0.1, 0.2, 0.0])Turnover      w ┼ Vector{Float64}: [0.2, 0.3, 0.5]    val ┼ Vector{Float64}: [0.1, 0.2, 0.0]  fixed ┴ Bool: falsejulia> Turnover(; w = [0.2, 0.3, 0.5], val = 0.02, fixed = true)Turnover      w ┼ Vector{Float64}: [0.2, 0.3, 0.5]    val ┼ Float64: 0.02  fixed ┴ Bool: true

Related

References

  • [5] D. Cajas. Advanced Portfolio Optimization: A Cutting-edge Quantitative Approach (Springer Nature Switzerland, 2025). Section 9.1, Equations 9.10 and 9.11.
source
PortfolioOptimisers.factoryMethod
factory(tn::Turnover, w::VecNum)

Replace the reference weights of a Turnover, unless fixed holds them.

The fixed field decides which weight vector survives. A fixed constraint pins the reference weights it was built with, so the incoming w is discarded and the argument tn is returned itself.

Algorithm

  1. Read tn.fixed. When it is true, return tn unchanged: the reference weights tn.w survive and the argument w is discarded.
  2. When it is false, build a new Turnover whose w is the argument w, and whose val and fixed are those of tn. The argument w survives.

Arguments

  • tn: Existing Turnover constraint object. Supplies the turnover values and the fixed flag.
  • w: Candidate reference weights vector.

Validation

  • Step 2 builds a Turnover, so w meets that constructor's rules. Step 1 builds nothing and checks nothing.

Returns

  • tn::Turnover: tn itself when tn.fixed is true, otherwise a new constraint carrying w as its reference weights.

Examples

julia> tn = Turnover(; w = [0.2, 0.3, 0.5], val = [0.1, 0.2, 0.0])Turnover      w ┼ Vector{Float64}: [0.2, 0.3, 0.5]    val ┼ Vector{Float64}: [0.1, 0.2, 0.0]  fixed ┴ Bool: falsejulia> factory(tn, [0.0, 0.2, 0.8])Turnover      w ┼ Vector{Float64}: [0.0, 0.2, 0.8]    val ┼ Vector{Float64}: [0.1, 0.2, 0.0]  fixed ┴ Bool: falsejulia> tn = Turnover(; w = [0.2, 0.3, 0.5], val = [0.1, 0.2, 0.0], fixed = true)Turnover      w ┼ Vector{Float64}: [0.2, 0.3, 0.5]    val ┼ Vector{Float64}: [0.1, 0.2, 0.0]  fixed ┴ Bool: truejulia> factory(tn, [0.0, 0.2, 0.8])Turnover      w ┼ Vector{Float64}: [0.2, 0.3, 0.5]    val ┼ Vector{Float64}: [0.1, 0.2, 0.0]  fixed ┴ Bool: true

Related

source
PortfolioOptimisers.factoryMethod
factory(tn::TurnoverEstimator, w::VecNum)

Replace the reference weights of a TurnoverEstimator, unless fixed holds them.

The fixed field decides which weight vector survives. A fixed estimator pins the reference weights it was built with, so the incoming w is discarded and the argument tn is returned itself.

Algorithm

  1. Read tn.fixed. When it is true, return tn unchanged: the reference weights tn.w survive and the argument w is discarded.
  2. When it is false, build a new TurnoverEstimator whose w is the argument w, and whose val, dval and fixed are those of tn. The argument w survives.

Arguments

  • tn: Existing TurnoverEstimator object. Supplies the turnover values, the default value and the fixed flag.
  • w: Candidate reference weights vector.

Validation

  • Step 2 builds a TurnoverEstimator, so w meets that constructor's rules. Step 1 builds nothing and checks nothing.

Returns

  • tn::TurnoverEstimator: tn itself when tn.fixed is true, otherwise a new estimator carrying w as its reference weights.

Examples

julia> tn = TurnoverEstimator(; w = [0.2, 0.3, 0.5], val = Dict("A" => 0.1, "B" => 0.2),                              dval = 0.0)TurnoverEstimator      w ┼ Vector{Float64}: [0.2, 0.3, 0.5]    val ┼ Dict{String, Float64}: Dict("B" => 0.2, "A" => 0.1)   dval ┼ Float64: 0.0  fixed ┴ Bool: falsejulia> factory(tn, [0.1, 0.4, 0.5])TurnoverEstimator      w ┼ Vector{Float64}: [0.1, 0.4, 0.5]    val ┼ Dict{String, Float64}: Dict("B" => 0.2, "A" => 0.1)   dval ┼ Float64: 0.0  fixed ┴ Bool: falsejulia> tn = TurnoverEstimator(; w = [0.2, 0.3, 0.5], val = Dict("A" => 0.1, "B" => 0.2),                              dval = 0.0, fixed = true)TurnoverEstimator      w ┼ Vector{Float64}: [0.2, 0.3, 0.5]    val ┼ Dict{String, Float64}: Dict("B" => 0.2, "A" => 0.1)   dval ┼ Float64: 0.0  fixed ┴ Bool: truejulia> factory(tn, [0.1, 0.4, 0.5])TurnoverEstimator      w ┼ Vector{Float64}: [0.2, 0.3, 0.5]    val ┼ Dict{String, Float64}: Dict("B" => 0.2, "A" => 0.1)   dval ┼ Float64: 0.0  fixed ┴ Bool: true

Related

source
PortfolioOptimisers.factoryMethod
factory(tn::VecTnE_Tn, w::VecNum)

Create new turnover constraints or estimators with updated portfolio weights.

Applies factory to each element in tn, constructing a new collection of turnover constraints or estimators with the provided portfolio weights w.

This is the generic vector factory plus concrete_typed_array_if_abstract: a mixed vector of Turnover and TurnoverEstimator keeps a concrete element type.

Algorithm

  1. For each entry tni of tn, in the order of tn, call factory on tni with w. A fixed entry returns itself and keeps its own reference weights, so a vector may hold both outcomes.
  2. Pass the collected vector through concrete_typed_array_if_abstract. Step 1 can widen the element type to an abstract one, because the two turnover types have no common concrete type; this step narrows it back to a Union the compiler can dispatch on.

Arguments

  • tn: Vector of turnover constraints or estimators.
  • w: New portfolio weights vector.

Returns

  • res::VecTnE_Tn: Vector of updated turnover constraints or estimators.

Examples

julia> tn1 = Turnover(; w = [0.2, 0.3, 0.5], val = [0.1, 0.2, 0.0]);julia> tn2 = Turnover(; w = [0.2, 0.3, 0.5], val = [0.05, 0.1, 0.0]);julia> factory([tn1, tn2], [0.1, 0.4, 0.5])2-element Vector{Turnover{Vector{Float64}, Vector{Float64}, Bool}}: Turnover      w ┼ Vector{Float64}: [0.1, 0.4, 0.5]    val ┼ Vector{Float64}: [0.1, 0.2, 0.0]  fixed ┴ Bool: false Turnover      w ┼ Vector{Float64}: [0.1, 0.4, 0.5]    val ┼ Vector{Float64}: [0.05, 0.1, 0.0]  fixed ┴ Bool: false

Related

source
PortfolioOptimisers.turnover_constraintsFunction
turnover_constraints(tn::TurnoverEstimator, sets::UniverseSets; datatype::DataType = Float64,
                     strict::Bool = false)

Generate turnover portfolio constraints from a TurnoverEstimator and asset set.

turnover_constraints constructs a Turnover object representing turnover constraints for the assets in sets, using the specifications in tn. Supports scalar, vector, dictionary, pair, or custom turnover types for flexible assignment and validation.

Algorithm

  1. Resolve tn.val against the universe of sets with estimator_to_val, giving one turnover bound per asset. The bounds follow the order of the universe, not the order of the keys of tn.val. Every asset the keys miss takes tn.dval, or zero(datatype) when tn.dval is nothing. A key that names neither an asset nor a group raises when strict is true, and warns otherwise.
  2. Build a Turnover from tn.w, the bound vector of step 1 and tn.fixed.

Arguments

  • tn: TurnoverEstimator specifying current weights, asset-specific turnover values, and default value.
  • sets: UniverseSets containing asset names or indices.
  • datatype: Data type for default turnover values when dval is nothing.
  • strict: If true, enforces strict matching between assets and turnover values (throws error on mismatch); if false, issues a warning.

Returns

  • tn::Turnover: Object containing portfolio weights and turnover values aligned with sets.

Examples

julia> sets = UniverseSets(; dict = Dict("nx" => ["A", "B", "C"]));julia> tn = TurnoverEstimator(; w = [0.2, 0.3, 0.5], val = Dict("A" => 0.1, "B" => 0.2));julia> turnover_constraints(tn, sets)Turnover      w ┼ Vector{Float64}: [0.2, 0.3, 0.5]    val ┼ Vector{Float64}: [0.1, 0.2, 0.0]  fixed ┴ Bool: false

Related

source
turnover_constraints(tn::Option{<:Turnover}, args...; kwargs...)

Propagate or pass through turnover portfolio constraints.

turnover_constraints returns the input Turnover object unchanged or nothing. This method is used to propagate already constructed turnover constraints, enabling composability and uniform interface handling in constraint generation workflows.

Algorithm

  1. Return tn. A Turnover already carries one bound per asset, so no universe is resolved. The method reads none of its other arguments and none of its keywords.

Arguments

  • tn: An existing Turnover object.
  • args...: Additional positional arguments (ignored).
  • kwargs...: Additional keyword arguments (ignored).

Returns

  • tn::Option{<:Turnover}: The input constraint object, unchanged.

Examples

julia> tn = Turnover(; w = [0.2, 0.3, 0.5], val = [0.1, 0.2, 0.0]);julia> turnover_constraints(tn)Turnover      w ┼ Vector{Float64}: [0.2, 0.3, 0.5]    val ┼ Vector{Float64}: [0.1, 0.2, 0.0]  fixed ┴ Bool: false

Related

source
turnover_constraints(tn::VecTnE_Tn, sets::UniverseSets; datatype::DataType = Float64,
                     strict::Bool = false)

Broadcasts turnover_constraints over the vector.

Provides a uniform interface for processing multiple constraint estimators simultaneously.

Algorithm

  1. For each entry tni of tn, in the order of tn, call turnover_constraints on tni with sets, datatype and strict. An entry that is already a Turnover passes through.
  2. Collect the results into a vector that preserves the order of tn.

Arguments

  • tn: Vector of turnover constraints or estimators.
  • sets: UniverseSets containing asset names or indices.
  • datatype: Data type for default turnover values when dval is nothing.
  • strict: If true, enforces strict matching between assets and turnover values (throws error on mismatch); if false, issues a warning.

Returns

  • res::VecTn: Vector of constructed turnover constraints.

Examples

julia> sets = UniverseSets(; dict = Dict("nx" => ["A", "B", "C"]));julia> tn1 = TurnoverEstimator(; w = [0.2, 0.3, 0.5], val = Dict("A" => 0.1, "B" => 0.2));julia> tn2 = TurnoverEstimator(; w = [0.1, 0.4, 0.5], val = Dict("B" => 0.15, "C" => 0.3));julia> turnover_constraints([tn1, tn2], sets)2-element Vector{Turnover{Vector{Float64}, Vector{Float64}, Bool}}: Turnover      w ┼ Vector{Float64}: [0.2, 0.3, 0.5]    val ┼ Vector{Float64}: [0.1, 0.2, 0.0]  fixed ┴ Bool: false Turnover      w ┼ Vector{Float64}: [0.1, 0.4, 0.5]    val ┼ Vector{Float64}: [0.0, 0.15, 0.3]  fixed ┴ Bool: false

Related

source
PortfolioOptimisers.port_opt_viewMethod
port_opt_view(tn::VecTnE_Tn, i, args...)

Create views of multiple turnover constraints or estimators for a subset of assets.

port_opt_view returns a vector of turnover constraint or estimator objects, each restricted to the indices or assets specified by i.

This is the generic vector port_opt_view plus concrete_typed_array_if_abstract: a mixed vector of Turnover and TurnoverEstimator keeps a concrete element type.

Algorithm

  1. For each entry tni of tn, in the order of tn, call port_opt_view on tni with i and args.... The @vprop tags of each type decide what is sliced: w and a vector val become views over i, and a scalar or dictionary val passes through unchanged.
  2. Pass the collected vector through concrete_typed_array_if_abstract, for the reason factory(tn::VecTnE_Tn, w::VecNum) gives.

Arguments

  • tn: Vector of turnover constraints or estimators.
  • i: Index or indices specifying the subset of assets.
  • args...: Further arguments, forwarded unchanged to each element's port_opt_view.

Returns

  • res::VecTnE_Tn: Vector of turnover constraint or estimator objects, each restricted to the specified subset.

Examples

julia> tn1 = Turnover(; w = [0.2, 0.3, 0.5], val = [0.1, 0.2, 0.0], fixed = true);julia> tn2 = TurnoverEstimator(; w = [0.2, 0.3, 0.5], val = Dict("A" => 0.1, "B" => 0.2),                               dval = 0.0, fixed = true);julia> PortfolioOptimisers.port_opt_view(concrete_typed_array([tn1, tn2]), 1:2)2-element Vector{Union{Turnover{SubArray{Float64, 1, Vector{Float64}, Tuple{UnitRange{Int64}}, true}, SubArray{Float64, 1, Vector{Float64}, Tuple{UnitRange{Int64}}, true}, Bool}, TurnoverEstimator{SubArray{Float64, 1, Vector{Float64}, Tuple{UnitRange{Int64}}, true}, Dict{String, Float64}, Float64, Bool}}}: 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.2]  fixed ┴ Bool: true TurnoverEstimator      w ┼ SubArray{Float64, 1, Vector{Float64}, Tuple{UnitRange{Int64}}, true}: [0.2, 0.3]    val ┼ Dict{String, Float64}: Dict("B" => 0.2, "A" => 0.1)   dval ┼ Float64: 0.0  fixed ┴ Bool: true

Related

source
PortfolioOptimisers.needs_previous_weightsMethod
needs_previous_weights(tn::TnE_Tn) -> Bool
needs_previous_weights(tn::VecTnE_Tn) -> Bool

Check if a turnover constraint or estimator requires previous portfolio weights.

A fixed entry pins its own reference weights, so it needs none: the scalar method answers !tn.fixed. The vector method answers any and not all, so one entry that is not fixed makes the whole vector need them.

Algorithm

  1. On a single TnE_Tn, return !tn.fixed.
  2. On a VecTnE_Tn, apply step 1 to every entry and reduce with any.

Arguments

  • tn: One turnover constraint or estimator, or a vector of them.

Returns

  • Bool: true if previous weights are needed, false otherwise.

Related

source

References

[5]
D. Cajas. Advanced Portfolio Optimization: A Cutting-edge Quantitative Approach (Springer Nature Switzerland, 2025).