24 KiB
24 KiB
description, shortcut
| description | shortcut |
|---|---|
| Analyze entire crypto portfolio with allocation, risk metrics, and rebalancing suggestions | pa |
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
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
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
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
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.