analyze_diversification
Analyze portfolio concentration using the Herfindahl-Hirschman Index to assess diversification across asset classes, regions, and sectors, providing concentration grades and risk warnings.
Instructions
Portfolio diversification analysis using Herfindahl-Hirschman Index (HHI). Returns concentration grade (excellent/good/moderate/poor/critical), effective number of assets, breakdown by asset class / region / sector, and concentration warnings. Payment: $0.01 USDC on Tempo chain.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| holdings | Yes | ||
| profile | No | Risk profile — affects rebalance targets and scoring. Default: balanced. | |
| benchmarkReturn | No | Annual benchmark return for Sharpe calculation, e.g. 0.08 = 8%. Default: 0.08. | |
| riskFreeRate | No | Annual risk-free rate for Sortino and VaR excess return, e.g. 0.05 = 5%. Default: 0.05. | |
| rebalanceMethod | No | Portfolio construction method for rebalance recommendations. Default: profile. | |
| marketIndicators | No | Optional macro indicators — improves market regime detection confidence to HIGH when 3+ provided. |
Implementation Reference
- src/analyze/diversification.ts:59-118 (handler)The main handler function that calculates the diversification metrics for a given portfolio, including HHI, effective assets, breakdowns, and risk warnings.
export function analyzeDiversification(portfolio: Portfolio): DiversificationResult { const sumWeightsSq = portfolio.holdings.reduce((acc, h) => acc + h.weight ** 2, 0) const hhi = Math.round(sumWeightsSq * 10000) const effectiveAssets = Number((1 / sumWeightsSq).toFixed(2)) const grade = hhiGrade(hhi) const stableRatio = portfolio.holdings .filter((h) => h.isStable || h.assetClass === 'cash') .reduce((acc, h) => acc + h.weight, 0) const topHolding = portfolio.holdings.reduce((max, h) => (h.weight > max.weight ? h : max)) const acBreakdown = assetClassBreakdown(portfolio) const regBreakdown = regionBreakdown(portfolio) const secBreakdown = sectorBreakdown(portfolio) // Count distinct asset classes (excluding cash) const nonCashClasses = new Set( portfolio.holdings.filter((h) => h.assetClass !== 'cash').map((h) => h.assetClass), ) const warnings: string[] = [] if (topHolding.weight > 0.5) warnings.push( `Over 50% concentrated in single asset (${topHolding.asset} at ${Math.round(topHolding.weight * 100)}%)`, ) if (stableRatio < 0.05 && portfolio.profile !== 'aggressive') warnings.push('Less than 5% in stable / cash assets — no liquidity buffer') if (effectiveAssets < 2) warnings.push('Effective number of holdings below 2 — portfolio is barely diversified') if (hhi > 4000) warnings.push(`HHI = ${hhi} (>4000) — high concentration risk`) if (nonCashClasses.size === 1) warnings.push(`Single asset class (${[...nonCashClasses][0]}) — no cross-asset diversification`) const cryptoWeight = acBreakdown.find((a) => a.assetClass === 'crypto')?.weight ?? 0 if (cryptoWeight > 0.7) warnings.push(`${Math.round(cryptoWeight * 100)}% in crypto — extreme volatility exposure`) const emWeight = regBreakdown.find((r) => r.region === 'EM')?.weight ?? 0 if (emWeight > 0.5 && portfolio.profile !== 'aggressive') warnings.push(`${Math.round(emWeight * 100)}% in emerging markets — elevated geopolitical / currency risk`) return { hhi, effectiveAssets, grade, stableRatio: Number(stableRatio.toFixed(4)), topHolding: { asset: topHolding.asset, weight: topHolding.weight }, assetClassBreakdown: acBreakdown, regionBreakdown: regBreakdown, sectorBreakdown: secBreakdown, warnings, } }