commit 581108d2fe2d6992c0f149120414a2f4ae2c7e3e Author: Zhongwei Li Date: Sat Nov 29 18:53:07 2025 +0800 Initial commit diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json new file mode 100644 index 0000000..5ee36e4 --- /dev/null +++ b/.claude-plugin/plugin.json @@ -0,0 +1,16 @@ +{ + "name": "crypto-portfolio-tracker", + "description": "Professional crypto portfolio tracking with real-time prices, PnL analysis, and risk metrics", + "version": "1.0.0", + "author": { + "name": "Intent Solutions IO", + "email": "jeremy@intentsolutions.ai", + "url": "https://intentsolutions.ai" + }, + "skills": [ + "./skills" + ], + "commands": [ + "./commands" + ] +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..1cc0fdd --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# crypto-portfolio-tracker + +Professional crypto portfolio tracking with real-time prices, PnL analysis, and risk metrics diff --git a/commands/portfolio-analysis.md b/commands/portfolio-analysis.md new file mode 100644 index 0000000..0fab21a --- /dev/null +++ b/commands/portfolio-analysis.md @@ -0,0 +1,580 @@ +--- +description: Analyze entire crypto portfolio with allocation, risk metrics, and rebalancing suggestions +shortcut: 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 + +```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. \ No newline at end of file diff --git a/commands/track-position.md b/commands/track-position.md new file mode 100644 index 0000000..34a91af --- /dev/null +++ b/commands/track-position.md @@ -0,0 +1,418 @@ +--- +description: Track crypto positions with entry prices, current values, and PnL calculations +shortcut: tp +--- + +# Track Crypto Position + +Comprehensive position tracking for cryptocurrency investments with real-time price updates and advanced analytics. + +## Usage + +When the user wants to track a crypto position, gather the following information and implement a complete tracking system: + +### Required Information +- **Symbol**: The cryptocurrency ticker (BTC, ETH, SOL, etc.) +- **Entry Price**: Purchase price per unit +- **Quantity**: Amount purchased +- **Entry Date**: When the position was opened +- **Exchange**: Where the trade was executed (optional) +- **Target Price**: Profit target (optional) +- **Stop Loss**: Risk management level (optional) + +## Implementation + +### 1. Database Schema + +Create a structured database to track positions: + +```sql +CREATE TABLE IF NOT EXISTS crypto_positions ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + symbol VARCHAR(10) NOT NULL, + entry_price DECIMAL(20,8) NOT NULL, + quantity DECIMAL(20,8) NOT NULL, + entry_date TIMESTAMP NOT NULL, + current_price DECIMAL(20,8), + last_updated TIMESTAMP, + exchange VARCHAR(50), + target_price DECIMAL(20,8), + stop_loss DECIMAL(20,8), + status VARCHAR(20) DEFAULT 'open', + notes TEXT, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +CREATE TABLE IF NOT EXISTS position_history ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + position_id UUID REFERENCES crypto_positions(id), + price DECIMAL(20,8) NOT NULL, + value DECIMAL(20,8) NOT NULL, + pnl DECIMAL(20,8) NOT NULL, + pnl_percentage DECIMAL(10,4) NOT NULL, + recorded_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +CREATE INDEX idx_positions_symbol ON crypto_positions(symbol); +CREATE INDEX idx_positions_status ON crypto_positions(status); +CREATE INDEX idx_history_position ON position_history(position_id); +``` + +### 2. Position Tracking Class + +```javascript +class CryptoPositionTracker { + constructor() { + this.priceFeeds = { + coingecko: 'https://api.coingecko.com/api/v3', + binance: 'https://api.binance.com/api/v3', + coinbase: 'https://api.coinbase.com/v2' + }; + } + + async trackPosition(positionData) { + const { + symbol, + entryPrice, + quantity, + entryDate, + exchange = 'Unknown', + targetPrice = null, + stopLoss = null, + notes = '' + } = positionData; + + // Validate input + this.validatePositionData(positionData); + + // Get current price + const currentPrice = await this.getCurrentPrice(symbol); + + // Calculate metrics + const metrics = this.calculateMetrics({ + entryPrice, + quantity, + currentPrice + }); + + // Store position + const position = await this.storePosition({ + symbol: symbol.toUpperCase(), + entry_price: entryPrice, + quantity, + entry_date: entryDate, + current_price: currentPrice, + exchange, + target_price: targetPrice, + stop_loss: stopLoss, + notes + }); + + // Record initial history + await this.recordHistory(position.id, currentPrice, metrics); + + return { + position, + metrics, + analysis: this.analyzePosition(position, metrics) + }; + } + + calculateMetrics({ entryPrice, quantity, currentPrice }) { + const entryValue = entryPrice * quantity; + const currentValue = currentPrice * quantity; + const unrealizedPnL = currentValue - entryValue; + const pnlPercentage = ((currentValue - entryValue) / entryValue) * 100; + + return { + entryValue: parseFloat(entryValue.toFixed(2)), + currentValue: parseFloat(currentValue.toFixed(2)), + unrealizedPnL: parseFloat(unrealizedPnL.toFixed(2)), + pnlPercentage: parseFloat(pnlPercentage.toFixed(2)), + roi: pnlPercentage, + riskRewardRatio: this.calculateRiskReward({ + entryPrice, + currentPrice, + targetPrice: position.target_price, + stopLoss: position.stop_loss + }) + }; + } + + calculateRiskReward({ entryPrice, currentPrice, targetPrice, stopLoss }) { + if (!targetPrice || !stopLoss) return null; + + const potentialReward = targetPrice - entryPrice; + const potentialRisk = entryPrice - stopLoss; + + if (potentialRisk === 0) return null; + + return parseFloat((potentialReward / potentialRisk).toFixed(2)); + } + + analyzePosition(position, metrics) { + const analysis = { + status: this.determineStatus(metrics), + recommendation: this.getRecommendation(position, metrics), + risks: this.identifyRisks(position, metrics), + opportunities: this.identifyOpportunities(position, metrics) + }; + + return analysis; + } + + determineStatus(metrics) { + if (metrics.pnlPercentage > 20) return 'STRONG_PROFIT'; + if (metrics.pnlPercentage > 5) return 'PROFIT'; + if (metrics.pnlPercentage > -5) return 'NEUTRAL'; + if (metrics.pnlPercentage > -20) return 'LOSS'; + return 'SIGNIFICANT_LOSS'; + } + + getRecommendation(position, metrics) { + const recommendations = []; + + // Check stop loss + if (position.stop_loss && position.current_price <= position.stop_loss) { + recommendations.push({ + action: 'EXIT', + reason: 'Stop loss hit', + urgency: 'HIGH' + }); + } + + // Check target + if (position.target_price && position.current_price >= position.target_price) { + recommendations.push({ + action: 'TAKE_PROFIT', + reason: 'Target price reached', + urgency: 'MEDIUM' + }); + } + + // Check significant profit + if (metrics.pnlPercentage > 50) { + recommendations.push({ + action: 'PARTIAL_PROFIT', + reason: 'Consider taking partial profits (50%+ gain)', + urgency: 'LOW' + }); + } + + // Check significant loss + if (metrics.pnlPercentage < -30 && !position.stop_loss) { + recommendations.push({ + action: 'SET_STOP_LOSS', + reason: 'Significant loss without stop loss protection', + urgency: 'HIGH' + }); + } + + return recommendations; + } + + async getCurrentPrice(symbol) { + // Implementation would fetch from multiple sources + // and return average or most reliable price + try { + const prices = await Promise.all([ + this.fetchCoingeckoPrice(symbol), + this.fetchBinancePrice(symbol) + ]); + + return prices.reduce((sum, price) => sum + price, 0) / prices.length; + } catch (error) { + throw new Error(`Failed to fetch price for ${symbol}: ${error.message}`); + } + } + + async updateAllPositions() { + const openPositions = await this.getOpenPositions(); + const updates = []; + + for (const position of openPositions) { + try { + const currentPrice = await this.getCurrentPrice(position.symbol); + const metrics = this.calculateMetrics({ + entryPrice: position.entry_price, + quantity: position.quantity, + currentPrice + }); + + await this.updatePosition(position.id, { + current_price: currentPrice, + last_updated: new Date() + }); + + await this.recordHistory(position.id, currentPrice, metrics); + + updates.push({ + symbol: position.symbol, + currentPrice, + metrics, + status: 'UPDATED' + }); + } catch (error) { + updates.push({ + symbol: position.symbol, + status: 'ERROR', + error: error.message + }); + } + } + + return updates; + } +} +``` + +### 3. Display Format + +When displaying position information, format it clearly: + +```javascript +function displayPosition(position, metrics) { + const output = ` +╔════════════════════════════════════════════════════════════════╗ +║ CRYPTO POSITION TRACKER ║ +╠════════════════════════════════════════════════════════════════╣ +║ Symbol: ${position.symbol.padEnd(48)} ║ +║ Entry Price: $${position.entry_price.toFixed(2).padEnd(47)} ║ +║ Current Price: $${position.current_price.toFixed(2).padEnd(47)} ║ +║ Quantity: ${position.quantity.toString().padEnd(48)} ║ +╠════════════════════════════════════════════════════════════════╣ +║ METRICS ║ +╠════════════════════════════════════════════════════════════════╣ +║ Entry Value: $${metrics.entryValue.toFixed(2).padEnd(47)} ║ +║ Current Value: $${metrics.currentValue.toFixed(2).padEnd(47)} ║ +║ Unrealized P&L: ${formatPnL(metrics.unrealizedPnL).padEnd(47)} ║ +║ P&L %: ${formatPnLPercentage(metrics.pnlPercentage).padEnd(48)} ║ +╠════════════════════════════════════════════════════════════════╣ +║ Status: ${determineStatusEmoji(metrics.pnlPercentage)} ${metrics.status.padEnd(43)} ║ +╚════════════════════════════════════════════════════════════════╝ + `; + + return output; +} + +function formatPnL(value) { + const formatted = `$${Math.abs(value).toFixed(2)}`; + if (value >= 0) { + return `+${formatted} `; + } else { + return `-${formatted} `; + } +} + +function formatPnLPercentage(percentage) { + const formatted = `${Math.abs(percentage).toFixed(2)}%`; + if (percentage >= 0) { + return `+${formatted} `; + } else { + return `-${formatted} `; + } +} + +function determineStatusEmoji(percentage) { + if (percentage > 20) return ''; + if (percentage > 5) return ''; + if (percentage > -5) return ''; + if (percentage > -20) return '️'; + return ''; +} +``` + +### 4. Alert System + +Set up automatic alerts for significant events: + +```javascript +class PositionAlertSystem { + constructor() { + this.alertThresholds = { + profitTarget: 0.20, // 20% profit + lossWarning: -0.10, // 10% loss + criticalLoss: -0.25, // 25% loss + volatilitySpike: 0.15 // 15% daily move + }; + } + + async checkAlerts(position, previousPrice, currentPrice) { + const alerts = []; + const priceChange = ((currentPrice - previousPrice) / previousPrice) * 100; + + // Check profit targets + if (metrics.pnlPercentage >= this.alertThresholds.profitTarget * 100) { + alerts.push({ + type: 'PROFIT_TARGET', + message: ` ${position.symbol} hit profit target: +${metrics.pnlPercentage.toFixed(2)}%`, + severity: 'INFO', + action: 'Consider taking profits' + }); + } + + // Check loss warnings + if (metrics.pnlPercentage <= this.alertThresholds.lossWarning * 100 && + metrics.pnlPercentage > this.alertThresholds.criticalLoss * 100) { + alerts.push({ + type: 'LOSS_WARNING', + message: `️ ${position.symbol} loss warning: ${metrics.pnlPercentage.toFixed(2)}%`, + severity: 'WARNING', + action: 'Review position and consider stop loss' + }); + } + + // Check critical loss + if (metrics.pnlPercentage <= this.alertThresholds.criticalLoss * 100) { + alerts.push({ + type: 'CRITICAL_LOSS', + message: ` ${position.symbol} critical loss: ${metrics.pnlPercentage.toFixed(2)}%`, + severity: 'CRITICAL', + action: 'Immediate review required' + }); + } + + // Check volatility + if (Math.abs(priceChange) >= this.alertThresholds.volatilitySpike * 100) { + alerts.push({ + type: 'VOLATILITY_SPIKE', + message: ` ${position.symbol} volatility spike: ${priceChange > 0 ? '+' : ''}${priceChange.toFixed(2)}% in 24h`, + severity: 'INFO', + action: 'Monitor closely for opportunities or risks' + }); + } + + return alerts; + } +} +``` + +## Error Handling + +Always implement comprehensive error handling: + +```javascript +try { + const position = await tracker.trackPosition({ + symbol: 'BTC', + entryPrice: 45000, + quantity: 0.5, + entryDate: new Date('2024-01-01'), + targetPrice: 60000, + stopLoss: 40000 + }); + + displayPosition(position.position, position.metrics); +} catch (error) { + if (error.code === 'INVALID_SYMBOL') { + console.error(`Invalid cryptocurrency symbol: ${error.symbol}`); + } else if (error.code === 'API_ERROR') { + console.error(`Failed to fetch price data: ${error.message}`); + } else { + console.error(`Unexpected error: ${error.message}`); + } +} +``` + +This command provides comprehensive position tracking with real-time updates, PnL calculations, risk analysis, and actionable recommendations for crypto investments. \ No newline at end of file diff --git a/plugin.lock.json b/plugin.lock.json new file mode 100644 index 0000000..5590047 --- /dev/null +++ b/plugin.lock.json @@ -0,0 +1,89 @@ +{ + "$schema": "internal://schemas/plugin.lock.v1.json", + "pluginId": "gh:jeremylongshore/claude-code-plugins-plus:plugins/crypto/crypto-portfolio-tracker", + "normalized": { + "repo": null, + "ref": "refs/tags/v20251128.0", + "commit": "4f7c9da8bf1061171006afc775ffbbb569c6c1ba", + "treeHash": "7e9415002c8fb95dbce1e035c87f046b75034dd0588c114921b643e471fd9172", + "generatedAt": "2025-11-28T10:18:16.306418Z", + "toolVersion": "publish_plugins.py@0.2.0" + }, + "origin": { + "remote": "git@github.com:zhongweili/42plugin-data.git", + "branch": "master", + "commit": "aa1497ed0949fd50e99e70d6324a29c5b34f9390", + "repoRoot": "/Users/zhongweili/projects/openmind/42plugin-data" + }, + "manifest": { + "name": "crypto-portfolio-tracker", + "description": "Professional crypto portfolio tracking with real-time prices, PnL analysis, and risk metrics", + "version": "1.0.0" + }, + "content": { + "files": [ + { + "path": "README.md", + "sha256": "a1e503a8b1c6d7828555b4811ed4121e0d736d56ab54a9a76c252913cf934889" + }, + { + "path": ".claude-plugin/plugin.json", + "sha256": "61b237256e23633ca78504410690484a246131e6e7ed2b4846133bfbc9f9af82" + }, + { + "path": "commands/track-position.md", + "sha256": "c1a5e110859ce0f8f80ca78f81409c0541b7c5df61b64cc7080c6841829ec3ae" + }, + { + "path": "commands/portfolio-analysis.md", + "sha256": "37aa1243c8db4b41f519b6eadb4dee9ef10ba43e51b91da2becfc47cf91c1680" + }, + { + "path": "skills/skill-adapter/references/examples.md", + "sha256": "922bbc3c4ebf38b76f515b5c1998ebde6bf902233e00e2c5a0e9176f975a7572" + }, + { + "path": "skills/skill-adapter/references/best-practices.md", + "sha256": "c8f32b3566252f50daacd346d7045a1060c718ef5cfb07c55a0f2dec5f1fb39e" + }, + { + "path": "skills/skill-adapter/references/README.md", + "sha256": "779b6cf97de7e9c8db5ba04d28f94cd18636931a1e6c82c4e5b3a57ec6db8eae" + }, + { + "path": "skills/skill-adapter/scripts/helper-template.sh", + "sha256": "0881d5660a8a7045550d09ae0acc15642c24b70de6f08808120f47f86ccdf077" + }, + { + "path": "skills/skill-adapter/scripts/validation.sh", + "sha256": "92551a29a7f512d2036e4f1fb46c2a3dc6bff0f7dde4a9f699533e446db48502" + }, + { + "path": "skills/skill-adapter/scripts/README.md", + "sha256": "fec629c4d3b22cd08f9e81ad3481ee9ef44538da009943a94cc5ff7637157e56" + }, + { + "path": "skills/skill-adapter/assets/test-data.json", + "sha256": "ac17dca3d6e253a5f39f2a2f1b388e5146043756b05d9ce7ac53a0042eee139d" + }, + { + "path": "skills/skill-adapter/assets/README.md", + "sha256": "dd3a93632741c98e438badefe6b48e7589ba40170afbd30867f247afddcef4e9" + }, + { + "path": "skills/skill-adapter/assets/skill-schema.json", + "sha256": "f5639ba823a24c9ac4fb21444c0717b7aefde1a4993682897f5bf544f863c2cd" + }, + { + "path": "skills/skill-adapter/assets/config-template.json", + "sha256": "0c2ba33d2d3c5ccb266c0848fc43caa68a2aa6a80ff315d4b378352711f83e1c" + } + ], + "dirSha256": "7e9415002c8fb95dbce1e035c87f046b75034dd0588c114921b643e471fd9172" + }, + "security": { + "scannedAt": null, + "scannerVersion": null, + "flags": [] + } +} \ No newline at end of file diff --git a/skills/skill-adapter/assets/README.md b/skills/skill-adapter/assets/README.md new file mode 100644 index 0000000..87e0700 --- /dev/null +++ b/skills/skill-adapter/assets/README.md @@ -0,0 +1,7 @@ +# Assets + +Bundled resources for crypto-portfolio-tracker skill + +- [ ] rebalancing_template.xlsx: Excel template for visualizing and implementing rebalancing recommendations. +- [ ] portfolio_analysis_dashboard.html: HTML dashboard for displaying portfolio risk metrics and performance. +- [ ] example_portfolio.json: Example portfolio data in JSON format for testing and demonstration. diff --git a/skills/skill-adapter/assets/config-template.json b/skills/skill-adapter/assets/config-template.json new file mode 100644 index 0000000..16f1712 --- /dev/null +++ b/skills/skill-adapter/assets/config-template.json @@ -0,0 +1,32 @@ +{ + "skill": { + "name": "skill-name", + "version": "1.0.0", + "enabled": true, + "settings": { + "verbose": false, + "autoActivate": true, + "toolRestrictions": true + } + }, + "triggers": { + "keywords": [ + "example-trigger-1", + "example-trigger-2" + ], + "patterns": [] + }, + "tools": { + "allowed": [ + "Read", + "Grep", + "Bash" + ], + "restricted": [] + }, + "metadata": { + "author": "Plugin Author", + "category": "general", + "tags": [] + } +} diff --git a/skills/skill-adapter/assets/skill-schema.json b/skills/skill-adapter/assets/skill-schema.json new file mode 100644 index 0000000..8dc154c --- /dev/null +++ b/skills/skill-adapter/assets/skill-schema.json @@ -0,0 +1,28 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Claude Skill Configuration", + "type": "object", + "required": ["name", "description"], + "properties": { + "name": { + "type": "string", + "pattern": "^[a-z0-9-]+$", + "maxLength": 64, + "description": "Skill identifier (lowercase, hyphens only)" + }, + "description": { + "type": "string", + "maxLength": 1024, + "description": "What the skill does and when to use it" + }, + "allowed-tools": { + "type": "string", + "description": "Comma-separated list of allowed tools" + }, + "version": { + "type": "string", + "pattern": "^\\d+\\.\\d+\\.\\d+$", + "description": "Semantic version (x.y.z)" + } + } +} diff --git a/skills/skill-adapter/assets/test-data.json b/skills/skill-adapter/assets/test-data.json new file mode 100644 index 0000000..f0cd871 --- /dev/null +++ b/skills/skill-adapter/assets/test-data.json @@ -0,0 +1,27 @@ +{ + "testCases": [ + { + "name": "Basic activation test", + "input": "trigger phrase example", + "expected": { + "activated": true, + "toolsUsed": ["Read", "Grep"], + "success": true + } + }, + { + "name": "Complex workflow test", + "input": "multi-step trigger example", + "expected": { + "activated": true, + "steps": 3, + "toolsUsed": ["Read", "Write", "Bash"], + "success": true + } + } + ], + "fixtures": { + "sampleInput": "example data", + "expectedOutput": "processed result" + } +} diff --git a/skills/skill-adapter/references/README.md b/skills/skill-adapter/references/README.md new file mode 100644 index 0000000..f7d0b88 --- /dev/null +++ b/skills/skill-adapter/references/README.md @@ -0,0 +1,9 @@ +# References + +Bundled resources for crypto-portfolio-tracker skill + +- [ ] crypto_risk_metrics.md: Detailed explanation of risk metrics like Sharpe ratio, Sortino ratio, and maximum drawdown. +- [ ] modern_portfolio_theory.md: Overview of Modern Portfolio Theory and its application to crypto portfolio optimization. +- [ ] exchange_api_documentation.md: Documentation for accessing real-time price data from CoinGecko, Binance, and Coinbase. +- [ ] tax_implications_crypto_trading.md: Guide to tax-aware trading suggestions for crypto rebalancing. +- [ ] portfolio_diversification_strategies.md: Strategies for improving portfolio diversification in the crypto space. diff --git a/skills/skill-adapter/references/best-practices.md b/skills/skill-adapter/references/best-practices.md new file mode 100644 index 0000000..3505048 --- /dev/null +++ b/skills/skill-adapter/references/best-practices.md @@ -0,0 +1,69 @@ +# Skill Best Practices + +Guidelines for optimal skill usage and development. + +## For Users + +### Activation Best Practices + +1. **Use Clear Trigger Phrases** + - Match phrases from skill description + - Be specific about intent + - Provide necessary context + +2. **Provide Sufficient Context** + - Include relevant file paths + - Specify scope of analysis + - Mention any constraints + +3. **Understand Tool Permissions** + - Check allowed-tools in frontmatter + - Know what the skill can/cannot do + - Request appropriate actions + +### Workflow Optimization + +- Start with simple requests +- Build up to complex workflows +- Verify each step before proceeding +- Use skill consistently for related tasks + +## For Developers + +### Skill Development Guidelines + +1. **Clear Descriptions** + - Include explicit trigger phrases + - Document all capabilities + - Specify limitations + +2. **Proper Tool Permissions** + - Use minimal necessary tools + - Document security implications + - Test with restricted tools + +3. **Comprehensive Documentation** + - Provide usage examples + - Document common pitfalls + - Include troubleshooting guide + +### Maintenance + +- Keep version updated +- Test after tool updates +- Monitor user feedback +- Iterate on descriptions + +## Performance Tips + +- Scope skills to specific domains +- Avoid overlapping trigger phrases +- Keep descriptions under 1024 chars +- Test activation reliability + +## Security Considerations + +- Never include secrets in skill files +- Validate all inputs +- Use read-only tools when possible +- Document security requirements diff --git a/skills/skill-adapter/references/examples.md b/skills/skill-adapter/references/examples.md new file mode 100644 index 0000000..b1d8bd2 --- /dev/null +++ b/skills/skill-adapter/references/examples.md @@ -0,0 +1,70 @@ +# Skill Usage Examples + +This document provides practical examples of how to use this skill effectively. + +## Basic Usage + +### Example 1: Simple Activation + +**User Request:** +``` +[Describe trigger phrase here] +``` + +**Skill Response:** +1. Analyzes the request +2. Performs the required action +3. Returns results + +### Example 2: Complex Workflow + +**User Request:** +``` +[Describe complex scenario] +``` + +**Workflow:** +1. Step 1: Initial analysis +2. Step 2: Data processing +3. Step 3: Result generation +4. Step 4: Validation + +## Advanced Patterns + +### Pattern 1: Chaining Operations + +Combine this skill with other tools: +``` +Step 1: Use this skill for [purpose] +Step 2: Chain with [other tool] +Step 3: Finalize with [action] +``` + +### Pattern 2: Error Handling + +If issues occur: +- Check trigger phrase matches +- Verify context is available +- Review allowed-tools permissions + +## Tips & Best Practices + +- ✅ Be specific with trigger phrases +- ✅ Provide necessary context +- ✅ Check tool permissions match needs +- ❌ Avoid vague requests +- ❌ Don't mix unrelated tasks + +## Common Issues + +**Issue:** Skill doesn't activate +**Solution:** Use exact trigger phrases from description + +**Issue:** Unexpected results +**Solution:** Check input format and context + +## See Also + +- Main SKILL.md for full documentation +- scripts/ for automation helpers +- assets/ for configuration examples diff --git a/skills/skill-adapter/scripts/README.md b/skills/skill-adapter/scripts/README.md new file mode 100644 index 0000000..68dc25a --- /dev/null +++ b/skills/skill-adapter/scripts/README.md @@ -0,0 +1,9 @@ +# Scripts + +Bundled resources for crypto-portfolio-tracker skill + +- [ ] calculate_risk_metrics.py: Calculates Sharpe ratio, Sortino ratio, and maximum drawdown for a given portfolio. +- [ ] rebalance_portfolio.py: Generates rebalancing recommendations based on Modern Portfolio Theory. +- [ ] set_price_alert.py: Sets up price alerts for specific cryptocurrencies and sends notifications. +- [ ] track_position.py: Tracks a new crypto position, including entry price, quantity, and date. +- [ ] validate_api_keys.py: Validates API keys for various exchanges (CoinGecko, Binance, Coinbase). diff --git a/skills/skill-adapter/scripts/helper-template.sh b/skills/skill-adapter/scripts/helper-template.sh new file mode 100755 index 0000000..c4aae90 --- /dev/null +++ b/skills/skill-adapter/scripts/helper-template.sh @@ -0,0 +1,42 @@ +#!/bin/bash +# Helper script template for skill automation +# Customize this for your skill's specific needs + +set -e + +function show_usage() { + echo "Usage: $0 [options]" + echo "" + echo "Options:" + echo " -h, --help Show this help message" + echo " -v, --verbose Enable verbose output" + echo "" +} + +# Parse arguments +VERBOSE=false + +while [[ $# -gt 0 ]]; do + case $1 in + -h|--help) + show_usage + exit 0 + ;; + -v|--verbose) + VERBOSE=true + shift + ;; + *) + echo "Unknown option: $1" + show_usage + exit 1 + ;; + esac +done + +# Your skill logic here +if [ "$VERBOSE" = true ]; then + echo "Running skill automation..." +fi + +echo "✅ Complete" diff --git a/skills/skill-adapter/scripts/validation.sh b/skills/skill-adapter/scripts/validation.sh new file mode 100755 index 0000000..590af58 --- /dev/null +++ b/skills/skill-adapter/scripts/validation.sh @@ -0,0 +1,32 @@ +#!/bin/bash +# Skill validation helper +# Validates skill activation and functionality + +set -e + +echo "🔍 Validating skill..." + +# Check if SKILL.md exists +if [ ! -f "../SKILL.md" ]; then + echo "❌ Error: SKILL.md not found" + exit 1 +fi + +# Validate frontmatter +if ! grep -q "^---$" "../SKILL.md"; then + echo "❌ Error: No frontmatter found" + exit 1 +fi + +# Check required fields +if ! grep -q "^name:" "../SKILL.md"; then + echo "❌ Error: Missing 'name' field" + exit 1 +fi + +if ! grep -q "^description:" "../SKILL.md"; then + echo "❌ Error: Missing 'description' field" + exit 1 +fi + +echo "✅ Skill validation passed"