UNPKG

@sei-code/analytics

Version:

Blockchain data analysis and monitoring for Sei network

359 lines (358 loc) 15.2 kB
import { BaseCapability } from '@sei-code/core'; export class PerformanceTracker extends BaseCapability { agent; precompiles; priceHistory = new Map(); benchmarks = []; constructor(agent, precompiles) { super('performance-tracker', 'Performance tracking and analysis'); this.agent = agent; this.precompiles = precompiles; this.initializeBenchmarks(); } async execute(params) { const { action, ...args } = params; switch (action) { case 'trackAssetPerformance': return this.trackAssetPerformance(args.denom, args.timeframes); case 'compareAssetPerformance': return this.compareAssetPerformance(args.denoms, args.timeframe); case 'generatePerformanceReport': return this.generatePerformanceReport(args.denom); default: throw new Error(`Unknown action: ${action}`); } } initializeBenchmarks() { this.benchmarks = [ { name: 'Sei Network', symbol: 'SEI', performance: [] }, { name: 'Cosmos Hub', symbol: 'ATOM', performance: [] } ]; } async trackAssetPerformance(denom, timeframes) { try { this.agent.emit('info', `Tracking performance for ${denom} across ${timeframes.length} timeframes`); const reports = []; for (const timeframe of timeframes) { const metrics = await this.calculatePerformanceMetrics(denom, timeframe); const benchmark = await this.getBenchmarkData(denom, timeframe); const ranking = await this.calculateRanking(denom, metrics); const insights = this.generateInsights(metrics, benchmark); reports.push({ asset: denom, timeframe, metrics, benchmark, ranking, insights }); } return reports; } catch (error) { this.agent.emit('error', `Failed to track performance: ${error.message}`); throw error; } } async calculatePerformanceMetrics(denom, period) { try { const lookbackSeconds = this.getLookbackSeconds(period); const [currentPrice, twapData] = await Promise.all([ this.precompiles.oracle.execute({ action: 'get_price', denom }), this.precompiles.oracle.execute({ action: 'get_twap', denom, lookbackSeconds }) ]); const priceHistory = await this.getPriceHistory(denom, period); if (priceHistory.length < 2) { return this.getDefaultMetrics(period); } const startPrice = priceHistory[0].price; const endPrice = currentPrice.price; const absoluteReturn = endPrice - startPrice; const percentageReturn = startPrice > 0 ? (absoluteReturn / startPrice) * 100 : 0; const volatility = this.calculateVolatility(priceHistory); const sharpeRatio = this.calculateSharpeRatio(percentageReturn, volatility); const maxDrawdown = this.calculateMaxDrawdown(priceHistory); const winRate = this.calculateWinRate(priceHistory); // Simplified alpha/beta calculation (would need market data for proper calculation) const alpha = percentageReturn > 0 ? Math.min(percentageReturn * 0.1, 5) : 0; const beta = volatility > 0 ? Math.min(volatility / 20, 2) : 1; return { period, absoluteReturn: absoluteReturn.toString(), percentageReturn, volatility, sharpeRatio, maxDrawdown, winRate, alpha, beta }; } catch (error) { this.agent.emit('warn', `Failed to calculate metrics for ${denom}: ${error.message}`); return this.getDefaultMetrics(period); } } getLookbackSeconds(period) { const lookbackMap = { '1h': 3600, '4h': 14400, '24h': 86400, '7d': 604800, '30d': 2592000 }; return lookbackMap[period] || 86400; } async getPriceHistory(denom, period) { // In a real implementation, this would fetch historical data from a price feed // For now, we'll simulate with TWAP data and current price try { const lookbackSeconds = this.getLookbackSeconds(period); const intervals = Math.min(period === '1h' ? 6 : period === '4h' ? 4 : period === '24h' ? 24 : 30, 50); const history = []; const currentTime = Date.now(); const intervalMs = (lookbackSeconds * 1000) / intervals; for (let i = intervals; i >= 0; i--) { const timestamp = new Date(currentTime - (i * intervalMs)).toISOString(); try { // Use TWAP as historical price approximation const twapData = await this.precompiles.oracle.execute({ action: 'get_twap', denom, lookbackSeconds: Math.max(3600, lookbackSeconds - (i * (lookbackSeconds / intervals))) }); history.push({ timestamp, price: twapData.price }); } catch (error) { // If TWAP fails, use interpolated price const basePrice = await this.precompiles.oracle.execute({ action: 'get_price', denom }); const randomVariation = (Math.random() - 0.5) * 0.1; // ±5% variation history.push({ timestamp, price: basePrice.price * (1 + randomVariation) }); } } return history; } catch (error) { return []; } } calculateVolatility(priceHistory) { if (priceHistory.length < 2) return 0; const returns = []; for (let i = 1; i < priceHistory.length; i++) { const returnVal = (priceHistory[i].price - priceHistory[i - 1].price) / priceHistory[i - 1].price; returns.push(returnVal); } 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; // Annualized volatility return Math.sqrt(variance) * Math.sqrt(365) * 100; } calculateSharpeRatio(return_, volatility, riskFreeRate = 3) { if (volatility === 0) return 0; return (return_ - riskFreeRate) / volatility; } calculateMaxDrawdown(priceHistory) { if (priceHistory.length < 2) return 0; let maxDrawdown = 0; let peak = priceHistory[0].price; for (const point of priceHistory) { if (point.price > peak) { peak = point.price; } const drawdown = (peak - point.price) / peak; if (drawdown > maxDrawdown) { maxDrawdown = drawdown; } } return maxDrawdown * 100; } calculateWinRate(priceHistory) { if (priceHistory.length < 2) return 0; let wins = 0; let totalMoves = 0; for (let i = 1; i < priceHistory.length; i++) { const priceChange = priceHistory[i].price - priceHistory[i - 1].price; if (priceChange > 0) wins++; totalMoves++; } return totalMoves > 0 ? (wins / totalMoves) * 100 : 0; } async getBenchmarkData(denom, timeframe) { // For SEI, compare against cosmos ecosystem const benchmarkDenom = denom === 'usei' ? 'uatom' : 'usei'; try { const benchmarkMetrics = await this.calculatePerformanceMetrics(benchmarkDenom, timeframe); return { name: benchmarkDenom === 'usei' ? 'Sei Network' : 'Cosmos Hub', symbol: benchmarkDenom === 'usei' ? 'SEI' : 'ATOM', performance: [benchmarkMetrics] }; } catch (error) { return { name: 'Market', symbol: 'MARKET', performance: [this.getDefaultMetrics(timeframe)] }; } } async calculateRanking(denom, metrics) { // Simplified ranking system (would compare against universe of assets in practice) const performancePercentile = Math.min(Math.max((metrics.percentageReturn + 50) / 100 * 100, 0), 100); const volatilityPercentile = Math.min(Math.max(100 - (metrics.volatility / 2), 0), 100); const riskAdjustedPercentile = Math.min(Math.max((metrics.sharpeRatio + 2) / 4 * 100, 0), 100); return { performance: Math.round(performancePercentile), volatility: Math.round(volatilityPercentile), riskAdjustedReturn: Math.round(riskAdjustedPercentile) }; } generateInsights(metrics, benchmark) { const insights = []; const benchmarkMetric = benchmark.performance[0]; // Performance insights if (metrics.percentageReturn > benchmarkMetric.percentageReturn) { insights.push(`📈 Outperforming benchmark by ${(metrics.percentageReturn - benchmarkMetric.percentageReturn).toFixed(2)}%`); } else { insights.push(`📉 Underperforming benchmark by ${(benchmarkMetric.percentageReturn - metrics.percentageReturn).toFixed(2)}%`); } // Volatility insights if (metrics.volatility < benchmarkMetric.volatility) { insights.push(`📊 Lower volatility than benchmark (${metrics.volatility.toFixed(1)}% vs ${benchmarkMetric.volatility.toFixed(1)}%)`); } else { insights.push(`⚡ Higher volatility than benchmark (${metrics.volatility.toFixed(1)}% vs ${benchmarkMetric.volatility.toFixed(1)}%)`); } // Risk-adjusted performance if (metrics.sharpeRatio > 1) { insights.push(`✅ Strong risk-adjusted returns (Sharpe: ${metrics.sharpeRatio.toFixed(2)})`); } else if (metrics.sharpeRatio < 0) { insights.push(`⚠️ Poor risk-adjusted returns (Sharpe: ${metrics.sharpeRatio.toFixed(2)})`); } // Win rate insights if (metrics.winRate > 60) { insights.push(`🎯 High win rate of ${metrics.winRate.toFixed(1)}%`); } else if (metrics.winRate < 40) { insights.push(`📉 Low win rate of ${metrics.winRate.toFixed(1)}%`); } // Drawdown insights if (metrics.maxDrawdown > 20) { insights.push(`⚠️ High maximum drawdown of ${metrics.maxDrawdown.toFixed(1)}%`); } else if (metrics.maxDrawdown < 5) { insights.push(`🛡️ Low maximum drawdown of ${metrics.maxDrawdown.toFixed(1)}%`); } return insights; } getDefaultMetrics(period) { return { period, absoluteReturn: '0', percentageReturn: 0, volatility: 0, sharpeRatio: 0, maxDrawdown: 0, winRate: 0, alpha: 0, beta: 1 }; } async compareAssetPerformance(denoms, timeframe) { try { const results = await Promise.all(denoms.map(async (denom) => ({ asset: denom, metrics: await this.calculatePerformanceMetrics(denom, timeframe) }))); // Sort by percentage return const sorted = results.sort((a, b) => b.metrics.percentageReturn - a.metrics.percentageReturn); const ranking = sorted.map((result, index) => ({ ...result, rank: index + 1 })); const returns = results.map(r => r.metrics.percentageReturn); const volatilities = results.map(r => r.metrics.volatility); return { ranking, summary: { bestPerformer: sorted[0]?.asset || '', worstPerformer: sorted[sorted.length - 1]?.asset || '', averageReturn: returns.reduce((sum, r) => sum + r, 0) / returns.length, averageVolatility: volatilities.reduce((sum, v) => sum + v, 0) / volatilities.length } }; } catch (error) { this.agent.emit('error', `Failed to compare asset performance: ${error.message}`); throw error; } } async generatePerformanceReport(denom) { try { const timeframes = ['1h', '4h', '24h', '7d', '30d']; const currentPrice = await this.precompiles.oracle.execute({ action: 'get_price', denom }); const allTimeframes = await this.trackAssetPerformance(denom, timeframes); // Analyze trend const shortTerm = allTimeframes.find(r => r.timeframe === '24h')?.metrics.percentageReturn || 0; const mediumTerm = allTimeframes.find(r => r.timeframe === '7d')?.metrics.percentageReturn || 0; const longTerm = allTimeframes.find(r => r.timeframe === '30d')?.metrics.percentageReturn || 0; const avgReturn = (shortTerm + mediumTerm + longTerm) / 3; let trend = 'sideways'; let strength = 'weak'; if (avgReturn > 2) { trend = 'bullish'; strength = avgReturn > 10 ? 'strong' : avgReturn > 5 ? 'moderate' : 'weak'; } else if (avgReturn < -2) { trend = 'bearish'; strength = avgReturn < -10 ? 'strong' : avgReturn < -5 ? 'moderate' : 'weak'; } let recommendation = ''; if (trend === 'bullish' && strength !== 'weak') { recommendation = 'Consider increasing allocation - showing strong upward momentum'; } else if (trend === 'bearish' && strength !== 'weak') { recommendation = 'Consider reducing exposure - showing strong downward momentum'; } else { recommendation = 'Monitor closely - trend is unclear or weak'; } return { asset: denom, currentPrice: currentPrice.price.toString(), allTimeframes, summary: { trend, strength, recommendation } }; } catch (error) { this.agent.emit('error', `Failed to generate performance report: ${error.message}`); throw error; } } }