Calculate the Nested Clustered Optimization (NCO) weights. The assets are clustered by their correlations, the objective is optimized within every cluster, and then once more across the clusters, each cluster acting as one asset that holds its own optimal portfolio. Estimation noise in the covariance matrix mostly sits among correlated assets, which is what makes mean-variance weights unstable; by optimizing within and across clusters separately, NCO keeps that noise contained and the weights steadier out of sample.

With W the matrix whose column k holds the optimal weights within cluster k, the clusters have the expected returns W’μ and the covariance matrix W’ΣW; the weights across the clusters v are optimal for those, and the weight of every asset is w = W v. The clusters are found as in HERC (correlation distance, Ward linkage, the number of clusters from the two-order difference of the gap statistic) and the covariance matrix is denoised by default, as in the paper.

For more information, see the following papers:

  • López de Prado, M. (2019). “A Robust Estimator of the Efficient Frontier.” SSRN 3469961.
  • López de Prado, M. (2020). “Machine Learning for Asset Managers.” Cambridge University Press, chapter 7.

Also known as: NCO, nested clustered optimisation.

Nested Clustered Optimization (NCO) in Python or via the MCP

Install the Finance Toolkit with:

pip install financetoolkit -U

Then call get_nested_clustered_optimization_weights as shown below.

from financetoolkit import Toolkit

toolkit = Toolkit(
    ["AAPL", "MSFT", "JNJ", "XOM"],
    api_key="FINANCIAL_MODELING_PREP_KEY",
    start_date="2021-01-01",
    end_date="2025-12-31",
)

toolkit.portfolio.get_nested_clustered_optimization_weights()[
    ["Weight", "Volatility", "Sharpe Ratio", "Risk Contribution", "Cluster"]
]

Or, after setting up the Finance Toolkit MCP, ask:

How would nested clustered optimization weight Apple, Microsoft, Johnson & Johnson and ExxonMobil, based on 2021 to 2025, and which clusters does it form?

Which returns:

  Weight Volatility Sharpe Ratio Risk Contribution Cluster
AAPL 0.1081 0.2786 0.5725 0.1106 1
MSFT 0.1388 0.2571 0.6534 0.1362 1
JNJ 0.575 0.1673 0.3884 0.575 3
XOM 0.1782 0.2702 0.9524 0.1782 2
Portfolio 1 0.1366 0.9061 1 nan

Parameters

get_nested_clustered_optimization_weights accepts the following parameters.

ParameterTypeDescriptionExample
objectivestrThe objective at both levels: “minimum_variance”, “maximum_sharpe” or “risk_parity” (equal risk contribution). Defaults to “minimum_variance”."minimum_variance"
linkage_methodstrThe linkage of the hierarchical clustering: “single”, “complete”, “average” or “ward”. Defaults to “ward”."ward"
clustersint | NoneThe number of clusters. Defaults to None, chosen by the two-order difference of the gap statistic.None, chosen by the two-order difference of the gap statistic
codependencestrThe correlation the assets are clustered on, through the distance √((1 - ρ) / 2): “pearson” (the linear correlation of the covariance matrix), “spearman” (the rank correlation of the returns, Spearman, 1904) or “kendall” (the rank correlation τ_b of the returns, Kendall, 1938). The rank correlations depend only on the order of the returns, so a few extreme returns weigh less. Defaults to “pearson”."pearson"
periodstrThe frequency of the returns (daily, weekly, monthly, quarterly or yearly). Defaults to “daily”."weekly"
covariancestrThe estimator of the covariance matrix, which also gives the correlations that are clustered: “denoised” (the eigenvalues of the correlation matrix that random returns could produce averaged, Laloux et al., 2000), “sample” (ddof=1), “ledoit_wolf” (towards the scaled identity, Ledoit and Wolf, 2004), “ledoit_wolf_constant_correlation” (towards the average correlation, Ledoit and Wolf, 2004), “ledoit_wolf_single_factor” (towards the covariance that the benchmark as a single factor implies, Ledoit and Wolf, 2003), “oas” (Chen et al., 2010), “semicovariance” (the co-movement below a zero return, Estrada, 2007) or “exponential” (recent periods weigh most, RiskMetrics). Defaults to “denoised”, as in the paper."denoised", as in the paper
covariance_spanintThe span in periods of the “exponential” covariance; the weight of a period halves after about span / 2.9 periods. Defaults to 180.180
expected_returnsstrThe estimator of the expected returns of the “maximum_sharpe” objective: “mean” (the historical average), “exponential” (the exponentially weighted average, recent periods weigh most), “capm” (the returns the Capital Asset Pricing Model implies from each asset’s beta to the benchmark, Sharpe, 1964) or “james_stein” (the averages shrunk towards that of the minimum variance portfolio, Jorion, 1986). Defaults to “mean”."mean"
expected_returns_spanintThe span in periods of the “exponential” expected returns. Defaults to 180.180
include_benchmarkboolWhether to include the benchmark as an asset. Defaults to False.True
roundingint | NoneThe number of decimals to round the results to. Defaults to None, which uses the Toolkit’s rounding.2
Share