From 6e614f297acd84e98b7006f4f4c6b9ca07a28949 Mon Sep 17 00:00:00 2001 From: PiPi_Mac Date: Thu, 22 Oct 2026 03:07:57 -0400 Subject: [PATCH] fix: validate clustering methods through public scipy linkage API --- pypfopt/hierarchical_portfolio.py | 4 +--- tests/test_hrp.py | 11 +++++++++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/pypfopt/hierarchical_portfolio.py b/pypfopt/hierarchical_portfolio.py index 6dc76d07..ae1eb616 100644 --- a/pypfopt/hierarchical_portfolio.py +++ b/pypfopt/hierarchical_portfolio.py @@ -176,9 +176,6 @@ def optimize(self, linkage_method="single"): OrderedDict weights for the HRP portfolio """ - if linkage_method not in sch._LINKAGE_METHODS: - raise ValueError("linkage_method must be one recognised by scipy") - if self.returns is None: cov = self.cov_matrix corr = cov_to_corr(self.cov_matrix).round(6) @@ -192,6 +189,7 @@ def optimize(self, linkage_method="single"): matrix = np.sqrt(np.clip((1.0 - corr) / 2.0, a_min=0.0, a_max=1.0)) dist = ssd.squareform(matrix, checks=False) + # Let the public API validate methods; SciPy's private registry can change. self.clusters = sch.linkage(dist, linkage_method) sort_ix = HRPOpt._get_quasi_diag(self.clusters) ordered_tickers = corr.index[sort_ix].tolist() diff --git a/tests/test_hrp.py b/tests/test_hrp.py index 369a1727..7c933bf1 100644 --- a/tests/test_hrp.py +++ b/tests/test_hrp.py @@ -6,6 +6,17 @@ from tests.utilities_for_tests import get_data, resource +@pytest.mark.parametrize( + "method", ["single", "complete", "average", "weighted", "centroid", "median", "ward"] +) +def test_documented_linkage_methods(method): + cov = pd.DataFrame(np.eye(3), index=list("ABC"), columns=list("ABC")) + weights = HRPOpt(cov_matrix=cov).optimize(linkage_method=method) + assert set(weights) == set("ABC") + assert sum(weights.values()) == pytest.approx(1) + assert all(weight > 0 for weight in weights.values()) + + def test_hrp_errors(): with pytest.raises(ValueError): hrp = HRPOpt()