Skip to content
13

Hierarchical Risk Parity

PortfolioOptimisers.HierarchicalRiskParity Type
julia
struct HierarchicalRiskParity{__T_opt, __T_r, __T_sca, __T_fb} <: ClusteringOptimisationEstimator

Hierarchical Risk Parity (HRP) portfolio optimiser.

HierarchicalRiskParity implements the Hierarchical Risk Parity algorithm of López de Prado (2016). It clusters assets using hierarchical clustering, then allocates weights by recursively bisecting the dendrogram and applying inverse-risk weighting within each cluster.

Fields

  • opt: Base hierarchical optimiser configuration.

  • r: Risk measure or vector of risk measures.

  • sca: Scalariser for combining multiple risk measures.

  • fb: Fallback result or estimator.

Constructors

julia
HierarchicalRiskParity(;
    opt::HierarchicalOptimiser = HierarchicalOptimiser(),
    r::OptRM_VecOptRM = Variance(),
    sca::Scalariser = SumScalariser(),
    fb::Option{<:OptE_Opt} = nothing
) -> HierarchicalRiskParity

Keywords correspond to the struct's fields.

Validation

  • If r is a vector: !isempty(r).

Propagated parameters

When factory is called on this type, the following @fprop-tagged fields are automatically propagated:

  • opt: Recursively updated via factory.

  • r: Recursively updated via factory.

  • fb: Recursively updated via factory.

Examples

julia
julia> HierarchicalRiskParity()
HierarchicalRiskParity
  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
    r ┼ Variance
      │   settings ┼ RiskMeasureSettings
      │            │   scale ┼ Float64: 1.0
      │            │      ub ┼ nothing
      │            │     rke ┴ Bool: true
      │      sigma ┼ nothing
      │       chol ┼ nothing
      │         rc ┼ nothing
      │        alg ┴ SquaredSOCRiskExpr()
  sca ┼ SumScalariser()
   fb ┴ nothing

Mathematical definition

At each bisection step, the algorithm splits cluster C into sub-clusters C1 and C2 and allocates weights proportional to inverse portfolio risk:

α=ρ~(C2)ρ~(C1)+ρ~(C2),wC1αwC,wC2(1α)wC.

Where:

  • α: Bisection weight allocating fraction of cluster weight wC to sub-cluster C1.

  • ρ~(C): Risk of the quasi-diagonal sub-portfolio restricted to cluster C.

  • C1, C2: Sub-clusters of the bisected cluster C.

  • wC: Weight vector assigned to cluster C before bisection.

Related

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

Return whether the HierarchicalRiskParity requires previous portfolio weights.

Returns true if any of the base optimiser, risk measure, or fallback estimator require previous weights.

Related

source
PortfolioOptimisers.port_opt_view Method
julia
port_opt_view(
    hrp::HierarchicalRiskParity,
    i,
    X::AbstractMatrix{<:Union{var"#s20", var"#s19"} where {var"#s20"<:Number, var"#s19"<:AbstractJuMPScalar}},
    args...
) -> HierarchicalRiskParity

Return a view of HierarchicalRiskParity hrp sliced to asset indices i.

Related

source
PortfolioOptimisers.split_factor_weight_constraints Method
julia
split_factor_weight_constraints(alpha, wb, w, ...)

Split and scale factor weight constraints for hierarchical risk parity.

Distributes the weight constraints across clusters based on the hierarchical factor alpha and the current weight allocation w.

Arguments

  • alpha: Hierarchical scaling factor.

  • wb: Weight bounds.

  • w: Current portfolio weights.

  • Additional parameters.

Returns

  • Tuple of updated weight bounds for each cluster.

Related

source
PortfolioOptimisers.hrp_scalarised_risk Method
julia
hrp_scalarised_risk(scalariser, wu, wk, rku, lc, rc, rs, X, fees)

Compute the scalarised HRP left/right cluster risk for weight allocation.

Aggregates risk measures across clusters using a scalariser (sum, max, min, or log-sum-exp), returning the left and right cluster risks used to allocate weights in HRP.

Arguments

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

  • wu: Unitary weight matrix (pre-allocated buffer).

  • wk: Cluster weight vector.

  • rku: Unitary risk vector.

  • lc: Left cluster asset indices.

  • rc: Right cluster asset indices.

  • rs: Vector of risk measures.

  • X: Return matrix.

  • fees: Optional fees.

Returns

  • (lrisk, rrisk): Left and right cluster risk scalars.

Related

source
PortfolioOptimisers.optimise Function
julia
optimise(hrp::HierarchicalRiskParity{<:Any, <:Any, <:Any, <:Nothing},
         rd::ReturnsResult = ReturnsResult(); dims::Int = 1, kwargs...) -> HierarchicalResult

Run the Hierarchical Risk Parity portfolio optimisation.

Arguments

  • hrp: The hierarchical risk parity optimiser to use.

  • rd: The returns result to use. If isa(hrp.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.

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

Related

source