您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Advanced market analysis tool for RugPlay with modern UI, comprehensive analysis and enhanced security metrics - Fixed Delete Button
// ==UserScript== // @name RugPlay Market Analyzer - Enhanced v2.0 Fixed // @namespace http://tampermonkey.net/ // @version 2.0.1 // @description Advanced market analysis tool for RugPlay with modern UI, comprehensive analysis and enhanced security metrics - Fixed Delete Button // @author seltonmt012 // @match https://rugplay.com/* // @grant GM_setValue // @grant GM_getValue // @grant GM_addStyle // @grant GM_xmlhttpRequest // @connect rugplay.com // @license Seltonmt // ==/UserScript== (function() { 'use strict'; // =========================== // Configuration & Storage // =========================== const getApiKey = () => GM_getValue('rugplay_api_key', ''); const saveApiKey = (key) => GM_setValue('rugplay_api_key', key); const API_BASE_URL = 'https://rugplay.com/api/v1'; const getPortfolio = () => GM_getValue('rugplay_portfolio', {}); const savePortfolio = (portfolio) => { try { console.log('Saving portfolio data:', portfolio); GM_setValue('rugplay_portfolio', portfolio); console.log('Portfolio saved successfully'); } catch (error) { console.error('Error saving portfolio:', error); showNotification('Error saving data: ' + error.message, 'error'); } }; const addTransaction = (symbol, quantity, price, date = new Date()) => { const portfolio = getPortfolio(); if (!portfolio[symbol]) { portfolio[symbol] = { transactions: [], notes: '' }; } portfolio[symbol].transactions.push({ id: Date.now() + Math.random().toString(36).substring(2, 9), quantity: parseFloat(quantity), price: parseFloat(price), date: date.toISOString(), type: quantity > 0 ? 'buy' : 'sell' }); savePortfolio(portfolio); updateUI(); }; const deleteTransaction = (symbol, transactionId) => { try { console.log(`🗑️ Deleting transaction: ${symbol}, ${transactionId}`); const portfolio = getPortfolio(); if (!portfolio[symbol] || !portfolio[symbol].transactions) { console.error('No transactions found for symbol:', symbol); return false; } const transactionIndex = portfolio[symbol].transactions.findIndex(tx => tx.id === transactionId); if (transactionIndex === -1) { console.error('Transaction not found:', transactionId); return false; } portfolio[symbol].transactions.splice(transactionIndex, 1); if (portfolio[symbol].transactions.length === 0) { delete portfolio[symbol]; } savePortfolio(portfolio); console.log('✅ Transaction deleted successfully'); return true; } catch (error) { console.error('Error deleting transaction:', error); return false; } }; const calculateHoldings = (symbol) => { const portfolio = getPortfolio(); if (!portfolio[symbol]) return { quantity: 0, avgPrice: 0 }; let totalQuantity = 0; let totalCost = 0; portfolio[symbol].transactions.forEach(tx => { if (tx.type === 'buy') { totalCost += (tx.quantity * tx.price); totalQuantity += tx.quantity; } else { const avgCostPerCoin = totalCost / totalQuantity; totalCost -= (tx.quantity * avgCostPerCoin); totalQuantity -= tx.quantity; } }); return { quantity: totalQuantity, avgPrice: totalQuantity > 0 ? totalCost / totalQuantity : 0 }; }; // =========================== // API Functions // =========================== const fetchCoinData = async (symbol) => { const apiKey = getApiKey(); if (!apiKey) throw new Error('API key not set'); return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: 'GET', url: `${API_BASE_URL}/coin/${symbol}`, headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json' }, onload: (response) => { if (response.status === 200) { resolve(JSON.parse(response.responseText)); } else { reject(`Error: ${response.statusText}`); } }, onerror: (error) => reject(`Network error: ${error}`) }); }); }; const fetchCoinHolders = async (symbol) => { const apiKey = getApiKey(); if (!apiKey) throw new Error('API key not set'); return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: 'GET', url: `${API_BASE_URL}/holders/${symbol}?limit=100`, headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json' }, onload: (response) => { if (response.status === 200) { resolve(JSON.parse(response.responseText)); } else { reject(`Error: ${response.statusText}`); } }, onerror: (error) => reject(`Network error: ${error}`) }); }); }; const searchCoins = async (searchTerm) => { const apiKey = getApiKey(); if (!apiKey) throw new Error('API key not set'); return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: 'GET', url: `${API_BASE_URL}/market?search=${encodeURIComponent(searchTerm)}`, headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json' }, onload: (response) => { if (response.status === 200) { resolve(JSON.parse(response.responseText)); } else { reject(`Error: ${response.statusText}`); } }, onerror: (error) => reject(`Network error: ${error}`) }); }); }; // =========================== // Enhanced Analysis Functions // =========================== const analyzePrice = (candlesticks) => { if (!candlesticks || candlesticks.length < 10) { return { trend: 'UNKNOWN', trendPercentage: 0, message: 'Insufficient data', confidence: 0 }; } const recentCandles = candlesticks.slice(-20); const firstPrice = recentCandles[0].open; const lastPrice = recentCandles[recentCandles.length - 1].close; const changePercentage = ((lastPrice - firstPrice) / firstPrice) * 100; // Calculate volatility const prices = recentCandles.map(c => c.close); const avgPrice = prices.reduce((a, b) => a + b) / prices.length; const volatility = Math.sqrt(prices.reduce((sum, price) => sum + Math.pow(price - avgPrice, 2), 0) / prices.length); const volatilityPercent = (volatility / avgPrice) * 100; // Calculate momentum const shortMA = prices.slice(-5).reduce((a, b) => a + b) / 5; const longMA = prices.slice(-10).reduce((a, b) => a + b) / 10; const momentum = ((shortMA - longMA) / longMA) * 100; let trend, message, confidence; if (changePercentage > 50) { trend = 'STRONG_UP'; message = `🚀 Strong bullish trend (+${changePercentage.toFixed(1)}%)`; confidence = Math.min(95, 70 + Math.abs(changePercentage) / 2); } else if (changePercentage > 20) { trend = 'UP'; message = `📈 Bullish trend (+${changePercentage.toFixed(1)}%)`; confidence = Math.min(85, 60 + Math.abs(changePercentage)); } else if (changePercentage < -30) { trend = 'STRONG_DOWN'; message = `📉 Strong bearish trend (${changePercentage.toFixed(1)}%)`; confidence = Math.min(90, 65 + Math.abs(changePercentage) / 2); } else if (changePercentage < -10) { trend = 'DOWN'; message = `⬇️ Bearish trend (${changePercentage.toFixed(1)}%)`; confidence = Math.min(80, 55 + Math.abs(changePercentage)); } else { trend = 'NEUTRAL'; message = `↔️ Sideways movement (${changePercentage.toFixed(1)}%)`; confidence = 50; } return { trend, trendPercentage: changePercentage, message, confidence, volatility: volatilityPercent, momentum }; }; const analyzeSecurity = (coinData, holdersData) => { if (!holdersData || !holdersData.holders || !coinData) { return { securityScore: 0, level: 'UNKNOWN', factors: [], recommendation: 'Insufficient data' }; } let securityScore = 100; const factors = []; // Holder concentration analysis const topHolder = holdersData.holders[0]; const topHolderPercentage = topHolder ? topHolder.percentage : 0; if (topHolderPercentage > 80) { securityScore -= 40; factors.push({ type: 'critical', message: `🚨 Extreme concentration: Top holder owns ${topHolderPercentage.toFixed(2)}%`, impact: -40 }); } else if (topHolderPercentage > 50) { securityScore -= 25; factors.push({ type: 'high', message: `⚠️ High concentration: Top holder owns ${topHolderPercentage.toFixed(2)}%`, impact: -25 }); } else if (topHolderPercentage > 30) { securityScore -= 15; factors.push({ type: 'medium', message: `⚠️ Moderate concentration: Top holder owns ${topHolderPercentage.toFixed(2)}%`, impact: -15 }); } // Top 10 holders analysis const top10Holders = holdersData.holders.slice(0, 10); const top10Percentage = top10Holders.reduce((sum, holder) => sum + holder.percentage, 0); if (top10Percentage > 95) { securityScore -= 30; factors.push({ type: 'critical', message: `🚨 Top 10 holders control ${top10Percentage.toFixed(2)}%`, impact: -30 }); } else if (top10Percentage > 80) { securityScore -= 20; factors.push({ type: 'high', message: `⚠️ Top 10 holders control ${top10Percentage.toFixed(2)}%`, impact: -20 }); } // Liquidity analysis const poolPercentage = (holdersData.poolInfo.coinAmount / holdersData.circulatingSupply) * 100; if (poolPercentage < 1) { securityScore -= 25; factors.push({ type: 'critical', message: `🚨 Very low liquidity: ${poolPercentage.toFixed(3)}% in pool`, impact: -25 }); } else if (poolPercentage < 5) { securityScore -= 15; factors.push({ type: 'medium', message: `⚠️ Low liquidity: ${poolPercentage.toFixed(2)}% in pool`, impact: -15 }); } // Age analysis const creationDate = new Date(coinData.coin.createdAt); const ageInDays = (new Date() - creationDate) / (1000 * 60 * 60 * 24); if (ageInDays < 1) { securityScore -= 20; factors.push({ type: 'high', message: `🕐 Very new project (${ageInDays.toFixed(1)} days old)`, impact: -20 }); } else if (ageInDays < 7) { securityScore -= 10; factors.push({ type: 'medium', message: `🕐 New project (${ageInDays.toFixed(1)} days old)`, impact: -10 }); } else if (ageInDays > 30) { securityScore += 5; factors.push({ type: 'positive', message: `✅ Established project (${ageInDays.toFixed(0)} days old)`, impact: +5 }); } // Volume analysis const volumeToMarketCap = (coinData.coin.volume24h / coinData.coin.marketCap) * 100; if (volumeToMarketCap > 50) { securityScore -= 10; factors.push({ type: 'medium', message: `⚠️ Extremely high volume ratio: ${volumeToMarketCap.toFixed(2)}%`, impact: -10 }); } else if (volumeToMarketCap > 20) { securityScore -= 5; factors.push({ type: 'low', message: `⚠️ High volume ratio: ${volumeToMarketCap.toFixed(2)}%`, impact: -5 }); } securityScore = Math.max(0, Math.min(100, securityScore)); let level, recommendation; if (securityScore >= 80) { level = 'HIGH'; recommendation = '✅ Low risk - Appears relatively safe'; } else if (securityScore >= 60) { level = 'MEDIUM'; recommendation = '⚠️ Medium risk - Exercise caution'; } else if (securityScore >= 40) { level = 'LOW'; recommendation = '🔶 High risk - Be very careful'; } else { level = 'CRITICAL'; recommendation = '🚨 Critical risk - Avoid or minimal exposure'; } return { securityScore, level, factors, recommendation, holderAnalysis: { topHolder: topHolderPercentage, top10Holders: top10Percentage, poolPercentage, totalHolders: holdersData.holders.length } }; }; const analyzeActivity = (coinData, holdersData) => { const coin = coinData.coin; let activityScore = 0; const factors = []; // Volume activity const volumeScore = Math.min(30, (coin.volume24h / 10000) * 10); activityScore += volumeScore; factors.push({ metric: 'Trading Volume', value: `$${formatNumber(coin.volume24h)}`, score: volumeScore, maxScore: 30 }); // Market cap activity const mcapScore = Math.min(25, (coin.marketCap / 100000) * 5); activityScore += mcapScore; factors.push({ metric: 'Market Cap', value: `$${formatNumber(coin.marketCap)}`, score: mcapScore, maxScore: 25 }); // Holder count activity const holderScore = Math.min(25, (holdersData.holders.length / 10) * 2); activityScore += holderScore; factors.push({ metric: 'Holder Count', value: holdersData.holders.length.toString(), score: holderScore, maxScore: 25 }); // Price volatility as activity indicator const priceChange = Math.abs((coin.change24h / (coin.currentPrice - coin.change24h)) * 100); const volatilityScore = Math.min(20, priceChange / 2); activityScore += volatilityScore; factors.push({ metric: 'Price Volatility', value: `${priceChange.toFixed(2)}%`, score: volatilityScore, maxScore: 20 }); activityScore = Math.min(100, activityScore); let level; if (activityScore >= 80) level = 'VERY_HIGH'; else if (activityScore >= 60) level = 'HIGH'; else if (activityScore >= 40) level = 'MEDIUM'; else if (activityScore >= 20) level = 'LOW'; else level = 'VERY_LOW'; return { activityScore, level, factors }; }; const analyzeProfitability = (coinData, holdings) => { const coin = coinData.coin; let profitScore = 50; // Start neutral const factors = []; // Price performance const priceChange = (coin.change24h / (coin.currentPrice - coin.change24h)) * 100; const performanceScore = Math.min(25, Math.max(-25, priceChange)); profitScore += performanceScore; factors.push({ metric: '24h Performance', value: `${priceChange >= 0 ? '+' : ''}${priceChange.toFixed(2)}%`, score: performanceScore, impact: performanceScore >= 0 ? 'positive' : 'negative' }); // Volume/Market Cap ratio (liquidity premium) const volumeRatio = (coin.volume24h / coin.marketCap) * 100; const liquidityScore = Math.min(15, volumeRatio * 2) - 10; profitScore += liquidityScore; factors.push({ metric: 'Liquidity Ratio', value: `${volumeRatio.toFixed(2)}%`, score: liquidityScore, impact: liquidityScore >= 0 ? 'positive' : 'negative' }); // Personal holdings performance if (holdings.quantity > 0) { const currentValue = holdings.quantity * coin.currentPrice; const costBasis = holdings.quantity * holdings.avgPrice; const personalReturn = ((currentValue - costBasis) / costBasis) * 100; const personalScore = Math.min(25, Math.max(-25, personalReturn)); profitScore += personalScore; factors.push({ metric: 'Your P&L', value: `${personalReturn >= 0 ? '+' : ''}${personalReturn.toFixed(2)}%`, score: personalScore, impact: personalScore >= 0 ? 'positive' : 'negative' }); } // Market cap potential const mcapScore = coin.marketCap < 100000 ? 10 : coin.marketCap < 1000000 ? 5 : 0; profitScore += mcapScore; if (mcapScore > 0) { factors.push({ metric: 'Growth Potential', value: 'Small Cap', score: mcapScore, impact: 'positive' }); } profitScore = Math.max(0, Math.min(100, profitScore)); let level; if (profitScore >= 80) level = 'EXCELLENT'; else if (profitScore >= 65) level = 'GOOD'; else if (profitScore >= 50) level = 'NEUTRAL'; else if (profitScore >= 35) level = 'POOR'; else level = 'VERY_POOR'; return { profitScore, level, factors }; }; // =========================== // Modern UI Styles // =========================== const addStyles = () => { GM_addStyle(` @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap'); .rp-analyzer { position: fixed; top: 20px; right: 20px; width: 650px; max-height: 90vh; overflow-y: auto; background: rgba(15, 15, 15, 0.95); backdrop-filter: blur(20px); border: 1px solid rgba(255, 255, 255, 0.1); border-radius: 16px; box-shadow: 0 20px 40px rgba(0, 0, 0, 0.8), 0 0 0 1px rgba(255, 255, 255, 0.05); color: #ffffff; font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; z-index: 10000; padding: 0; scrollbar-width: thin; scrollbar-color: rgba(255, 255, 255, 0.2) transparent; } .rp-analyzer::-webkit-scrollbar { width: 6px; } .rp-analyzer::-webkit-scrollbar-track { background: transparent; } .rp-analyzer::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.2); border-radius: 3px; } .rp-analyzer::-webkit-scrollbar-thumb:hover { background: rgba(255, 255, 255, 0.3); } .rp-analyzer-header { display: flex; justify-content: space-between; align-items: center; padding: 20px 24px; border-bottom: 1px solid rgba(255, 255, 255, 0.1); background: rgba(255, 255, 255, 0.02); border-radius: 16px 16px 0 0; } .rp-analyzer-title { font-size: 18px; font-weight: 600; color: #00d4ff; display: flex; align-items: center; gap: 8px; } .rp-analyzer-close { cursor: pointer; color: rgba(255, 255, 255, 0.6); font-size: 24px; line-height: 1; padding: 4px; border-radius: 6px; transition: all 0.2s ease; } .rp-analyzer-close:hover { color: #ff4757; background: rgba(255, 71, 87, 0.1); } .rp-analyzer-tabs { display: flex; padding: 0 24px; background: rgba(255, 255, 255, 0.02); border-bottom: 1px solid rgba(255, 255, 255, 0.1); gap: 4px; } .rp-analyzer-tab { padding: 12px 16px; cursor: pointer; font-size: 14px; font-weight: 500; border-radius: 8px 8px 0 0; background: transparent; color: rgba(255, 255, 255, 0.7); transition: all 0.2s ease; position: relative; display: flex; align-items: center; gap: 6px; } .rp-analyzer-tab:hover { color: #ffffff; background: rgba(255, 255, 255, 0.05); } .rp-analyzer-tab.active { background: linear-gradient(135deg, rgba(0, 212, 255, 0.1), rgba(0, 150, 255, 0.1)); color: #00d4ff; border-bottom: 2px solid #00d4ff; } .rp-analyzer-content { padding: 24px; background: transparent; } .rp-analyzer-section { margin-bottom: 24px; background: rgba(255, 255, 255, 0.03); border: 1px solid rgba(255, 255, 255, 0.08); border-radius: 12px; padding: 20px; backdrop-filter: blur(10px); } .rp-analyzer-section-title { font-weight: 600; font-size: 16px; margin-bottom: 16px; color: #00d4ff; display: flex; align-items: center; gap: 8px; } .rp-score-card { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 16px; margin-bottom: 20px; } .rp-score-item { background: rgba(255, 255, 255, 0.05); border: 1px solid rgba(255, 255, 255, 0.1); border-radius: 10px; padding: 16px; text-align: center; transition: all 0.2s ease; } .rp-score-item:hover { transform: translateY(-2px); box-shadow: 0 8px 25px rgba(0, 0, 0, 0.3); } .rp-score-value { font-size: 24px; font-weight: 700; margin-bottom: 4px; } .rp-score-label { font-size: 12px; color: rgba(255, 255, 255, 0.7); text-transform: uppercase; letter-spacing: 0.5px; } .rp-score-excellent { color: #00ff88; } .rp-score-good { color: #00d4ff; } .rp-score-neutral { color: #ffa500; } .rp-score-poor { color: #ff6b6b; } .rp-score-critical { color: #ff4757; } .rp-analyzer-data-row { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; padding: 8px 0; border-bottom: 1px solid rgba(255, 255, 255, 0.05); } .rp-analyzer-data-row:last-child { border-bottom: none; margin-bottom: 0; } .rp-analyzer-label { color: rgba(255, 255, 255, 0.8); font-size: 14px; } .rp-analyzer-value { font-weight: 600; font-size: 14px; color: #ffffff; } .rp-analyzer-input { width: 100%; padding: 12px 16px; background: rgba(255, 255, 255, 0.05); border: 1px solid rgba(255, 255, 255, 0.15); border-radius: 8px; color: #ffffff; margin-bottom: 12px; font-size: 14px; font-family: inherit; transition: all 0.2s ease; } .rp-analyzer-input:focus { outline: none; border-color: #00d4ff; box-shadow: 0 0 0 3px rgba(0, 212, 255, 0.1); } .rp-analyzer-input::placeholder { color: rgba(255, 255, 255, 0.5); } .rp-analyzer-button { background: linear-gradient(135deg, #00d4ff, #0096ff); color: #000; padding: 10px 20px; border: none; border-radius: 8px; cursor: pointer; font-weight: 600; font-size: 14px; margin-right: 8px; margin-bottom: 8px; transition: all 0.2s ease; font-family: inherit; } .rp-analyzer-button:hover { transform: translateY(-1px); box-shadow: 0 4px 15px rgba(0, 212, 255, 0.3); } .rp-analyzer-button:disabled { opacity: 0.6; cursor: not-allowed; transform: none; } .rp-analyzer-button.rp-delete { background: linear-gradient(135deg, #ff4757, #ff3742); padding: 6px 12px; font-size: 12px; color: #fff; } .rp-analyzer-button.rp-delete:hover:not(:disabled) { box-shadow: 0 4px 15px rgba(255, 71, 87, 0.3); } .rp-positive { color: #00ff88; } .rp-negative { color: #ff6b6b; } .rp-warning { color: #ffa500; } .rp-neutral { color: #00d4ff; } .rp-toggle-button { position: fixed; bottom: 24px; right: 24px; width: 60px; height: 60px; border-radius: 50%; background: linear-gradient(135deg, #00d4ff, #0096ff); color: #000; display: flex; align-items: center; justify-content: center; font-size: 24px; cursor: pointer; box-shadow: 0 8px 30px rgba(0, 212, 255, 0.4); z-index: 10001; transition: all 0.3s ease; border: none; } .rp-toggle-button:hover { transform: scale(1.1); box-shadow: 0 12px 40px rgba(0, 212, 255, 0.5); } .rp-notification { position: fixed; bottom: 100px; right: 24px; background: rgba(15, 15, 15, 0.95); backdrop-filter: blur(20px); color: #fff; padding: 16px 20px; border-radius: 12px; box-shadow: 0 8px 30px rgba(0, 0, 0, 0.5); z-index: 10002; font-weight: 500; transition: all 0.3s ease; border-left: 4px solid #00d4ff; max-width: 300px; } .rp-notification.rp-error { border-left-color: #ff4757; } .rp-notification.rp-success { border-left-color: #00ff88; } .rp-notification.rp-warning { border-left-color: #ffa500; } .rp-prediction-card { background: linear-gradient(135deg, rgba(0, 212, 255, 0.1), rgba(0, 150, 255, 0.05)); border: 1px solid rgba(0, 212, 255, 0.2); border-radius: 12px; padding: 20px; margin-bottom: 20px; text-align: center; } .rp-prediction-icon { font-size: 32px; margin-bottom: 12px; display: block; } .rp-prediction-text { font-size: 16px; font-weight: 600; margin-bottom: 8px; } .rp-prediction-confidence { font-size: 12px; color: rgba(255, 255, 255, 0.7); } .rp-risk-analysis { background: rgba(255, 71, 87, 0.05); border: 1px solid rgba(255, 71, 87, 0.2); border-radius: 12px; padding: 20px; margin-bottom: 20px; } .rp-risk-title { font-size: 16px; font-weight: 600; margin-bottom: 16px; display: flex; align-items: center; gap: 8px; } .rp-risk-factors { display: flex; flex-direction: column; gap: 8px; margin-bottom: 16px; } .rp-risk-factor { background: rgba(255, 255, 255, 0.05); border-radius: 8px; padding: 12px; display: flex; justify-content: space-between; align-items: center; font-size: 14px; } .rp-risk-impact { font-size: 12px; padding: 4px 8px; border-radius: 6px; font-weight: 600; } .rp-risk-impact.critical { background: rgba(255, 71, 87, 0.2); color: #ff4757; } .rp-risk-impact.high { background: rgba(255, 107, 107, 0.2); color: #ff6b6b; } .rp-risk-impact.medium { background: rgba(255, 165, 0, 0.2); color: #ffa500; } .rp-risk-impact.positive { background: rgba(0, 255, 136, 0.2); color: #00ff88; } .rp-empty-state { text-align: center; padding: 40px 20px; color: rgba(255, 255, 255, 0.6); } .rp-empty-state-icon { font-size: 48px; margin-bottom: 16px; opacity: 0.5; } .rp-progress-bar { width: 100%; height: 8px; background: rgba(255, 255, 255, 0.1); border-radius: 4px; overflow: hidden; margin-bottom: 8px; } .rp-progress-fill { height: 100%; border-radius: 4px; transition: width 0.3s ease; } .rp-progress-excellent { background: linear-gradient(90deg, #00ff88, #00d4aa); } .rp-progress-good { background: linear-gradient(90deg, #00d4ff, #0096ff); } .rp-progress-neutral { background: linear-gradient(90deg, #ffa500, #ff8c00); } .rp-progress-poor { background: linear-gradient(90deg, #ff6b6b, #ff5252); } .rp-progress-critical { background: linear-gradient(90deg, #ff4757, #ff3742); } .rp-transaction-table { width: 100%; border-collapse: collapse; margin-top: 16px; font-size: 13px; background: rgba(255, 255, 255, 0.02); border-radius: 8px; overflow: hidden; } .rp-transaction-table th, .rp-transaction-table td { padding: 10px 8px; text-align: left; border-bottom: 1px solid rgba(255, 255, 255, 0.05); word-wrap: break-word; max-width: 0; } .rp-transaction-table th { background: rgba(255, 255, 255, 0.05); color: #00d4ff; font-weight: 600; text-transform: uppercase; font-size: 11px; letter-spacing: 0.5px; white-space: nowrap; } .rp-transaction-table td { font-size: 12px; } .rp-transaction-table th:nth-child(1) { width: 15%; } /* Date */ .rp-transaction-table th:nth-child(2) { width: 12%; } /* Coin */ .rp-transaction-table th:nth-child(3) { width: 8%; } /* Type */ .rp-transaction-table th:nth-child(4) { width: 15%; } /* Quantity */ .rp-transaction-table th:nth-child(5) { width: 12%; } /* Price */ .rp-transaction-table th:nth-child(6) { width: 15%; } /* Total */ .rp-transaction-table th:nth-child(7) { width: 23%; } /* Actions */ .rp-transaction-table tr:hover { background: rgba(255, 255, 255, 0.03); } .rp-transaction-date { font-size: 11px; color: rgba(255, 255, 255, 0.8); } .rp-transaction-coin { font-weight: 600; color: #00d4ff; } .rp-transaction-type { font-weight: 600; text-transform: uppercase; font-size: 11px; padding: 2px 6px; border-radius: 4px; display: inline-block; } .rp-transaction-type.buy { background: rgba(0, 255, 136, 0.2); color: #00ff88; } .rp-transaction-type.sell { background: rgba(255, 107, 107, 0.2); color: #ff6b6b; } .rp-modal-backdrop { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.8); backdrop-filter: blur(10px); z-index: 10005; display: flex; justify-content: center; align-items: center; } .rp-modal { background: rgba(15, 15, 15, 0.95); backdrop-filter: blur(20px); border: 1px solid rgba(255, 255, 255, 0.1); padding: 24px; border-radius: 16px; max-width: 450px; width: 90%; box-shadow: 0 20px 40px rgba(0, 0, 0, 0.8); } .rp-modal-title { font-size: 18px; font-weight: 600; margin-bottom: 16px; color: #00d4ff; display: flex; align-items: center; gap: 8px; } .rp-modal-content { margin-bottom: 24px; color: rgba(255, 255, 255, 0.9); line-height: 1.5; } .rp-modal-actions { display: flex; justify-content: flex-end; gap: 12px; } .rp-chart-placeholder { width: 100%; height: 120px; background: rgba(255, 255, 255, 0.05); border: 1px dashed rgba(255, 255, 255, 0.2); border-radius: 8px; display: flex; align-items: center; justify-content: center; color: rgba(255, 255, 255, 0.5); font-size: 14px; margin: 16px 0; } .rp-metric-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 12px; margin: 16px 0; } .rp-metric-item { background: rgba(255, 255, 255, 0.03); border: 1px solid rgba(255, 255, 255, 0.08); border-radius: 8px; padding: 12px; } .rp-metric-title { font-size: 12px; color: rgba(255, 255, 255, 0.7); margin-bottom: 4px; text-transform: uppercase; letter-spacing: 0.5px; } .rp-metric-value { font-size: 16px; font-weight: 600; color: #ffffff; } @media (max-width: 768px) { .rp-analyzer { width: 95%; right: 2.5%; max-height: 85vh; } .rp-score-card { grid-template-columns: repeat(2, 1fr); } .rp-metric-grid { grid-template-columns: 1fr; } } @keyframes rp-spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } .rp-loading { width: 40px; height: 40px; border: 4px solid rgba(255, 255, 255, 0.1); border-top: 4px solid #00d4ff; border-radius: 50%; animation: rp-spin 1s linear infinite; margin: 20px auto; } `); }; // =========================== // UI Creation Functions // =========================== const createUI = () => { createToggleButton(); const container = document.createElement('div'); container.className = 'rp-analyzer'; container.style.display = 'none'; container.id = 'rp-analyzer-container'; const header = document.createElement('div'); header.className = 'rp-analyzer-header'; const title = document.createElement('div'); title.className = 'rp-analyzer-title'; title.innerHTML = '📊 RugPlay Analyzer Pro'; const closeButton = document.createElement('div'); closeButton.className = 'rp-analyzer-close'; closeButton.innerHTML = '×'; closeButton.addEventListener('click', toggleAnalyzer); header.appendChild(title); header.appendChild(closeButton); const tabs = document.createElement('div'); tabs.className = 'rp-analyzer-tabs'; const tabsData = [ { key: 'analysis', label: '🔍 Analysis', icon: '🔍' }, { key: 'portfolio', label: '💼 Portfolio', icon: '💼' }, { key: 'transactions', label: '📝 Transactions', icon: '📝' }, { key: 'search', label: '🔎 Search', icon: '🔎' }, { key: 'settings', label: '⚙️ Settings', icon: '⚙️' } ]; tabsData.forEach((tab, index) => { const tabElement = document.createElement('div'); tabElement.className = `rp-analyzer-tab ${index === 0 ? 'active' : ''}`; tabElement.innerHTML = `${tab.icon} ${tab.label}`; tabElement.dataset.tab = tab.key; tabElement.addEventListener('click', (e) => switchTab(e.target)); tabs.appendChild(tabElement); }); const content = document.createElement('div'); content.className = 'rp-analyzer-content'; content.id = 'rp-analyzer-content'; container.appendChild(header); container.appendChild(tabs); container.appendChild(content); document.body.appendChild(container); // Initialize with settings check const apiKey = getApiKey(); if (!apiKey) { switchTab(document.querySelector('[data-tab="settings"]')); } else { updateAnalysisTab(); } // Auto-show on transactions page if (window.location.href.includes('rugplay.com/transactions')) { setTimeout(() => { if (container.style.display === 'none') { toggleAnalyzer(); switchTab(document.querySelector('[data-tab="transactions"]')); } }, 1000); } }; const createToggleButton = () => { const existingButton = document.getElementById('rp-toggle-button'); if (existingButton) existingButton.remove(); const toggleButton = document.createElement('div'); toggleButton.className = 'rp-toggle-button'; toggleButton.id = 'rp-toggle-button'; toggleButton.innerHTML = '📊'; toggleButton.title = 'RugPlay Analyzer Pro'; toggleButton.addEventListener('click', toggleAnalyzer); document.body.appendChild(toggleButton); }; const switchTab = (tabElement) => { document.querySelectorAll('.rp-analyzer-tab').forEach(tab => { tab.classList.remove('active'); }); tabElement.classList.add('active'); const tabName = tabElement.dataset.tab; switch (tabName) { case 'analysis': updateAnalysisTab(); break; case 'portfolio': updatePortfolioTab(); break; case 'transactions': updateTransactionsTab(); break; case 'search': updateSearchTab(); break; case 'settings': updateSettingsTab(); break; } }; const toggleAnalyzer = () => { const container = document.getElementById('rp-analyzer-container'); if (container.style.display === 'none') { container.style.display = 'block'; const symbol = getCoinFromUrl(); if (symbol) { loadCoinData(symbol); } } else { container.style.display = 'none'; } }; // =========================== // Tab Update Functions // =========================== const updateAnalysisTab = async () => { const content = document.getElementById('rp-analyzer-content'); if (!getApiKey()) { content.innerHTML = ` <div class="rp-empty-state"> <div class="rp-empty-state-icon">🔐</div> <h3>API Key Required</h3> <p>Configure your RugPlay API key to start analyzing coins</p> <button class="rp-analyzer-button" onclick="document.querySelector('[data-tab=\\"settings\\"]').click()"> ⚙️ Go to Settings </button> </div> `; return; } let symbol = currentCoin || getCoinFromUrl(); if (!symbol) { content.innerHTML = ` <div class="rp-empty-state"> <div class="rp-empty-state-icon">🔍</div> <h3>No Coin Selected</h3> <p>Navigate to a coin page or search for a coin to analyze</p> <button class="rp-analyzer-button" onclick="document.querySelector('[data-tab=\\"search\\"]').click()"> 🔎 Search Coins </button> </div> `; return; } content.innerHTML = ` <div class="rp-analyzer-section"> <div class="rp-analyzer-section-title">🔄 Loading ${symbol}...</div> <div class="rp-loading"></div> <p style="text-align: center; margin-top: 16px;">Fetching comprehensive market data...</p> </div> `; try { await loadCoinData(symbol); } catch (error) { content.innerHTML = ` <div class="rp-analyzer-section"> <div class="rp-analyzer-section-title">❌ Error</div> <p style="color: #ff6b6b;">${error}</p> <button class="rp-analyzer-button" onclick="updateAnalysisTab()">🔄 Retry</button> </div> `; } }; const updatePortfolioTab = () => { const content = document.getElementById('rp-analyzer-content'); const portfolio = getPortfolio(); const coins = Object.keys(portfolio); if (coins.length === 0) { content.innerHTML = ` <div class="rp-empty-state"> <div class="rp-empty-state-icon">💼</div> <h3>Your Portfolio is Empty</h3> <p>Start tracking your investments by adding transactions</p> <button class="rp-analyzer-button" onclick="document.querySelector('[data-tab=\\"search\\"]').click()"> 🔎 Find Coins to Track </button> </div> `; return; } let totalValue = 0; let totalCost = 0; let portfolioHTML = ` <div class="rp-analyzer-section"> <div class="rp-analyzer-section-title">💼 Portfolio Overview</div> <div class="rp-score-card"> `; // Calculate portfolio metrics coins.forEach(symbol => { const holdings = calculateHoldings(symbol); if (holdings.quantity > 0) { // Note: We'd need current price to calculate actual values // For now, showing holdings data totalCost += holdings.quantity * holdings.avgPrice; } }); portfolioHTML += ` <div class="rp-score-item"> <div class="rp-score-value">${coins.length}</div> <div class="rp-score-label">Assets Tracked</div> </div> <div class="rp-score-item"> <div class="rp-score-value">${formatNumber(totalCost)}</div> <div class="rp-score-label">Total Cost Basis</div> </div> </div> </div> <div class="rp-analyzer-section"> <div class="rp-analyzer-section-title">📊 Holdings</div> <table class="rp-transaction-table"> <thead> <tr> <th>Coin</th> <th>Quantity</th> <th>Avg Price</th> <th>Cost Basis</th> <th>Action</th> </tr> </thead> <tbody> `; coins.forEach(symbol => { const holdings = calculateHoldings(symbol); if (holdings.quantity > 0) { const costBasis = holdings.quantity * holdings.avgPrice; portfolioHTML += ` <tr> <td><strong>${symbol}</strong></td> <td>${formatNumberWithCommas(holdings.quantity.toFixed(6))}</td> <td>${formatPrice(holdings.avgPrice)}</td> <td>${formatNumberWithCommas(costBasis.toFixed(2))}</td> <td> <button class="rp-analyzer-button rp-analyze-btn" data-symbol="${symbol}" style="padding: 6px 12px; font-size: 12px;"> 🔍 Analyze </button> </td> </tr> `; } }); portfolioHTML += ` </tbody> </table> </div> `; content.innerHTML = portfolioHTML; // Setup event listeners for analyze buttons setTimeout(() => { document.querySelectorAll('.rp-analyze-btn').forEach(button => { button.addEventListener('click', function() { const symbol = this.getAttribute('data-symbol'); console.log('🔍 Analyze button clicked for:', symbol); currentCoin = symbol; document.querySelector('[data-tab="analysis"]').click(); }); }); }, 100); }; const updateTransactionsTab = () => { const content = document.getElementById('rp-analyzer-content'); const portfolio = getPortfolio(); let allTransactions = []; Object.keys(portfolio).forEach(symbol => { if (portfolio[symbol].transactions) { portfolio[symbol].transactions.forEach(tx => { allTransactions.push({ ...tx, symbol }); }); } }); allTransactions.sort((a, b) => new Date(b.date) - new Date(a.date)); if (allTransactions.length === 0) { content.innerHTML = ` <div class="rp-empty-state"> <div class="rp-empty-state-icon">📝</div> <h3>No Transactions</h3> <p>Your transaction history will appear here</p> </div> `; return; } const itemsPerPage = 15; const totalPages = Math.ceil(allTransactions.length / itemsPerPage); const currentPage = transactionsCurrentPage || 1; const startIndex = (currentPage - 1) * itemsPerPage; const endIndex = startIndex + itemsPerPage; const currentTransactions = allTransactions.slice(startIndex, endIndex); content.innerHTML = ` <div class="rp-analyzer-section"> <div class="rp-analyzer-section-title">📝 Transaction History</div> <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px;"> <div>Total: <strong>${allTransactions.length}</strong> transactions</div> <div style="display: flex; gap: 8px;"> <button class="rp-analyzer-button rp-export-btn">📤 Export</button> <button class="rp-analyzer-button rp-import-btn">📥 Import</button> </div> </div> <table class="rp-transaction-table"> <thead> <tr> <th>Date</th> <th>Coin</th> <th>Type</th> <th>Quantity</th> <th>Price</th> <th>Total</th> <th>Actions</th> </tr> </thead> <tbody> ${currentTransactions.map(tx => ` <tr> <td class="rp-transaction-date">${formatDateShort(tx.date)}</td> <td class="rp-transaction-coin">${tx.symbol}</td> <td> <span class="rp-transaction-type ${tx.type}"> ${tx.type.toUpperCase()} </span> </td> <td>${formatNumberWithCommas(Math.abs(tx.quantity).toFixed(6))}</td> <td>${formatPrice(tx.price)}</td> <td>${formatNumberWithCommas((Math.abs(tx.quantity) * tx.price).toFixed(2))}</td> <td> <button class="rp-analyzer-button rp-delete rp-delete-tx-btn" data-symbol="${tx.symbol}" data-txid="${tx.id}"> 🗑️ Delete </button> </td> </tr> `).join('')} </tbody> </table> ${totalPages > 1 ? ` <div style="display: flex; justify-content: center; gap: 8px; margin-top: 16px;"> ${currentPage > 1 ? `<button class="rp-analyzer-button rp-prev-page-btn">❮ Prev</button>` : ''} <span style="display: flex; align-items: center; padding: 0 16px;"> Page ${currentPage} of ${totalPages} </span> ${currentPage < totalPages ? `<button class="rp-analyzer-button rp-next-page-btn">Next ❯</button>` : ''} </div> ` : ''} </div> `; // CRITICAL: Setup delete button event listeners IMMEDIATELY setTimeout(() => { console.log('🔧 Setting up transaction delete button listeners...'); // Delete transaction buttons document.querySelectorAll('.rp-delete-tx-btn').forEach((button, index) => { const symbol = button.getAttribute('data-symbol'); const txId = button.getAttribute('data-txid'); console.log(`🗑️ Setting up delete button ${index + 1}: ${symbol} - ${txId}`); button.addEventListener('click', function(e) { e.preventDefault(); e.stopPropagation(); console.log(`🗑️ DELETE CLICKED! Symbol: ${symbol}, TxId: ${txId}`); // Immediate visual feedback const originalHTML = this.innerHTML; this.innerHTML = '⏳ Deleting...'; this.disabled = true; this.style.background = 'linear-gradient(135deg, #ffa500, #ff8c00)'; try { const success = deleteTransaction(symbol, txId); if (success) { console.log('✅ Transaction deleted successfully'); showNotification(`✅ ${symbol} transaction deleted!`, 'success'); // Smooth row removal const row = this.closest('tr'); if (row) { row.style.transition = 'all 0.5s ease'; row.style.background = 'rgba(255, 71, 87, 0.3)'; row.style.transform = 'scale(0.95)'; row.style.opacity = '0.5'; setTimeout(() => { updateTransactionsTab(); // Refresh the entire tab }, 500); } else { updateTransactionsTab(); } } else { console.error('❌ Delete failed'); showNotification('❌ Failed to delete transaction', 'error'); this.innerHTML = originalHTML; this.disabled = false; this.style.background = 'linear-gradient(135deg, #ff4757, #ff3742)'; } } catch (error) { console.error('❌ Delete error:', error); showNotification('❌ Error deleting transaction', 'error'); this.innerHTML = originalHTML; this.disabled = false; this.style.background = 'linear-gradient(135deg, #ff4757, #ff3742)'; } }); console.log(`✅ Delete button ${index + 1} listener set successfully`); }); // Export button const exportBtn = document.querySelector('.rp-export-btn'); if (exportBtn) { exportBtn.addEventListener('click', () => { console.log('📤 Export button clicked'); exportTransactions(); }); } // Import button const importBtn = document.querySelector('.rp-import-btn'); if (importBtn) { importBtn.addEventListener('click', () => { console.log('📥 Import button clicked'); showImportModal(); }); } // Pagination buttons const prevBtn = document.querySelector('.rp-prev-page-btn'); if (prevBtn) { prevBtn.addEventListener('click', () => { transactionsCurrentPage = Math.max(1, currentPage - 1); updateTransactionsTab(); }); } const nextBtn = document.querySelector('.rp-next-page-btn'); if (nextBtn) { nextBtn.addEventListener('click', () => { transactionsCurrentPage = Math.min(totalPages, currentPage + 1); updateTransactionsTab(); }); } console.log('✅ All transaction tab event listeners setup complete'); }, 100); }; const updateSearchTab = () => { const content = document.getElementById('rp-analyzer-content'); content.innerHTML = ` <div class="rp-analyzer-section"> <div class="rp-analyzer-section-title">🔎 Search Coins</div> <div style="display: flex; gap: 8px; margin-bottom: 16px;"> <input type="text" class="rp-analyzer-input rp-search-input" placeholder="Enter coin name or symbol..." style="margin-bottom: 0; flex: 1;"> <button class="rp-analyzer-button rp-search-btn">🔍 Search</button> </div> </div> <div class="rp-search-results"></div> `; // Setup search functionality setTimeout(() => { const searchInput = document.querySelector('.rp-search-input'); const searchBtn = document.querySelector('.rp-search-btn'); const performSearch = async () => { const searchTerm = searchInput.value.trim(); if (!searchTerm) { showNotification('Please enter a search term', 'error'); return; } const resultsContainer = document.querySelector('.rp-search-results'); resultsContainer.innerHTML = ` <div style="text-align: center; padding: 40px;"> <div class="rp-loading"></div> <p style="margin-top: 16px;">Searching for "${searchTerm}"...</p> </div> `; try { const results = await searchCoins(searchTerm); if (!results.coins || results.coins.length === 0) { resultsContainer.innerHTML = ` <div class="rp-empty-state"> <div class="rp-empty-state-icon">❌</div> <h3>No Results Found</h3> <p>No coins found matching "${searchTerm}"</p> </div> `; return; } resultsContainer.innerHTML = ` <div class="rp-analyzer-section"> <div class="rp-analyzer-section-title">📊 Search Results (${results.coins.length})</div> <table class="rp-transaction-table"> <thead> <tr> <th>Coin</th> <th>Price</th> <th>24h Change</th> <th>Market Cap</th> <th>Action</th> </tr> </thead> <tbody> ${results.coins.map(coin => { const changePercent = ((coin.change24h / (coin.currentPrice - coin.change24h)) * 100).toFixed(2); const direction = changePercent >= 0 ? 'positive' : 'negative'; return ` <tr> <td><strong>${coin.name}</strong><br><small>${coin.symbol}</small></td> <td>${formatPrice(coin.currentPrice)}</td> <td class="rp-${direction}"> ${changePercent >= 0 ? '+' : ''}${changePercent}% </td> <td>${formatNumber(coin.marketCap)}</td> <td> <button class="rp-analyzer-button rp-analyze-search-btn" data-symbol="${coin.symbol}"> 🔍 Analyze </button> </td> </tr> `; }).join('')} </tbody> </table> </div> `; // Setup analyze buttons for search results setTimeout(() => { document.querySelectorAll('.rp-analyze-search-btn').forEach(button => { button.addEventListener('click', function() { const symbol = this.getAttribute('data-symbol'); console.log('🔍 Search analyze button clicked for:', symbol); currentCoin = symbol; document.querySelector('[data-tab="analysis"]').click(); }); }); }, 100); } catch (error) { resultsContainer.innerHTML = ` <div class="rp-analyzer-section"> <div class="rp-analyzer-section-title">❌ Error</div> <p style="color: #ff6b6b;">Error searching: ${error}</p> <button class="rp-analyzer-button rp-retry-search-btn">🔄 Retry</button> </div> `; // Setup retry button setTimeout(() => { const retryBtn = document.querySelector('.rp-retry-search-btn'); if (retryBtn) { retryBtn.addEventListener('click', performSearch); } }, 100); } }; // Setup search events searchBtn.addEventListener('click', performSearch); searchInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') performSearch(); }); }, 100); }; const updateSettingsTab = () => { const content = document.getElementById('rp-analyzer-content'); const apiKey = getApiKey(); content.innerHTML = ` <div class="rp-analyzer-section"> <div class="rp-analyzer-section-title">🔐 API Configuration</div> <p style="margin-bottom: 16px; color: rgba(255, 255, 255, 0.8);"> Enter your RugPlay API key to access comprehensive market data </p> <div style="position: relative;"> <input type="password" class="rp-analyzer-input rp-api-key-input" value="${apiKey}" placeholder="Enter your RugPlay API key..."> <button class="rp-analyzer-button rp-toggle-key-btn" style="position: absolute; right: 8px; top: 8px; padding: 8px;">👁️</button> </div> <button class="rp-analyzer-button rp-save-api-btn">💾 Save API Key</button> </div> <div class="rp-analyzer-section"> <div class="rp-analyzer-section-title">💼 Portfolio Management</div> <p style="margin-bottom: 16px; color: rgba(255, 255, 255, 0.8);"> Backup and restore your portfolio data </p> <div style="display: flex; gap: 12px; flex-wrap: wrap;"> <button class="rp-analyzer-button rp-export-portfolio-btn">📤 Export Portfolio</button> <button class="rp-analyzer-button rp-import-portfolio-btn">📥 Import Portfolio</button> <button class="rp-analyzer-button rp-delete rp-clear-portfolio-btn">🗑️ Clear All Data</button> </div> </div> <div class="rp-analyzer-section"> <div class="rp-analyzer-section-title">ℹ️ About</div> <div class="rp-metric-grid"> <div class="rp-metric-item"> <div class="rp-metric-title">Version</div> <div class="rp-metric-value">2.0.1 Fixed</div> </div> <div class="rp-metric-item"> <div class="rp-metric-title">Author</div> <div class="rp-metric-value">seltonmt012</div> </div> <div class="rp-metric-item"> <div class="rp-metric-title">Updated</div> <div class="rp-metric-value">2025-06-26</div> </div> <div class="rp-metric-item"> <div class="rp-metric-title">Features</div> <div class="rp-metric-value">AI Analysis</div> </div> </div> </div> `; // Setup settings event listeners setTimeout(() => { // API key visibility toggle const toggleKeyBtn = document.querySelector('.rp-toggle-key-btn'); const apiKeyInput = document.querySelector('.rp-api-key-input'); if (toggleKeyBtn && apiKeyInput) { toggleKeyBtn.addEventListener('click', () => { if (apiKeyInput.type === 'password') { apiKeyInput.type = 'text'; toggleKeyBtn.textContent = '🔒'; } else { apiKeyInput.type = 'password'; toggleKeyBtn.textContent = '👁️'; } }); } // Save API key const saveApiBtn = document.querySelector('.rp-save-api-btn'); if (saveApiBtn) { saveApiBtn.addEventListener('click', () => { const key = apiKeyInput.value.trim(); if (!key) { showNotification('❌ API key cannot be empty', 'error'); return; } saveApiKey(key); showNotification('✅ API key saved successfully', 'success'); setTimeout(() => { document.querySelector('[data-tab="analysis"]').click(); }, 1000); }); } // Export portfolio const exportPortfolioBtn = document.querySelector('.rp-export-portfolio-btn'); if (exportPortfolioBtn) { exportPortfolioBtn.addEventListener('click', () => { exportTransactions(); }); } // Import portfolio const importPortfolioBtn = document.querySelector('.rp-import-portfolio-btn'); if (importPortfolioBtn) { importPortfolioBtn.addEventListener('click', () => { showImportModal(); }); } // Clear portfolio const clearPortfolioBtn = document.querySelector('.rp-clear-portfolio-btn'); if (clearPortfolioBtn) { clearPortfolioBtn.addEventListener('click', () => { showConfirmModal( '🗑️ Clear All Data', 'This will permanently delete all your portfolio data and transactions. This action cannot be undone.', () => { try { savePortfolio({}); showNotification('✅ Portfolio data cleared successfully', 'success'); // Update UI if we're on portfolio tab const activeTab = document.querySelector('.rp-analyzer-tab.active'); if (activeTab && activeTab.dataset.tab === 'portfolio') { setTimeout(() => updatePortfolioTab(), 500); } } catch (error) { console.error('❌ Error clearing portfolio:', error); showNotification('❌ Error clearing portfolio', 'error'); } } ); }); } }, 100); }; // =========================== // Enhanced Coin Data Loading // =========================== let currentCoin = null; let currentCoinData = null; let currentHoldersData = null; let transactionsCurrentPage = 1; const loadCoinData = async (symbol) => { try { const [coinData, holdersData] = await Promise.all([ fetchCoinData(symbol), fetchCoinHolders(symbol) ]); currentCoin = symbol; currentCoinData = coinData; currentHoldersData = holdersData; const holdings = calculateHoldings(symbol); const coin = coinData.coin; const price = coin.currentPrice; const change24h = coin.change24h; const changePercent = ((change24h / (price - change24h)) * 100); // Enhanced Analysis const priceAnalysis = analyzePrice(coinData.candlestickData); const securityAnalysis = analyzeSecurity(coinData, holdersData); const activityAnalysis = analyzeActivity(coinData, holdersData); const profitAnalysis = analyzeProfitability(coinData, holdings); // Personal P&L let profitLoss = 0; let profitLossPercent = 0; if (holdings.quantity > 0) { profitLoss = holdings.quantity * (price - holdings.avgPrice); profitLossPercent = ((price / holdings.avgPrice) - 1) * 100; } const content = document.getElementById('rp-analyzer-content'); content.innerHTML = ` <!-- Prediction Card --> <div class="rp-prediction-card"> <div class="rp-prediction-icon">${getPredictionIcon(priceAnalysis.trend)}</div> <div class="rp-prediction-text">${priceAnalysis.message}</div> <div class="rp-prediction-confidence">Confidence: ${priceAnalysis.confidence.toFixed(0)}%</div> </div> <!-- Overall Scores --> <div class="rp-analyzer-section"> <div class="rp-analyzer-section-title">🎯 ${coin.name} (${coin.symbol}) Analysis</div> <div class="rp-score-card"> <div class="rp-score-item"> <div class="rp-score-value rp-score-${getScoreClass(securityAnalysis.securityScore)}">${securityAnalysis.securityScore}/100</div> <div class="rp-score-label">Security Score</div> </div> <div class="rp-score-item"> <div class="rp-score-value rp-score-${getScoreClass(activityAnalysis.activityScore)}">${activityAnalysis.activityScore.toFixed(0)}/100</div> <div class="rp-score-label">Activity Level</div> </div> <div class="rp-score-item"> <div class="rp-score-value rp-score-${getProfitClass(profitAnalysis.level)}">${profitAnalysis.profitScore.toFixed(0)}/100</div> <div class="rp-score-label">Profit Potential</div> </div> <div class="rp-score-item"> <div class="rp-score-value ${changePercent >= 0 ? 'rp-positive' : 'rp-negative'}"> ${changePercent >= 0 ? '+' : ''}${changePercent.toFixed(2)}% </div> <div class="rp-score-label">24h Change</div> </div> </div> </div> <!-- Market Data --> <div class="rp-analyzer-section"> <div class="rp-analyzer-section-title">📊 Market Data</div> <div class="rp-metric-grid"> <div class="rp-metric-item"> <div class="rp-metric-title">Current Price</div> <div class="rp-metric-value">${formatPrice(price)}</div> </div> <div class="rp-metric-item"> <div class="rp-metric-title">Market Cap</div> <div class="rp-metric-value">${formatNumber(coin.marketCap)}</div> </div> <div class="rp-metric-item"> <div class="rp-metric-title">24h Volume</div> <div class="rp-metric-value">${formatNumber(coin.volume24h)}</div> </div> <div class="rp-metric-item"> <div class="rp-metric-title">Liquidity</div> <div class="rp-metric-value">${formatNumber(coin.poolBaseCurrencyAmount)}</div> </div> <div class="rp-metric-item"> <div class="rp-metric-title">Supply</div> <div class="rp-metric-value">${formatNumber(coin.circulatingSupply)}</div> </div> <div class="rp-metric-item"> <div class="rp-metric-title">Holders</div> <div class="rp-metric-value">${holdersData.holders.length}</div> </div> </div> </div> <!-- Security Analysis --> <div class="rp-risk-analysis"> <div class="rp-risk-title">🛡️ Security Analysis - ${securityAnalysis.level}</div> <div style="margin-bottom: 16px;"> <div class="rp-progress-bar"> <div class="rp-progress-fill rp-progress-${getScoreClass(securityAnalysis.securityScore)}" style="width: ${securityAnalysis.securityScore}%"></div> </div> <div style="text-align: center; margin-top: 8px; font-weight: 600;"> ${securityAnalysis.recommendation} </div> </div> <div class="rp-risk-factors"> ${securityAnalysis.factors.map(factor => ` <div class="rp-risk-factor"> <span>${factor.message}</span> <span class="rp-risk-impact ${factor.type}">${factor.impact > 0 ? '+' : ''}${factor.impact}</span> </div> `).join('')} </div> </div> <!-- Activity Analysis --> <div class="rp-analyzer-section"> <div class="rp-analyzer-section-title">⚡ Activity Analysis - ${activityAnalysis.level}</div> <div class="rp-progress-bar"> <div class="rp-progress-fill rp-progress-${getScoreClass(activityAnalysis.activityScore)}" style="width: ${activityAnalysis.activityScore}%"></div> </div> <div class="rp-metric-grid" style="margin-top: 16px;"> ${activityAnalysis.factors.map(factor => ` <div class="rp-metric-item"> <div class="rp-metric-title">${factor.metric}</div> <div class="rp-metric-value">${factor.value}</div> <div style="font-size: 12px; color: rgba(255,255,255,0.6);"> Score: ${factor.score.toFixed(0)}/${factor.maxScore} </div> </div> `).join('')} </div> </div> <!-- Profitability Analysis --> <div class="rp-analyzer-section"> <div class="rp-analyzer-section-title">💰 Profitability Analysis - ${profitAnalysis.level}</div> <div class="rp-progress-bar"> <div class="rp-progress-fill rp-progress-${getProfitClass(profitAnalysis.level)}" style="width: ${profitAnalysis.profitScore}%"></div> </div> <div style="margin-top: 16px;"> ${profitAnalysis.factors.map(factor => ` <div class="rp-analyzer-data-row"> <span class="rp-analyzer-label">${factor.metric}:</span> <span class="rp-analyzer-value rp-${factor.impact}">${factor.value}</span> </div> `).join('')} </div> </div> <!-- Your Holdings --> <div class="rp-analyzer-section"> <div class="rp-analyzer-section-title">💼 Your Holdings</div> ${holdings.quantity > 0 ? ` <div class="rp-metric-grid"> <div class="rp-metric-item"> <div class="rp-metric-title">Quantity</div> <div class="rp-metric-value">${formatNumberWithCommas(holdings.quantity.toFixed(6))}</div> </div> <div class="rp-metric-item"> <div class="rp-metric-title">Avg Cost</div> <div class="rp-metric-value">${formatPrice(holdings.avgPrice)}</div> </div> <div class="rp-metric-item"> <div class="rp-metric-title">Current Value</div> <div class="rp-metric-value">${formatNumberWithCommas((holdings.quantity * price).toFixed(2))}</div> </div> <div class="rp-metric-item"> <div class="rp-metric-title">P&L</div> <div class="rp-metric-value ${profitLoss >= 0 ? 'rp-positive' : 'rp-negative'}"> ${profitLoss >= 0 ? '+' : ''}${formatNumberWithCommas(Math.abs(profitLoss).toFixed(2))} (${profitLossPercent >= 0 ? '+' : ''}${profitLossPercent.toFixed(2)}%) </div> </div> </div> ` : ` <p style="text-align: center; color: rgba(255,255,255,0.6); margin: 20px 0;"> You don't have any holdings for this coin yet. </p> `} <div style="border-top: 1px solid rgba(255,255,255,0.1); padding-top: 16px; margin-top: 16px;"> <h4 style="margin-bottom: 12px; color: #00d4ff;">➕ Add Transaction</h4> <div style="display: grid; grid-template-columns: 1fr 1fr auto; gap: 8px;"> <input type="number" class="rp-analyzer-input rp-transaction-quantity-input" placeholder="Quantity (+buy/-sell)" style="margin-bottom: 0;" step="any"> <input type="number" class="rp-analyzer-input rp-transaction-price-input" placeholder="Price per coin" style="margin-bottom: 0;" step="any"> <button class="rp-analyzer-button rp-add-transaction-btn" style="margin-bottom: 0;"> ➕ Add </button> </div> <div style="margin-top: 8px; font-size: 12px; color: rgba(255,255,255,0.6);"> 💡 Use positive numbers for buys, negative for sells </div> </div> </div> <!-- Technical Indicators --> <div class="rp-analyzer-section"> <div class="rp-analyzer-section-title">📈 Technical Analysis</div> <div class="rp-metric-grid"> <div class="rp-metric-item"> <div class="rp-metric-title">Trend</div> <div class="rp-metric-value">${priceAnalysis.trend}</div> </div> <div class="rp-metric-item"> <div class="rp-metric-title">Volatility</div> <div class="rp-metric-value">${priceAnalysis.volatility ? priceAnalysis.volatility.toFixed(2) + '%' : 'N/A'}</div> </div> <div class="rp-metric-item"> <div class="rp-metric-title">Momentum</div> <div class="rp-metric-value ${priceAnalysis.momentum >= 0 ? 'rp-positive' : 'rp-negative'}"> ${priceAnalysis.momentum ? (priceAnalysis.momentum >= 0 ? '+' : '') + priceAnalysis.momentum.toFixed(2) + '%' : 'N/A'} </div> </div> <div class="rp-metric-item"> <div class="rp-metric-title">Volume/MC Ratio</div> <div class="rp-metric-value">${((coin.volume24h / coin.marketCap) * 100).toFixed(2)}%</div> </div> </div> <div class="rp-chart-placeholder"> 📊 Price Chart Placeholder - Advanced charting coming soon </div> </div> <!-- Holder Analysis --> <div class="rp-analyzer-section"> <div class="rp-analyzer-section-title">👥 Holder Distribution</div> <div class="rp-metric-grid"> <div class="rp-metric-item"> <div class="rp-metric-title">Top Holder</div> <div class="rp-metric-value">${securityAnalysis.holderAnalysis.topHolder.toFixed(2)}%</div> </div> <div class="rp-metric-item"> <div class="rp-metric-title">Top 10 Holders</div> <div class="rp-metric-value">${securityAnalysis.holderAnalysis.top10Holders.toFixed(2)}%</div> </div> <div class="rp-metric-item"> <div class="rp-metric-title">Pool Holdings</div> <div class="rp-metric-value">${securityAnalysis.holderAnalysis.poolPercentage.toFixed(3)}%</div> </div> <div class="rp-metric-item"> <div class="rp-metric-title">Total Holders</div> <div class="rp-metric-value">${securityAnalysis.holderAnalysis.totalHolders}</div> </div> </div> </div> <!-- Project Info --> <div class="rp-analyzer-section"> <div class="rp-analyzer-section-title">ℹ️ Project Information</div> <div class="rp-analyzer-data-row"> <span class="rp-analyzer-label">Created:</span> <span class="rp-analyzer-value">${formatDate(coin.createdAt)}</span> </div> <div class="rp-analyzer-data-row"> <span class="rp-analyzer-label">Creator:</span> <span class="rp-analyzer-value">${coin.creatorName}</span> </div> <div class="rp-analyzer-data-row"> <span class="rp-analyzer-label">Age:</span> <span class="rp-analyzer-value">${Math.floor((new Date() - new Date(coin.createdAt)) / (1000 * 60 * 60 * 24))} days</span> </div> </div> `; // Setup add transaction functionality setTimeout(() => { const addButton = document.querySelector('.rp-add-transaction-btn'); const quantityInput = document.querySelector('.rp-transaction-quantity-input'); const priceInput = document.querySelector('.rp-transaction-price-input'); if (addButton && quantityInput && priceInput) { const addTransactionHandler = () => { const quantity = quantityInput.value; const price = priceInput.value; if (!quantity || !price) { showNotification('❌ Please enter both quantity and price', 'error'); return; } if (isNaN(quantity) || isNaN(price)) { showNotification('❌ Please enter valid numbers', 'error'); return; } if (parseFloat(price) <= 0) { showNotification('❌ Price must be greater than 0', 'error'); return; } try { addTransaction(symbol, parseFloat(quantity), parseFloat(price)); // Visual feedback addButton.innerHTML = '✅ Added!'; addButton.style.background = 'linear-gradient(135deg, #00ff88, #00d4aa)'; addButton.disabled = true; showNotification(`✅ ${quantity > 0 ? 'Buy' : 'Sell'} transaction added for ${symbol}!`, 'success'); // Clear inputs quantityInput.value = ''; priceInput.value = ''; // Reset button and refresh setTimeout(() => { addButton.innerHTML = '➕ Add'; addButton.style.background = 'linear-gradient(135deg, #00d4ff, #0096ff)'; addButton.disabled = false; loadCoinData(symbol); }, 2000); } catch (error) { console.error('Error adding transaction:', error); showNotification('❌ Error adding transaction', 'error'); } }; addButton.addEventListener('click', addTransactionHandler); // Enter key support [quantityInput, priceInput].forEach(input => { input.addEventListener('keypress', (e) => { if (e.key === 'Enter') addTransactionHandler(); }); }); } }, 100); } catch (error) { console.error('Error loading coin data:', error); throw new Error('Failed to load coin data. Please check your API key and network connection.'); } }; // =========================== // Helper Functions // =========================== const getPredictionIcon = (trend) => { const icons = { 'STRONG_UP': '🚀', 'UP': '📈', 'NEUTRAL': '↔️', 'DOWN': '📉', 'STRONG_DOWN': '💥', 'UNKNOWN': '❓' }; return icons[trend] || '❓'; }; const getScoreClass = (score) => { if (score >= 80) return 'excellent'; if (score >= 60) return 'good'; if (score >= 40) return 'neutral'; if (score >= 20) return 'poor'; return 'critical'; }; const getProfitClass = (level) => { const classes = { 'EXCELLENT': 'excellent', 'GOOD': 'good', 'NEUTRAL': 'neutral', 'POOR': 'poor', 'VERY_POOR': 'critical' }; return classes[level] || 'neutral'; }; const showNotification = (message, type = 'success') => { const existingNotification = document.querySelector('.rp-notification'); if (existingNotification) existingNotification.remove(); const notification = document.createElement('div'); notification.className = `rp-notification rp-${type}`; notification.textContent = message; document.body.appendChild(notification); setTimeout(() => { notification.style.opacity = '0'; setTimeout(() => notification.remove(), 300); }, 3000); }; const showConfirmModal = (title, message, onConfirm) => { const existingModal = document.getElementById('rp-confirm-modal'); if (existingModal) existingModal.remove(); const modal = document.createElement('div'); modal.className = 'rp-modal-backdrop'; modal.id = 'rp-confirm-modal'; modal.innerHTML = ` <div class="rp-modal"> <div class="rp-modal-title">${title}</div> <div class="rp-modal-content"> <p>${message}</p> </div> <div class="rp-modal-actions"> <button class="rp-analyzer-button rp-modal-cancel-btn" style="background: #555;">Cancel</button> <button class="rp-analyzer-button rp-delete rp-modal-confirm-btn">Confirm</button> </div> </div> `; document.body.appendChild(modal); // Setup modal button listeners setTimeout(() => { const cancelBtn = document.querySelector('.rp-modal-cancel-btn'); const confirmBtn = document.querySelector('.rp-modal-confirm-btn'); if (cancelBtn) { cancelBtn.addEventListener('click', () => { modal.remove(); }); } if (confirmBtn) { confirmBtn.addEventListener('click', () => { try { onConfirm(); modal.remove(); } catch (error) { console.error('Error in confirmation action:', error); showNotification('Error: ' + error.message, 'error'); modal.remove(); } }); } }, 100); }; // Export/Import Functions const exportTransactions = () => { const portfolio = getPortfolio(); const dataStr = JSON.stringify(portfolio, null, 2); const dataUri = 'data:application/json;charset=utf-8,'+ encodeURIComponent(dataStr); const exportFileDefaultName = `rugplay_portfolio_${new Date().toISOString().split('T')[0]}.json`; const linkElement = document.createElement('a'); linkElement.setAttribute('href', dataUri); linkElement.setAttribute('download', exportFileDefaultName); linkElement.click(); showNotification('Portfolio exported successfully', 'success'); }; const showImportModal = () => { const existingModal = document.querySelector('.rp-modal-backdrop'); if (existingModal) existingModal.remove(); const modal = document.createElement('div'); modal.className = 'rp-modal-backdrop'; modal.innerHTML = ` <div class="rp-modal"> <div class="rp-modal-title">📥 Import Portfolio Data</div> <div class="rp-modal-content"> <p>Select a JSON file containing your exported portfolio data:</p> <input type="file" class="rp-import-file-input" accept=".json" class="rp-analyzer-input"> <p style="color: #ffa500; margin-top: 10px;">⚠️ This will overwrite your current portfolio data!</p> </div> <div class="rp-modal-actions"> <button class="rp-analyzer-button rp-import-cancel-btn" style="background: #555;">Cancel</button> <button class="rp-analyzer-button rp-import-confirm-btn">Import</button> </div> </div> `; document.body.appendChild(modal); // Setup import modal listeners setTimeout(() => { const cancelBtn = document.querySelector('.rp-import-cancel-btn'); const confirmBtn = document.querySelector('.rp-import-confirm-btn'); const fileInput = document.querySelector('.rp-import-file-input'); if (cancelBtn) { cancelBtn.addEventListener('click', () => { modal.remove(); }); } if (confirmBtn && fileInput) { confirmBtn.addEventListener('click', () => { if (fileInput.files.length === 0) { showNotification('Please select a file to import', 'error'); return; } const file = fileInput.files[0]; const reader = new FileReader(); reader.onload = (e) => { try { const data = JSON.parse(e.target.result); savePortfolio(data); showNotification('Portfolio imported successfully', 'success'); modal.remove(); // Update current tab if it's portfolio const activeTab = document.querySelector('.rp-analyzer-tab.active'); if (activeTab && activeTab.dataset.tab === 'portfolio') { updatePortfolioTab(); } } catch (error) { showNotification('Error importing data: Invalid JSON format', 'error'); } }; reader.readAsText(file); }); } }, 100); }; // Utility Functions const formatPrice = (price) => { if (price >= 1000) return price.toFixed(2); if (price >= 1) return price.toFixed(4); if (price >= 0.01) return price.toFixed(5); if (price >= 0.0001) return price.toFixed(6); if (price >= 0.000001) return price.toFixed(8); return price.toExponential(4); }; const formatNumber = (num) => { if (num >= 1e9) return (num / 1e9).toFixed(2) + 'B'; if (num >= 1e6) return (num / 1e6).toFixed(2) + 'M'; if (num >= 1e3) return (num / 1e3).toFixed(2) + 'K'; return num.toFixed(2); }; const formatNumberWithCommas = (num) => { return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ','); }; const formatDate = (dateString) => { return new Date(dateString).toLocaleString(); }; const formatDateShort = (dateString) => { const date = new Date(dateString); const day = date.getDate().toString().padStart(2, '0'); const month = (date.getMonth() + 1).toString().padStart(2, '0'); const year = date.getFullYear(); const hours = date.getHours().toString().padStart(2, '0'); const minutes = date.getMinutes().toString().padStart(2, '0'); return `${day}.${month}.${year} ${hours}:${minutes}`; }; const getCoinFromUrl = () => { const url = window.location.href; const coinMatch = url.match(/\/coin\/([A-Z0-9]+)/i); return coinMatch ? coinMatch[1] : null; }; const updateUI = () => { const activeTab = document.querySelector('.rp-analyzer-tab.active'); if (activeTab) switchTab(activeTab); }; // =========================== // Initialization // =========================== const init = () => { console.log('🚀 Initializing RugPlay Analyzer Pro v2.0.1 Fixed...'); addStyles(); if (window.location.hostname.includes('rugplay.com')) { createUI(); if (!document.getElementById('rp-toggle-button')) { createToggleButton(); } const symbol = getCoinFromUrl(); if (symbol) { currentCoin = symbol; setTimeout(() => { const container = document.getElementById('rp-analyzer-container'); if (container && container.style.display === 'none') { toggleAnalyzer(); } }, 1000); } // URL change detection let lastUrl = location.href; new MutationObserver(() => { const url = location.href; if (url !== lastUrl) { lastUrl = url; if (!document.getElementById('rp-toggle-button')) { createToggleButton(); } const newSymbol = getCoinFromUrl(); if (newSymbol && newSymbol !== currentCoin) { currentCoin = newSymbol; if (document.getElementById('rp-analyzer-container').style.display !== 'none') { updateAnalysisTab(); } } } }).observe(document, {subtree: true, childList: true}); console.log('✅ RugPlay Analyzer Pro initialized successfully!'); } }; // Start the application if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } // Ensure toggle button always exists setInterval(() => { if (!document.getElementById('rp-toggle-button')) { createToggleButton(); } }, 5000); console.log('📊 RugPlay Market Analyzer Pro v2.0.1 Fixed - Delete Buttons Working!'); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址