Coverage for src/pyhrp/hrp.py: 100%

20 statements  

« prev     ^ index     » next       coverage.py v7.14.3, created at 2026-09-15 05:20 +0000

1"""Hierarchical Risk Parity (HRP) allocation entry points. 

2 

3This module exposes the top-level allocation functions and re-exports the 

4supporting building blocks so the public ``pyhrp.hrp`` API is unchanged: 

5- hrp: Compute HRP portfolio weights from prices 

6- schur_hrp: Compute Schur Complementary Allocation weights from prices 

7- build_tree: Build a hierarchical cluster tree (see :mod:`pyhrp.dendrogram`) 

8- compute_returns: Simple returns from prices (see :mod:`pyhrp.covariance`) 

9- compute_cov / compute_corr: Second-moment estimators (see :mod:`pyhrp.covariance`) 

10- Dendrogram: Clustering result container (see :mod:`pyhrp.dendrogram`) 

11""" 

12 

13from __future__ import annotations 

14 

15from typing import Literal 

16 

17import polars as pl 

18 

19from .algos import risk_parity, schur_risk_parity 

20from .cluster import Cluster 

21from .covariance import check_finite_matrix, compute_corr, compute_cov, compute_returns 

22from .dendrogram import Dendrogram, build_tree 

23 

24__all__ = [ 

25 "Dendrogram", 

26 "build_tree", 

27 "check_finite_matrix", 

28 "compute_corr", 

29 "compute_cov", 

30 "compute_returns", 

31 "hrp", 

32 "schur_hrp", 

33] 

34 

35 

36def hrp( 

37 prices: pl.DataFrame, 

38 node: Cluster | None = None, 

39 method: Literal["single", "complete", "average", "ward"] = "ward", 

40 bisection: bool = False, 

41) -> Cluster: 

42 """Compute the hierarchical risk parity portfolio weights. 

43 

44 This is the main entry point for the HRP algorithm. It calculates returns from prices, 

45 builds a hierarchical clustering tree if not provided, and applies risk parity weights. 

46 

47 Note: 

48 A ``node`` passed in is weighted in place and returned; see the allocator 

49 contract in :mod:`pyhrp.algos`. Passing ``dendrogram.root`` therefore 

50 rewrites the portfolios inside that ``Dendrogram``, even though the 

51 ``Dendrogram`` itself is a frozen dataclass. 

52 

53 Args: 

54 prices (pl.DataFrame): Asset price time series (columns are assets, rows are dates) 

55 node (Cluster, optional): Root node of the hierarchical clustering tree. 

56 If None, a tree will be built from the correlation matrix. 

57 method (Literal["single", "complete", "average", "ward"]): Linkage method to use for distance calculation 

58 - "single": minimum distance between points (nearest neighbor) 

59 - "complete": maximum distance between points (furthest neighbor) 

60 - "average": average distance between all points 

61 - "ward": Ward variance minimization 

62 bisection (bool): Whether to use bisection method for tree construction 

63 

64 Returns: 

65 Cluster: The root cluster with portfolio weights assigned according to HRP 

66 """ 

67 returns = compute_returns(prices) 

68 cov = check_finite_matrix(compute_cov(returns), name="covariance matrix") 

69 cor = compute_corr(returns) 

70 node = node or build_tree(cor, method=method, bisection=bisection).root 

71 

72 return risk_parity(root=node, cov=cov) 

73 

74 

75def schur_hrp( 

76 prices: pl.DataFrame, 

77 node: Cluster | None = None, 

78 method: Literal["single", "complete", "average", "ward"] = "ward", 

79 bisection: bool = False, 

80 gamma: float = 0.5, 

81) -> Cluster: 

82 """Compute Schur Complementary Allocation portfolio weights. 

83 

84 Extends HRP by augmenting each sub-covariance block with off-diagonal information 

85 via Schur complements before splitting risk between clusters. Introduced by Peter Cotton 

86 (arXiv:2411.05807). At gamma=0 this is identical to HRP; at gamma=1 it recovers the 

87 global minimum-variance portfolio through the same recursive hierarchy. 

88 

89 Note: 

90 A ``node`` passed in is weighted in place and returned; see the allocator 

91 contract in :mod:`pyhrp.algos`. Passing ``dendrogram.root`` therefore 

92 rewrites the portfolios inside that ``Dendrogram``, even though the 

93 ``Dendrogram`` itself is a frozen dataclass. 

94 

95 Args: 

96 prices (pl.DataFrame): Asset price time series (columns are assets, rows are dates) 

97 node (Cluster, optional): Root node of the hierarchical clustering tree. 

98 If None, a tree will be built from the correlation matrix. 

99 method (Literal["single", "complete", "average", "ward"]): Linkage method for clustering 

100 bisection (bool): Whether to use bisection method for tree construction 

101 gamma (float): Schur interpolation parameter in [0, 1]. 

102 0 recovers standard HRP; 1 recovers minimum-variance portfolio. 

103 

104 Returns: 

105 Cluster: The root cluster with portfolio weights assigned 

106 """ 

107 returns = compute_returns(prices) 

108 cov = check_finite_matrix(compute_cov(returns), name="covariance matrix") 

109 cor = compute_corr(returns) 

110 node = node or build_tree(cor, method=method, bisection=bisection).root 

111 

112 return schur_risk_parity(root=node, cov=cov, gamma=gamma)