Threshold Constraints

PortfolioOptimisers.ThresholdEstimatorType
struct ThresholdEstimator{__T_val, __T_key, __T_dval} <: AbstractConstraintEstimator

Resolves a minimum-holding threshold written in asset or group names against a universe.

threshold_constraints turns it into a Threshold: every name is mapped to its indices in the universe key selects, and an unnamed asset takes dval, which defaults to no threshold. A threshold may also be a scalar, a vector, or an algorithmic rule such as UniformValues.

The universe key is a field here, and estimator_to_val resolves a nothing key to sets.xkey. RiskBudgetEstimator is the same shape with the key taken positionally by its verb instead. A dval of nothing is passed straight through and becomes zero(datatype), so an unnamed asset carries no threshold — where a risk budget's nothing default becomes the uniform share of its axis.

Fields

  • val: Asset-specific minimum-holding threshold value(s).
  • key: Key to specify the universe in sets.dict that names resolve against. If nothing, the key is taken from sets.xkey — or, where the caller is written against another declared axis, from that axis' key.
  • dval: Default value for assets not specified in val.

Constructors

ThresholdEstimator(;    val::EstValType{<:VectorAbstractEstimatorValueAlgorithm},    key::Option{<:AbstractString} = nothing,    dval::Option{<:Number} = nothing) -> ThresholdEstimator

Keywords correspond to the struct's fields.

Validation

  • val and dval are both validated with assert_nonempty_nonneg_finite_val, so a threshold is non-empty, non-negative and finite.
  • If key is not nothing, it is a non-empty string.

View parameters

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

Only a vector val is sliced. A val that is a scalar, a Dict, a Pair or an algorithmic rule is not indexed by asset, so a view passes it through untouched and it resolves against the viewed universe when the estimator runs.

Examples

julia> ThresholdEstimator(; val = Dict("A" => 0.05, "B" => 0.1))ThresholdEstimator   val ┼ Dict{String, Float64}: Dict("B" => 0.1, "A" => 0.05)   key ┼ nothing  dval ┴ nothingjulia> ThresholdEstimator(; val = "A" => 0.05)ThresholdEstimator   val ┼ Pair{String, Float64}: "A" => 0.05   key ┼ nothing  dval ┴ nothingjulia> ThresholdEstimator(; val = 0.05)ThresholdEstimator   val ┼ Float64: 0.05   key ┼ nothing  dval ┴ nothingjulia> ThresholdEstimator(; val = [0.05])ThresholdEstimator   val ┼ Vector{Float64}: [0.05]   key ┼ nothing  dval ┴ nothingjulia> ThresholdEstimator(; val = UniformValues())ThresholdEstimator   val ┼ UniformValues()   key ┼ nothing  dval ┴ nothing

Related

References

  • [5] D. Cajas. Advanced Portfolio Optimization: A Cutting-edge Quantitative Approach (Springer Nature Switzerland, 2025). Section 9.4.
source
PortfolioOptimisers.ThresholdType
struct Threshold{__T_val} <: AbstractConstraintResult

Forces every held position to reach a minimum size, and drives anything smaller to zero.

The threshold is a scalar shared by every asset or a vector of one value per asset. It exists to keep a mixed-integer model from answering with a long tail of positions too small to trade. The source writes the same constraint over positive and negative trades against a reference portfolio; this library writes it over the position, so no reference portfolio enters it.

There is no upper bound on a threshold, and a threshold above the largest weight the budget admits makes the asset unholdable. On a three-asset long-only variance model with a cardinality cap of three, Threshold(0.15) held all three and Threshold(0.5) held two at exactly 0.5 each, while Threshold(1.5) returned an OptimisationFailure whose solver status is INFEASIBLE — the budget row forces the weights to sum to one, and no held weight can reach 1.5. Use a threshold above one to state that an asset must not be held only when the model has some other way to satisfy its budget.

Mathematical definition

The threshold is the lower half of a buy-in constraint, stated against the held binary:

\[\begin{align} \underset{\boldsymbol{w}}{\mathrm{opt}}\quad & \phi(\boldsymbol{w})\\ \textrm{s.t.}\quad & \ell_i z_i \leq w_i \leq u_i z_i\,,\quad \forall i = 1,\ldots,N\,,\\ & \boldsymbol{z} \in \{0, 1\}^{N}\,,\quad \boldsymbol{w} \in \mathcal{W}\,. \end{align}\]

Where:

  • $\boldsymbol{w}$: Portfolio weights vector $N \times 1$.
  • $\boldsymbol{z}$: Held binary, one entry per asset.
  • $\ell_i$: Minimum-holding threshold for asset $i$, the val field.
  • $u_i$: Upper weight bound for asset $i$, from WeightBounds.
  • $N$: Number of assets.
  • $\phi$: Objective function of the optimiser.
  • $\mathcal{W}$: Rest of the feasible set.

The binary carries both halves. Where $z_i = 0$ the two bounds collapse to $w_i = 0$; where $z_i = 1$ the position must reach $\ell_i$. A long and a short threshold are separate objects, each bound to its own side's binary.

A threshold above the corresponding $u_i$ admits only $z_i = 0$, because $\ell_i z_i \leq w_i \leq u_i z_i$ is infeasible for $z_i = 1$. So the pair of bounds carries the exclusion as well as the minimum, and no separate row states it.

Fields

  • val: Minimum-holding threshold(s) on the portfolio weights. A held position must reach its threshold; a position below it is driven to zero. The threshold binds the held weight, never the trade, so a reference portfolio does not enter it.

Constructors

Threshold(    val::Num_VecNum) -> ThresholdThreshold(;    val::Num_VecNum) -> Threshold

Keywords correspond to the struct's fields.

Validation

  • val is validated with assert_nonempty_nonneg_finite_val, so a threshold is non-empty, non-negative and finite. No upper bound is checked, and a value above one is admitted.

Both constructors run the same check, because the positional form is the inner one and the keyword form forwards to it.

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> Threshold(0.05)Threshold  val ┴ Float64: 0.05julia> Threshold([0.05, 0.1, 0.0])Threshold  val ┴ Vector{Float64}: [0.05, 0.1, 0.0]

Related

References

  • [5] D. Cajas. Advanced Portfolio Optimization: A Cutting-edge Quantitative Approach (Springer Nature Switzerland, 2025). Section 9.4.
source
PortfolioOptimisers.threshold_constraintsFunction
threshold_constraints(t::Option{<:Threshold}, args...; kwargs...)

Propagate or pass through buy-in threshold portfolio constraints.

threshold_constraints returns the input Threshold object or nothing unchanged. This method is used to propagate already constructed buy-in threshold constraints, enabling composability and uniform interface handling in constraint generation workflows.

It builds nothing and allocates nothing: the returned object is the same object, so threshold_constraints(t) === t holds. Accepting nothing is what lets VecOptBtE_Bt's broadcast carry an empty block through untouched.

Algorithm

  1. Return t. The method reads none of its other arguments and none of its keywords.

Arguments

  • t: An existing Threshold object or nothing.
  • args...: Additional positional arguments (ignored).
  • kwargs...: Additional keyword arguments (ignored).

Returns

  • bt::Option{<:Threshold}: The input constraint object, unchanged.

Examples

julia> threshold_constraints(Threshold(0.05))Threshold  val ┴ Float64: 0.05julia> threshold_constraints(nothing)

Related

source
threshold_constraints(t::ThresholdEstimator, sets::UniverseSets;
                      datatype::DataType = Float64, strict::Bool = false)

Generate buy-in threshold portfolio constraints from a ThresholdEstimator and asset set.

threshold_constraints constructs a Threshold object representing minimum allocation thresholds for the assets in sets, using the specifications in t. Supports scalar, vector, dictionary, pair, or custom threshold types for flexible assignment and validation.

It does not normalise the resolved vector, unlike risk_budget_constraints's estimator branch. A threshold is a bound on one weight and not a share of a total, so scaling the vector changes the model.

Algorithm

  1. Resolve t.val against sets with estimator_to_val, against the universe t.key selects, or sets.xkey's universe when t.key is nothing. The fill value for an asset that t.val does not name is t.dval, or zero(datatype) when t.dval is nothing, which is no threshold.
  2. Wrap the resolved vector in a Threshold, which checks that it is non-empty, non-negative and finite.

Arguments

  • t: ThresholdEstimator specifying asset-specific threshold values.
  • sets: UniverseSets containing asset names or indices.
  • datatype: Output data type for thresholds.
  • strict: If true, a name in t.val that sets does not resolve throws; if false, it issues a warning and is skipped.

Validation

  • strict governs an unresolvable name, not an unnamed asset. A name in t.val that names neither an asset nor a group raises ArgumentError when strict is true, and issues a warning otherwise. An unnamed asset always takes the default of step 1 and never throws.
  • The resolved vector passes assert_nonempty_nonneg_finite_val through the Threshold constructor of step 2.

Returns

  • bt::Threshold: Object containing threshold values aligned with sets.

Examples

julia> sets = UniverseSets(; dict = Dict("nx" => ["A", "B", "C"]));julia> t = ThresholdEstimator(Dict("A" => 0.05, "B" => 0.1));julia> threshold_constraints(t, sets)Threshold  val ┴ Vector{Float64}: [0.05, 0.1, 0.0]

Related

source
threshold_constraints(t::VecOptBtE_Bt, sets::UniverseSets;
                      kwargs...)

Broadcasts threshold_constraints over the vector.

Provides a uniform interface for processing multiple constraint estimators simultaneously. Each entry is resolved by the method its own type selects, so an estimator resolves, a Threshold passes through and a nothing stays nothing. The result is a VecOptBt of the same length and the same order, which is what a scenario-block target needs: entry i belongs to block i, so an empty block must survive as nothing rather than be dropped.

This is the difference from risk_budget_constraints, which has no vector method. The threshold targets :slt, :sst, :sglt and :sgst are in the set of routing targets that accumulate and hold a positional list, and a risk budget's target :rkb is not. RkbE_Rkb states the same decision from the other side.

Algorithm

  1. For each entry ti of t, in order, call threshold_constraints(ti, sets; kwargs...).
  2. Collect the results into a vector of the same length.

Arguments

Returns

  • bt::VecOptBt: One resolved Threshold or nothing per entry of t, in the order of t.

Examples

julia> sets = UniverseSets(; dict = Dict("nx" => ["A", "B", "C"]));julia> t = Union{Nothing, PortfolioOptimisers.BtE_Bt}[nothing, Threshold(0.05),                                                      ThresholdEstimator(; val = Dict("A" => 0.1))];julia> bt = threshold_constraints(t, sets);julia> length(bt), isnothing(bt[1]), bt[2].val, bt[3].val(3, true, 0.05, [0.1, 0.0, 0.0])

Related

source

References

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