# Portfolio Analysis

> Analyze entire crypto portfolio with allocation, risk metrics, and...

- Skill: `tools-only/portfolio-analysis` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds@latest add tools-only/portfolio-analysis`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tools-only/portfolio-analysis/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: tools-only (https://skillmd.com/u/tools-only)
- Updated: 2026-09-09
- Page: https://skillmd.com/skills/tools-only/portfolio-analysis

---

# Portfolio Analysis

Comprehensive portfolio analysis for cryptocurrency holdings with advanced metrics, risk assessment, and optimization recommendations.

## Usage

Analyze the user's complete crypto portfolio to provide insights on:
- Asset allocation and diversification
- Risk metrics (Sharpe ratio, volatility, max drawdown)
- Performance attribution
- Correlation analysis
- Rebalancing recommendations

## Implementation

### 1. Portfolio Analytics Engine

```javascript
class PortfolioAnalyzer {
    constructor() {
        this.riskFreeRate = 0.02; // 2% annual risk-free rate
        this.historicalData = new Map();
        this.correlationMatrix = null;
    }

    async analyzePortfolio(positions) {
        // Fetch current prices and calculate values
        const portfolioData = await this.enrichPositions(positions);

        // Calculate core metrics
        const metrics = {
            totalValue: this.calculateTotalValue(portfolioData),
            totalCost: this.calculateTotalCost(portfolioData),
            totalPnL: this.calculateTotalPnL(portfolioData),
            allocation: this.calculateAllocation(portfolioData),
            concentration: this.calculateConcentration(portfolioData),
            volatility: await this.calculateVolatility(portfolioData),
            sharpeRatio: await this.calculateSharpeRatio(portfolioData),
            sortino: await this.calculateSortinoRatio(portfolioData),
            maxDrawdown: await this.calculateMaxDrawdown(portfolioData),
            correlations: await this.calculateCorrelations(portfolioData),
            var95: this.calculateValueAtRisk(portfolioData, 0.95),
            var99: this.calculateValueAtRisk(portfolioData, 0.99)
        };

        // Generate insights and recommendations
        const analysis = {
            metrics,
            riskAssessment: this.assessRisk(metrics),
            diversificationScore: this.calculateDiversificationScore(metrics),
            rebalancingPlan: this.generateRebalancingPlan(portfolioData, metrics),
            optimizations: this.suggestOptimizations(metrics)
        };

        return analysis;
    }

    calculateAllocation(portfolioData) {
        const totalValue = portfolioData.reduce((sum, p) => sum + p.currentValue, 0);

        return portfolioData.map(position => ({
            symbol: position.symbol,
            value: position.currentValue,
            percentage: (position.currentValue / totalValue) * 100,
            targetPercentage: position.targetAllocation || null,
            deviation: position.targetAllocation
                ? Math.abs(((position.currentValue / totalValue) * 100) - position.targetAllocation)
                : 0
        })).sort((a, b) => b.percentage - a.percentage);
    }

    calculateConcentration(portfolioData) {
        const allocations = this.calculateAllocation(portfolioData);
        const herfindahlIndex = allocations.reduce((sum, a) => {
            return sum + Math.pow(a.percentage / 100, 2);
        }, 0);

        return {
            herfindahlIndex: herfindahlIndex.toFixed(4),
            effectiveAssets: (1 / herfindahlIndex).toFixed(2),
            topAssetConcentration: allocations[0].percentage.toFixed(2),
            top3Concentration: allocations.slice(0, 3).reduce((sum, a) => sum + a.percentage, 0).toFixed(2)
        };
    }

    async calculateVolatility(portfolioData, period = 30) {
        const returns = await this.getHistoricalReturns(portfolioData, period);

        if (returns.length < 2) return null;

        const avgReturn = returns.reduce((sum, r) => sum + r, 0) / returns.length;
        const variance = returns.reduce((sum, r) => sum + Math.pow(r - avgReturn, 2), 0) / (returns.length - 1);
        const volatility = Math.sqrt(variance);

        // Annualized volatility
        return {
            daily: (volatility * 100).toFixed(2),
            weekly: (volatility * Math.sqrt(7) * 100).toFixed(2),
            monthly: (volatility * Math.sqrt(30) * 100).toFixed(2),
            annual: (volatility * Math.sqrt(365) * 100).toFixed(2)
        };
    }

    async calculateSharpeRatio(portfolioData) {
        const returns = await this.getHistoricalReturns(portfolioData, 365);
        if (returns.length < 30) return null;

        const avgReturn = returns.reduce((sum, r) => sum + r, 0) / returns.length;
        const annualizedReturn = avgReturn * 365;

        const stdDev = Math.sqrt(
            returns.reduce((sum, r) => sum + Math.pow(r - avgReturn, 2), 0) / (returns.length - 1)
        );
        const annualizedStdDev = stdDev * Math.sqrt(365);

        const sharpeRatio = (annualizedReturn - this.riskFreeRate) / annualizedStdDev;

        return {
            value: sharpeRatio.toFixed(3),
            interpretation: this.interpretSharpeRatio(sharpeRatio)
        };
    }

    interpretSharpeRatio(ratio) {
        if (ratio < 0) return 'POOR - Returns below risk-free rate';
        if (ratio < 0.5) return 'SUBOPTIMAL - Low risk-adjusted returns';
        if (ratio < 1.0) return 'ACCEPTABLE - Moderate risk-adjusted returns';
        if (ratio < 2.0) return 'GOOD - Strong risk-adjusted returns';
        return 'EXCELLENT - Outstanding risk-adjusted returns';
    }

    async calculateCorrelations(portfolioData) {
        if (portfolioData.length < 2) return null;

        const correlationMatrix = [];
        const symbols = portfolioData.map(p => p.symbol);

        for (let i = 0; i < symbols.length; i++) {
            const row = [];
            for (let j = 0; j < symbols.length; j++) {
                if (i === j) {
                    row.push(1.0);
                } else {
                    const correlation = await this.calculatePairCorrelation(
                        symbols[i],
                        symbols[j],
                        30 // 30-day correlation
                    );
                    row.push(correlation);
                }
            }
            correlationMatrix.push(row);
        }

        // Find highest correlations
        const highCorrelations = [];
        for (let i = 0; i < symbols.length; i++) {
            for (let j = i + 1; j < symbols.length; j++) {
                if (Math.abs(correlationMatrix[i][j]) > 0.7) {
                    highCorrelations.push({
                        pair: `${symbols[i]}-${symbols[j]}`,
                        correlation: correlationMatrix[i][j].toFixed(3),
                        interpretation: correlationMatrix[i][j] > 0 ? 'POSITIVE' : 'NEGATIVE'
                    });
                }
            }
        }

        return {
            matrix: correlationMatrix,
            symbols,
            highCorrelations,
            averageCorrelation: this.calculateAverageCorrelation(correlationMatrix)
        };
    }

    calculateValueAtRisk(portfolioData, confidenceLevel) {
        const returns = this.historicalData.get('portfolio_returns') || [];
        if (returns.length < 100) return null;

        const sortedReturns = returns.sort((a, b) => a - b);
        const index = Math.floor((1 - confidenceLevel) * sortedReturns.length);
        const var_value = sortedReturns[index];
        const totalValue = portfolioData.reduce((sum, p) => sum + p.currentValue, 0);

        return {
            percentage: (var_value * 100).toFixed(2),
            dollarAmount: (totalValue * var_value).toFixed(2),
            confidenceLevel: (confidenceLevel * 100).toFixed(0),
            interpretation: `${confidenceLevel * 100}% chance that losses won't exceed $${Math.abs(totalValue * var_value).toFixed(2)}`
        };
    }

    assessRisk(metrics) {
        const riskFactors = [];
        let riskScore = 0;

        // Concentration risk
        if (metrics.concentration.topAssetConcentration > 50) {
            riskFactors.push({
                type: 'CONCENTRATION',
                severity: 'HIGH',
                description: `Top asset represents ${metrics.concentration.topAssetConcentration}% of portfolio`
            });
            riskScore += 30;
        }

        // Volatility risk
        if (metrics.volatility && metrics.volatility.annual > 100) {
            riskFactors.push({
                type: 'VOLATILITY',
                severity: 'HIGH',
                description: `Annual volatility exceeds 100% (${metrics.volatility.annual}%)`
            });
            riskScore += 25;
        }

        // Correlation risk
        if (metrics.correlations && metrics.correlations.highCorrelations.length > 0) {
            riskFactors.push({
                type: 'CORRELATION',
                severity: 'MEDIUM',
                description: `${metrics.correlations.highCorrelations.length} asset pairs highly correlated`
            });
            riskScore += 15;
        }

        // Drawdown risk
        if (metrics.maxDrawdown && metrics.maxDrawdown.percentage > 40) {
            riskFactors.push({
                type: 'DRAWDOWN',
                severity: 'HIGH',
                description: `Maximum drawdown of ${metrics.maxDrawdown.percentage}% observed`
            });
            riskScore += 20;
        }

        return {
            overallScore: Math.min(riskScore, 100),
            level: this.determineRiskLevel(riskScore),
            factors: riskFactors,
            recommendations: this.generateRiskRecommendations(riskFactors)
        };
    }

    determineRiskLevel(score) {
        if (score < 20) return 'LOW';
        if (score < 40) return 'MODERATE';
        if (score < 60) return 'ELEVATED';
        if (score < 80) return 'HIGH';
        return 'CRITICAL';
    }

    generateRebalancingPlan(portfolioData, metrics) {
        const currentAllocations = metrics.allocation;
        const targetAllocations = this.calculateOptimalAllocation(portfolioData, metrics);
        const totalValue = metrics.totalValue;

        const rebalancingActions = [];

        currentAllocations.forEach((current, index) => {
            const target = targetAllocations[index];
            const currentValue = current.value;
            const targetValue = (target.percentage / 100) * totalValue;
            const difference = targetValue - currentValue;

            if (Math.abs(difference) > totalValue * 0.01) { // Only rebalance if > 1% of portfolio
                rebalancingActions.push({
                    symbol: current.symbol,
                    action: difference > 0 ? 'BUY' : 'SELL',
                    amount: Math.abs(difference).toFixed(2),
                    currentPercentage: current.percentage.toFixed(2),
                    targetPercentage: target.percentage.toFixed(2),
                    reason: target.reason
                });
            }
        });

        return {
            actions: rebalancingActions,
            estimatedCost: this.estimateRebalancingCost(rebalancingActions),
            expectedImprovement: this.estimateImprovementMetrics(targetAllocations, currentAllocations)
        };
    }

    calculateOptimalAllocation(portfolioData, metrics) {
        // Modern Portfolio Theory optimization
        // This is a simplified version - real implementation would use quadratic programming

        const riskTolerance = this.determineRiskTolerance(metrics);
        const correlations = metrics.correlations?.matrix || [];

        // Start with equal weight
        let allocations = portfolioData.map(p => ({
            symbol: p.symbol,
            percentage: 100 / portfolioData.length,
            reason: 'Equal weight baseline'
        }));

        // Adjust based on performance
        allocations = this.adjustForPerformance(allocations, portfolioData);

        // Adjust based on risk
        allocations = this.adjustForRisk(allocations, metrics, riskTolerance);

        // Adjust based on correlations
        if (correlations.length > 0) {
            allocations = this.adjustForCorrelations(allocations, correlations);
        }

        // Apply constraints
        allocations = this.applyAllocationConstraints(allocations);

        return allocations;
    }

    adjustForPerformance(allocations, portfolioData) {
        // Increase allocation to better performers
        const performances = portfolioData.map(p => ({
            symbol: p.symbol,
            performance: p.pnlPercentage
        })).sort((a, b) => b.performance - a.performance);

        return allocations.map(alloc => {
            const perf = performances.find(p => p.symbol === alloc.symbol);
            const rank = performances.indexOf(perf);

            // Top 1/3 get boost, bottom 1/3 get reduction
            if (rank < performances.length / 3) {
                alloc.percentage *= 1.2;
                alloc.reason = 'Strong performance';
            } else if (rank > (performances.length * 2 / 3)) {
                alloc.percentage *= 0.8;
                alloc.reason = 'Weak performance';
            }

            return alloc;
        });
    }

    applyAllocationConstraints(allocations) {
        // No single asset > 40%
        const maxAllocation = 40;
        // No single asset < 5%
        const minAllocation = 5;

        // Normalize to ensure sum = 100
        const total = allocations.reduce((sum, a) => sum + a.percentage, 0);
        allocations = allocations.map(a => ({
            ...a,
            percentage: (a.percentage / total) * 100
        }));

        // Apply constraints
        allocations = allocations.map(alloc => ({
            ...alloc,
            percentage: Math.min(Math.max(alloc.percentage, minAllocation), maxAllocation)
        }));

        // Re-normalize
        const newTotal = allocations.reduce((sum, a) => sum + a.percentage, 0);
        return allocations.map(a => ({
            ...a,
            percentage: (a.percentage / newTotal) * 100
        }));
    }
}
```

### 2. Portfolio Visualization

```javascript
class PortfolioVisualizer {
    generateReport(analysis) {
        return `
╔════════════════════════════════════════════════════════════════════════════╗
║                          PORTFOLIO ANALYSIS REPORT                         ║
╠════════════════════════════════════════════════════════════════════════════╣
║                              SUMMARY METRICS                               ║
╠════════════════════════════════════════════════════════════════════════════╣
║ Total Value:        $${analysis.metrics.totalValue.toFixed(2).padEnd(55)} ║
║ Total Cost:         $${analysis.metrics.totalCost.toFixed(2).padEnd(55)} ║
║ Total P&L:          ${this.formatPnL(analysis.metrics.totalPnL).padEnd(56)} ║
║ Total Return:       ${this.formatPercentage(analysis.metrics.totalReturn).padEnd(56)} ║
╠════════════════════════════════════════════════════════════════════════════╣
║                             RISK METRICS                                   ║
╠════════════════════════════════════════════════════════════════════════════╣
║ Risk Level:         ${analysis.riskAssessment.level.padEnd(56)} ║
║ Risk Score:         ${(analysis.riskAssessment.overallScore + '/100').padEnd(56)} ║
║ Sharpe Ratio:       ${analysis.metrics.sharpeRatio?.value || 'N/A'.padEnd(56)} ║
║ Annual Volatility:  ${analysis.metrics.volatility?.annual + '%' || 'N/A'.padEnd(56)} ║
║ Max Drawdown:       ${analysis.metrics.maxDrawdown?.percentage + '%' || 'N/A'.padEnd(56)} ║
║ VaR (95%):         ${analysis.metrics.var95?.dollarAmount || 'N/A'.padEnd(56)} ║
╠════════════════════════════════════════════════════════════════════════════╣
║                            ASSET ALLOCATION                                ║
╠════════════════════════════════════════════════════════════════════════════╣
${this.formatAllocationTable(analysis.metrics.allocation)}
╠════════════════════════════════════════════════════════════════════════════╣
║                          CONCENTRATION ANALYSIS                            ║
╠════════════════════════════════════════════════════════════════════════════╣
║ Herfindahl Index:   ${analysis.metrics.concentration.herfindahlIndex.padEnd(56)} ║
║ Effective Assets:   ${analysis.metrics.concentration.effectiveAssets.padEnd(56)} ║
║ Top Asset:          ${analysis.metrics.concentration.topAssetConcentration + '%'.padEnd(56)} ║
║ Top 3 Assets:       ${analysis.metrics.concentration.top3Concentration + '%'.padEnd(56)} ║
╠════════════════════════════════════════════════════════════════════════════╣
║                         REBALANCING RECOMMENDATIONS                        ║
╠════════════════════════════════════════════════════════════════════════════╣
${this.formatRebalancingActions(analysis.rebalancingPlan.actions)}
╠════════════════════════════════════════════════════════════════════════════╣
║                            RISK FACTORS                                    ║
╠════════════════════════════════════════════════════════════════════════════╣
${this.formatRiskFactors(analysis.riskAssessment.factors)}
╚════════════════════════════════════════════════════════════════════════════╝
        `;
    }

    formatAllocationTable(allocations) {
        return allocations.slice(0, 5).map(a =>
            `║ ${a.symbol.padEnd(10)} ${(a.percentage.toFixed(2) + '%').padEnd(10)} $${a.value.toFixed(2).padEnd(15)} ${this.getAllocationBar(a.percentage).padEnd(20)} ║`
        ).join('\n');
    }

    getAllocationBar(percentage) {
        const barLength = Math.floor(percentage / 5);
        return '█'.repeat(Math.min(barLength, 20));
    }

    formatRebalancingActions(actions) {
        if (actions.length === 0) {
            return '║ Portfolio is well-balanced. No actions required.                          ║';
        }

        return actions.slice(0, 3).map(a =>
            `║ ${a.action.padEnd(5)} ${a.symbol.padEnd(6)} $${a.amount.padEnd(10)} (${a.currentPercentage}% → ${a.targetPercentage}%)`.padEnd(77) + '║'
        ).join('\n');
    }

    formatRiskFactors(factors) {
        if (factors.length === 0) {
            return '║ No significant risk factors identified.                                   ║';
        }

        return factors.map(f =>
            `║ [${f.severity}] ${f.type}: ${f.description}`.padEnd(77) + '║'
        ).join('\n');
    }

    formatPnL(value) {
        const formatted = `$${Math.abs(value).toFixed(2)}`;
        return value >= 0 ? `+${formatted} ` : `-${formatted} `;
    }

    formatPercentage(value) {
        const formatted = `${Math.abs(value).toFixed(2)}%`;
        return value >= 0 ? `+${formatted} ` : `-${formatted} `;
    }
}
```

### 3. Optimization Strategies

```javascript
class PortfolioOptimizer {
    suggestOptimizations(metrics) {
        const suggestions = [];

        // Diversification suggestions
        if (metrics.concentration.effectiveAssets < 3) {
            suggestions.push({
                priority: 'HIGH',
                category: 'DIVERSIFICATION',
                action: 'Add more uncorrelated assets',
                benefit: 'Reduce concentration risk by 30-40%',
                implementation: `
                    Consider adding:
                    - Large cap altcoins (ETH, BNB) if heavily in BTC
                    - DeFi tokens if heavily in L1s
                    - Stablecoins for risk reduction
                `
            });
        }

        // Rebalancing suggestions
        if (metrics.allocation.some(a => a.deviation > 10)) {
            suggestions.push({
                priority: 'MEDIUM',
                category: 'REBALANCING',
                action: 'Rebalance to target allocations',
                benefit: 'Improve risk-adjusted returns',
                implementation: `
                    Set up periodic rebalancing:
                    - Monthly for volatile markets
                    - Quarterly for stable markets
                    - Threshold-based (when deviation > 15%)
                `
            });
        }

        // Risk management suggestions
        if (!metrics.stopLossesSet) {
            suggestions.push({
                priority: 'HIGH',
                category: 'RISK_MANAGEMENT',
                action: 'Implement stop-loss orders',
                benefit: 'Limit downside risk',
                implementation: `
                    Recommended stop-loss levels:
                    - Conservative: 15% below entry
                    - Moderate: 25% below entry
                    - Aggressive: 35% below entry
                `
            });
        }

        // Performance suggestions
        if (metrics.sharpeRatio && metrics.sharpeRatio.value < 0.5) {
            suggestions.push({
                priority: 'MEDIUM',
                category: 'PERFORMANCE',
                action: 'Improve risk-adjusted returns',
                benefit: 'Better Sharpe ratio',
                implementation: `
                    Options to improve:
                    - Reduce allocation to high-volatility assets
                    - Add yield-generating positions (staking, lending)
                    - Consider market-neutral strategies
                `
            });
        }

        return suggestions;
    }
}
```

## Error Handling

```javascript
async function executePortfolioAnalysis() {
    try {
        const analyzer = new PortfolioAnalyzer();
        const visualizer = new PortfolioVisualizer();

        // Get all open positions
        const positions = await getOpenPositions();

        if (positions.length === 0) {
            console.log('No positions found. Start by tracking some positions first.');
            return;
        }

        // Run analysis
        console.log('Analyzing portfolio...');
        const analysis = await analyzer.analyzePortfolio(positions);

        // Display report
        const report = visualizer.generateReport(analysis);
        console.log(report);

        // Save analysis
        await saveAnalysis(analysis);

    } catch (error) {
        console.error('Portfolio analysis failed:', error.message);

        if (error.code === 'INSUFFICIENT_DATA') {
            console.log('Not enough historical data for full analysis. Some metrics may be unavailable.');
        } else if (error.code === 'API_ERROR') {
            console.log('Failed to fetch market data. Please check your internet connection.');
        }
    }
}
```

This comprehensive portfolio analysis command provides institutional-grade analytics for crypto portfolios with actionable insights and optimization recommendations.
