analyze_compare
Compare two investment portfolios across 9 key risk and performance metrics to identify superior strategies or evaluate rebalancing options.
Instructions
Side-by-side comparison of two portfolios across 9 key metrics: Sharpe ratio, Sortino ratio, VaR, CVaR, max drawdown, diversification score, stress test loss, market beta, and overall risk tier. Returns winner per metric and delta values. Useful for A/B testing portfolio strategies or comparing current vs. rebalanced allocation. Costs $0.05 USDC.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| portfolio_a | Yes | First portfolio to compare. | |
| portfolio_b | Yes | Second portfolio to compare against portfolio_a. |
Implementation Reference
- src/analyze/compare.ts:13-124 (handler)The implementation of the `analyzeCompare` tool handler, which computes and compares various risk, VaR, and diversification metrics between two portfolios to determine an overall recommendation.
export function analyzeCompare(portfolioA: Portfolio, portfolioB: Portfolio): CompareResult { const riskA = analyzeRisk(portfolioA) const riskB = analyzeRisk(portfolioB) const varA = analyzeVaR(portfolioA) const varB = analyzeVaR(portfolioB) const divA = analyzeDiversification(portfolioA) const divB = analyzeDiversification(portfolioB) const metrics: MetricComparison[] = [ { metric: 'Sharpe Ratio', portfolio_a: riskA.portfolioSharpe, portfolio_b: riskB.portfolioSharpe, winner: winner(riskA.portfolioSharpe, riskB.portfolioSharpe, true, 0.02), delta: riskA.portfolioSharpe - riskB.portfolioSharpe, description: 'Risk-adjusted return per unit of total volatility. Higher is better.', }, { metric: 'Sortino Ratio', portfolio_a: riskA.portfolioSortino, portfolio_b: riskB.portfolioSortino, winner: winner(riskA.portfolioSortino, riskB.portfolioSortino, true, 0.02), delta: riskA.portfolioSortino - riskB.portfolioSortino, description: 'Return relative to downside risk only. Higher is better.', }, { metric: 'Calmar Ratio', portfolio_a: riskA.portfolioCalmar, portfolio_b: riskB.portfolioCalmar, winner: winner(riskA.portfolioCalmar, riskB.portfolioCalmar, true, 0.02), delta: riskA.portfolioCalmar - riskB.portfolioCalmar, description: 'Return relative to max drawdown. Higher is better.', }, { metric: 'Annual Volatility', portfolio_a: riskA.portfolioVolatility, portfolio_b: riskB.portfolioVolatility, winner: winner(riskA.portfolioVolatility, riskB.portfolioVolatility, false, 0.005), delta: riskA.portfolioVolatility - riskB.portfolioVolatility, description: 'Annualised price fluctuation. Lower is better for capital-preservation goals.', }, { metric: 'Market Beta', portfolio_a: riskA.portfolioBeta, portfolio_b: riskB.portfolioBeta, winner: winner(Math.abs(riskA.portfolioBeta - 1), Math.abs(riskB.portfolioBeta - 1), false, 0.05), delta: riskA.portfolioBeta - riskB.portfolioBeta, description: 'Sensitivity to broad market movements. Lower = more defensive.', }, { metric: 'VaR 95% Daily', portfolio_a: varA.confidence95.dailyVaR, portfolio_b: varB.confidence95.dailyVaR, winner: winner(varA.confidence95.dailyVaR, varB.confidence95.dailyVaR, false, 0.001), delta: varA.confidence95.dailyVaR - varB.confidence95.dailyVaR, description: 'Daily downside at 95% confidence (loss fraction). Lower is better.', }, { metric: 'CVaR 99% Annual', portfolio_a: varA.confidence99.annualCVaR, portfolio_b: varB.confidence99.annualCVaR, winner: winner(varA.confidence99.annualCVaR, varB.confidence99.annualCVaR, false, 0.005), delta: varA.confidence99.annualCVaR - varB.confidence99.annualCVaR, description: 'Expected annual loss in worst 1% of years (loss fraction). Lower is better.', }, { metric: 'HHI Concentration', portfolio_a: divA.hhi, portfolio_b: divB.hhi, winner: winner(divA.hhi, divB.hhi, false, 100), delta: divA.hhi - divB.hhi, description: 'Herfindahl-Hirschman concentration index (0–10000). Lower = better diversified.', }, { metric: 'Effective Assets', portfolio_a: divA.effectiveAssets, portfolio_b: divB.effectiveAssets, winner: winner(divA.effectiveAssets, divB.effectiveAssets, true, 0.1), delta: divA.effectiveAssets - divB.effectiveAssets, description: 'Equivalent independent positions. Higher = better diversification.', }, ] const aWins = metrics.filter((m) => m.winner === 'a').length const bWins = metrics.filter((m) => m.winner === 'b').length const total = metrics.length const overall_winner: 'a' | 'b' | 'tie' = aWins > bWins ? 'a' : bWins > aWins ? 'b' : 'tie' const a_advantages = metrics.filter((m) => m.winner === 'a').map((m) => m.metric) const b_advantages = metrics.filter((m) => m.winner === 'b').map((m) => m.metric) let recommendation: string if (overall_winner === 'a') { recommendation = `Portfolio A is stronger overall (wins ${aWins}/${total} metrics). ` + `Key advantages: ${a_advantages.join(', ')}.` } else if (overall_winner === 'b') { recommendation = `Portfolio B is stronger overall (wins ${bWins}/${total} metrics). ` + `Key advantages: ${b_advantages.join(', ')}.` } else { const aStr = a_advantages.length > 0 ? `A leads: ${a_advantages.join(', ')}` : '' const bStr = b_advantages.length > 0 ? `B leads: ${b_advantages.join(', ')}` : '' recommendation = `Portfolios are evenly matched. Choose based on your primary objective. ` + [aStr, bStr].filter(Boolean).join('. ') + '.' } return { metrics, overall_winner, a_advantages, b_advantages, recommendation } }