Skip to content
13

Hierarchical Equal Risk Contribution

PortfolioOptimisers.HierarchicalEqualRiskContribution Type
julia
struct HierarchicalEqualRiskContribution{__T_opt, __T_ri, __T_ro, __T_scai, __T_scao, __T_ex, __T_fb} <: ClusteringOptimisationEstimator

Hierarchical Equal Risk Contribution (HERC) portfolio optimiser.

HierarchicalEqualRiskContribution implements the Hierarchical Equal Risk Contribution algorithm. It clusters assets, then allocates weights so that each cluster contributes equally to total portfolio risk (using ro), and within each cluster, assets are weighted by inverse intra-cluster risk (using ri).

Fields

  • opt: Base hierarchical optimiser configuration.

  • ri: Inner risk measure.

  • ro: Outer risk measure.

  • scai: Inner scalariser.

  • scao: Outer scalariser.

  • ex: Parallel execution strategy.

  • fb: Fallback result or estimator.

Constructors

julia
HierarchicalEqualRiskContribution(;
    opt::HierarchicalOptimiser = HierarchicalOptimiser(),
    ri::OptRM_VecOptRM = Variance(),
    ro::OptRM_VecOptRM = ri,
    scai::Scalariser = SumScalariser(),
    scao::Scalariser = scai,
    ex::FLoops.Transducers.Executor = FLoops.ThreadedEx(),
    fb::Option{<:OptE_Opt} = nothing
) -> HierarchicalEqualRiskContribution

Keywords correspond to the struct's fields.

Validation

  • If ri or ro is a vector: !isempty(ri) / !isempty(ro).

Examples

julia
julia> HierarchicalEqualRiskContribution()
HierarchicalEqualRiskContribution
   opt ┼ HierarchicalOptimiser
       │       pe ┼ EmpiricalPrior
       │          │        ce ┼ PortfolioOptimisersCovariance
       │          │           │   ce ┼ Covariance
       │          │           │      │    me ┼ SimpleExpectedReturns
       │          │           │      │       │   w ┴ nothing
       │          │           │      │    ce ┼ GeneralCovariance
       │          │           │      │       │   ce ┼ StatsBase.SimpleCovariance: StatsBase.SimpleCovariance(true)
       │          │           │      │       │    w ┴ nothing
       │          │           │      │   alg ┴ Full()
       │          │           │   mp ┼ MatrixProcessing
       │          │           │      │     pdm ┼ Posdef
       │          │           │      │         │      alg ┼ UnionAll: NearestCorrelationMatrix.Newton
       │          │           │      │         │   kwargs ┴ @NamedTuple{}: NamedTuple()
       │          │           │      │      dn ┼ nothing
       │          │           │      │      dt ┼ nothing
       │          │           │      │     alg ┼ nothing
       │          │           │      │   order ┴ NTuple{4, Symbol}: (:pdm, :dn, :dt, :alg)
       │          │        me ┼ SimpleExpectedReturns
       │          │           │   w ┴ nothing
       │          │   horizon ┴ nothing
       │      cle ┼ ClustersEstimator
       │          │    ce ┼ PortfolioOptimisersCovariance
       │          │       │   ce ┼ Covariance
       │          │       │      │    me ┼ SimpleExpectedReturns
       │          │       │      │       │   w ┴ nothing
       │          │       │      │    ce ┼ GeneralCovariance
       │          │       │      │       │   ce ┼ StatsBase.SimpleCovariance: StatsBase.SimpleCovariance(true)
       │          │       │      │       │    w ┴ nothing
       │          │       │      │   alg ┴ Full()
       │          │       │   mp ┼ MatrixProcessing
       │          │       │      │     pdm ┼ Posdef
       │          │       │      │         │      alg ┼ UnionAll: NearestCorrelationMatrix.Newton
       │          │       │      │         │   kwargs ┴ @NamedTuple{}: NamedTuple()
       │          │       │      │      dn ┼ nothing
       │          │       │      │      dt ┼ nothing
       │          │       │      │     alg ┼ nothing
       │          │       │      │   order ┴ NTuple{4, Symbol}: (:pdm, :dn, :dt, :alg)
       │          │    de ┼ Distance
       │          │       │   power ┼ nothing
       │          │       │     alg ┴ CanonicalDistance()
       │          │   alg ┼ HClustAlgorithm
       │          │       │   linkage ┴ Symbol: :ward
       │          │   onc ┼ OptimalNumberClusters
       │          │       │   max_k ┼ nothing
       │          │       │     alg ┼ SecondOrderDifference
       │          │       │         │   alg ┼ StandardisedValue
       │          │       │         │       │   mv ┼ MeanValue
       │          │       │         │       │      │   w ┴ nothing
       │          │       │         │       │   sv ┼ StdValue
       │          │       │         │       │      │           w ┼ nothing
       │          │       │         │       │      │   corrected ┴ Bool: true
       │      slv ┼ nothing
       │       wb ┼ WeightBounds
       │          │   lb ┼ Float64: 0.0
       │          │   ub ┴ Float64: 1.0
       │     fees ┼ nothing
       │     sets ┼ nothing
       │       wf ┼ IterativeWeightFinaliser
       │          │   iter ┴ Int64: 100
       │      brt ┼ Bool: false
       │   cle_pr ┼ Bool: true
       │   strict ┴ Bool: false
    ri ┼ Variance
       │   settings ┼ RiskMeasureSettings
       │            │   scale ┼ Float64: 1.0
       │            │      ub ┼ nothing
       │            │     rke ┴ Bool: true
       │      sigma ┼ nothing
       │       chol ┼ nothing
       │         rc ┼ nothing
       │        alg ┴ SquaredSOCRiskExpr()
    ro ┼ Variance
       │   settings ┼ RiskMeasureSettings
       │            │   scale ┼ Float64: 1.0
       │            │      ub ┼ nothing
       │            │     rke ┴ Bool: true
       │      sigma ┼ nothing
       │       chol ┼ nothing
       │         rc ┼ nothing
       │        alg ┴ SquaredSOCRiskExpr()
  scai ┼ SumScalariser()
  scao ┼ SumScalariser()
    ex ┼ Transducers.ThreadedEx{@NamedTuple{}}: Transducers.ThreadedEx()
    fb ┴ nothing

Mathematical definition

Let K be the number of clusters. The inter-cluster (outer) step assigns equal risk contribution across all clusters using risk measure ρo:

wCk=ρ~o(Ck)1j=1Kρ~o(Cj)1.

Within each cluster Ck, the intra-cluster (inner) step assigns weights proportional to inverse intra-cluster risk ρi:

wiρ~i({i})1,iCk,iCkwi=wCk.

Where:

  • wCk: Weight allocated to cluster Ck.

  • ρ~o(Ck): Outer (inter-cluster) risk of cluster Ck.

  • ρ~i({i}): Inner (intra-cluster) risk of asset i.

  • K: Number of clusters.

  • wi: Final weight of asset i.

  • ρ~: Quasi-diagonal cluster portfolio risk.

Related

source
PortfolioOptimisers.needs_previous_weights Method
julia
needs_previous_weights(
    opt::HierarchicalEqualRiskContribution
) -> Any

Return whether the HierarchicalEqualRiskContribution requires previous portfolio weights.

Returns true if any of the base optimiser, inner/outer risk measures, or fallback require previous weights.

Related

source
PortfolioOptimisers.factory Method
julia
factory(
    hec::HierarchicalEqualRiskContribution,
    w::AbstractVector
) -> HierarchicalEqualRiskContribution{HierarchicalOptimiser{__T_pe, __T_cle, __T_slv, __T_wb, __T_fees, __T_sets, __T_wf, __T_brt, __T_cle_pr, __T_strict}, _A, _B, <:Scalariser, <:Scalariser, <:Transducers.Executor} where {__T_pe, __T_cle, __T_slv, __T_wb, __T_fees, __T_sets, __T_wf, __T_brt, __T_cle_pr, __T_strict, _A, _B}

Create a HierarchicalEqualRiskContribution updating the base optimiser, risk measures, and fallback with weights w.

Related

source
PortfolioOptimisers.port_opt_view Method
julia
port_opt_view(
    hec::HierarchicalEqualRiskContribution,
    i,
    X::AbstractMatrix{<:Union{var"#s20", var"#s19"} where {var"#s20"<:Number, var"#s19"<:AbstractJuMPScalar}},
    args...
) -> HierarchicalEqualRiskContribution{HierarchicalOptimiser{__T_pe, __T_cle, __T_slv, __T_wb, __T_fees, __T_sets, __T_wf, __T_brt, __T_cle_pr, __T_strict}, _A, _B, <:Scalariser, <:Scalariser, <:Transducers.Executor} where {__T_pe, __T_cle, __T_slv, __T_wb, __T_fees, __T_sets, __T_wf, __T_brt, __T_cle_pr, __T_strict, _A, _B}

Return a view of HierarchicalEqualRiskContribution hec sliced to asset indices i.

Related

source
PortfolioOptimisers.herc_scalarised_risk_o! Function
julia
herc_scalarised_risk_o!(scalariser, wk, roku, rkbo, cl, ros, X, fees)

Compute and accumulate the scalarised outer (inter-cluster) HERC risk in-place.

Updates rkbo with inverse-risk weights for cluster cl and accumulates the scaled risk contribution using the given scalariser strategy.

Arguments

  • scalariser: Scalarisation strategy (SumScalariser, MaxScalariser, MinScalariser, or LogSumExpScalariser).

  • wk: Cluster weight vector.

  • roku: Unitary outer risk vector or matrix.

  • rkbo: Outer risk buffer vector (modified in-place).

  • cl: Cluster asset indices.

  • ros: Vector of outer risk measures.

  • X: Return matrix.

  • fees: Optional fees.

Returns

  • Scalarised outer cluster risk scalar.

Related

source
PortfolioOptimisers.herc_scalarised_risk_i! Function
julia
herc_scalarised_risk_i!(scalariser, wk, riku, cl, ris, X, fees)

Compute the scalarised inner (intra-cluster) HERC risk for cluster cl.

Aggregates inner risk measures across the assets in cluster cl using the given scalariser, returning the per-asset risk vector used for intra-cluster weight allocation.

Arguments

Returns

  • Per-asset inner risk vector for assets in cl.

Related

source
PortfolioOptimisers.herc_risk Function
julia
herc_risk(hec, pr, cls)

Compute per-cluster risk contributions for HERC weight allocation.

Evaluates the inner and outer risk measures for all clusters in cls, returning the risk arrays needed to allocate intra- and inter-cluster weights.

Arguments

  • hec: HierarchicalEqualRiskContribution optimiser instance.

  • pr: Prior result containing asset moments and return data.

  • cls: Vector of vectors of asset indices per cluster.

Returns

  • (riku, roku): Inner and outer per-asset risk arrays.

Related

source
PortfolioOptimisers.optimise Function
julia
optimise(hec::HierarchicalEqualRiskContribution{
                 <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, Nothing
             },
        rd::ReturnsResult = ReturnsResult(); dims::Int = 1,
        branchorder::Symbol = :optimal, kwargs...) -> HierarchicalResult

Run the Hierarchical Equal Risk Contribution portfolio optimisation.

Arguments

  • hec: The hierarchical equal risk contribution optimiser to use.

  • rd: The returns result to use. If isa(hec.opt.pe, AbstractPriorResult), rd is not necessary if doing a standalone optimisation, but may be required/desired by fallbacks and/or clusterisation.

  • dims: The dimension along which observations advance in time.

  • branchorder: The branch order to use for the clusterisation, this optimisation can use non-optimal branch orders, which make the clustering faster but the dendrogram won't be as nice.

  • kwargs: Additional keyword arguments passed to the optimisation function.

Related

source