/** * Returns Top source % and HHI-based traffic light (HHI hidden from UI) * Uses Herfindahl-Hirschman Index internally but only exposes traffic light color */ export const useConcentration = () => { const calculateTopSourcePct = (revenueShares: number[]): number => { if (revenueShares.length === 0) return 0 return Math.max(...revenueShares) } const calculateHHI = (revenueShares: number[]): number => { // HHI = sum of squared market shares (as percentages) return revenueShares.reduce((sum, share) => sum + (share * share), 0) } const getConcentrationStatus = (topSourcePct: number, hhi: number): 'green' | 'yellow' | 'red' => { // Primary threshold based on top source % if (topSourcePct > 50) return 'red' if (topSourcePct > 35) return 'yellow' // Secondary check using HHI for more nuanced analysis if (hhi > 2500) return 'red' // Highly concentrated if (hhi > 1500) return 'yellow' // Moderately concentrated return 'green' } const getConcentrationMessage = (status: 'green' | 'yellow' | 'red'): string => { switch (status) { case 'red': return 'Most of your money comes from one place. Add another stream to reduce risk.' case 'yellow': return 'Revenue somewhat concentrated. Consider diversifying further.' case 'green': return 'Good revenue diversification.' } } const analyzeConcentration = (revenueStreams: Array<{ targetPct: number }>) => { const shares = revenueStreams.map(stream => stream.targetPct || 0) const topSourcePct = calculateTopSourcePct(shares) const hhi = calculateHHI(shares) const status = getConcentrationStatus(topSourcePct, hhi) return { topSourcePct, status, message: getConcentrationMessage(status) // Note: HHI is deliberately not exposed in return } } return { calculateTopSourcePct, getConcentrationStatus, getConcentrationMessage, analyzeConcentration } }