Spaces:
Runtime error
Runtime error
| /** | |
| * Enhanced Charts Module | |
| * Modern, Beautiful, Responsive Charts with Chart.js | |
| */ | |
| // Chart.js Global Configuration | |
| Chart.defaults.color = '#e2e8f0'; | |
| Chart.defaults.borderColor = 'rgba(255, 255, 255, 0.1)'; | |
| Chart.defaults.font.family = "'Manrope', 'Inter', sans-serif"; | |
| Chart.defaults.font.size = 13; | |
| Chart.defaults.font.weight = 500; | |
| // Chart Instances Storage | |
| const chartInstances = {}; | |
| /** | |
| * Initialize Market Overview Chart | |
| * Shows top 5 cryptocurrencies price trends | |
| */ | |
| export function initMarketOverviewChart(data) { | |
| const ctx = document.getElementById('market-overview-chart'); | |
| if (!ctx) return; | |
| // Destroy existing chart | |
| if (chartInstances.marketOverview) { | |
| chartInstances.marketOverview.destroy(); | |
| } | |
| const topCoins = data.slice(0, 5); | |
| const labels = Array.from({length: 24}, (_, i) => `${i}:00`); | |
| const colors = [ | |
| { border: '#8f88ff', bg: 'rgba(143, 136, 255, 0.1)' }, | |
| { border: '#16d9fa', bg: 'rgba(22, 217, 250, 0.1)' }, | |
| { border: '#4ade80', bg: 'rgba(74, 222, 128, 0.1)' }, | |
| { border: '#f472b6', bg: 'rgba(244, 114, 182, 0.1)' }, | |
| { border: '#facc15', bg: 'rgba(250, 204, 21, 0.1)' } | |
| ]; | |
| const datasets = topCoins.map((coin, index) => ({ | |
| label: coin.name, | |
| data: coin.sparkline_in_7d?.price?.slice(-24) || [], | |
| borderColor: colors[index].border, | |
| backgroundColor: colors[index].bg, | |
| borderWidth: 3, | |
| fill: true, | |
| tension: 0.4, | |
| pointRadius: 0, | |
| pointHoverRadius: 6, | |
| pointHoverBackgroundColor: colors[index].border, | |
| pointHoverBorderColor: '#fff', | |
| pointHoverBorderWidth: 2 | |
| })); | |
| chartInstances.marketOverview = new Chart(ctx, { | |
| type: 'line', | |
| data: { labels, datasets }, | |
| options: { | |
| responsive: true, | |
| maintainAspectRatio: false, | |
| interaction: { | |
| mode: 'index', | |
| intersect: false, | |
| }, | |
| plugins: { | |
| legend: { | |
| display: true, | |
| position: 'top', | |
| align: 'end', | |
| labels: { | |
| usePointStyle: true, | |
| pointStyle: 'circle', | |
| padding: 20, | |
| font: { | |
| size: 13, | |
| weight: 600 | |
| }, | |
| color: '#e2e8f0' | |
| } | |
| }, | |
| tooltip: { | |
| enabled: true, | |
| backgroundColor: 'rgba(15, 23, 42, 0.95)', | |
| titleColor: '#fff', | |
| bodyColor: '#e2e8f0', | |
| borderColor: 'rgba(143, 136, 255, 0.5)', | |
| borderWidth: 1, | |
| padding: 16, | |
| displayColors: true, | |
| boxPadding: 8, | |
| usePointStyle: true, | |
| callbacks: { | |
| label: function(context) { | |
| return context.dataset.label + ': $' + context.parsed.y.toFixed(2); | |
| } | |
| } | |
| } | |
| }, | |
| scales: { | |
| x: { | |
| grid: { | |
| display: false | |
| }, | |
| ticks: { | |
| color: '#94a3b8', | |
| font: { | |
| size: 11 | |
| } | |
| } | |
| }, | |
| y: { | |
| grid: { | |
| color: 'rgba(255, 255, 255, 0.05)', | |
| drawBorder: false | |
| }, | |
| ticks: { | |
| color: '#94a3b8', | |
| font: { | |
| size: 11 | |
| }, | |
| callback: function(value) { | |
| return '$' + value.toLocaleString(); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| }); | |
| } | |
| /** | |
| * Create Mini Sparkline Chart for Table | |
| */ | |
| export function createSparkline(canvasId, data, color = '#8f88ff') { | |
| const ctx = document.getElementById(canvasId); | |
| if (!ctx) return; | |
| new Chart(ctx, { | |
| type: 'line', | |
| data: { | |
| labels: data.map((_, i) => i), | |
| datasets: [{ | |
| data: data, | |
| borderColor: color, | |
| backgroundColor: color + '20', | |
| borderWidth: 2, | |
| fill: true, | |
| tension: 0.4, | |
| pointRadius: 0 | |
| }] | |
| }, | |
| options: { | |
| responsive: true, | |
| maintainAspectRatio: false, | |
| plugins: { | |
| legend: { display: false }, | |
| tooltip: { enabled: false } | |
| }, | |
| scales: { | |
| x: { display: false }, | |
| y: { display: false } | |
| } | |
| } | |
| }); | |
| } | |
| /** | |
| * Initialize Price Chart with Advanced Features | |
| */ | |
| export function initPriceChart(coinId, days = 7) { | |
| const ctx = document.getElementById('price-chart'); | |
| if (!ctx) return; | |
| // Destroy existing | |
| if (chartInstances.price) { | |
| chartInstances.price.destroy(); | |
| } | |
| // Fetch data and create chart | |
| fetch(`https://api.coingecko.com/api/v3/coins/${coinId}/market_chart?vs_currency=usd&days=${days}`) | |
| .then(res => res.json()) | |
| .then(data => { | |
| const labels = data.prices.map(p => new Date(p[0]).toLocaleDateString()); | |
| const prices = data.prices.map(p => p[1]); | |
| chartInstances.price = new Chart(ctx, { | |
| type: 'line', | |
| data: { | |
| labels, | |
| datasets: [{ | |
| label: 'Price (USD)', | |
| data: prices, | |
| borderColor: '#8f88ff', | |
| backgroundColor: 'rgba(143, 136, 255, 0.1)', | |
| borderWidth: 3, | |
| fill: true, | |
| tension: 0.4, | |
| pointRadius: 0, | |
| pointHoverRadius: 8, | |
| pointHoverBackgroundColor: '#8f88ff', | |
| pointHoverBorderColor: '#fff', | |
| pointHoverBorderWidth: 3 | |
| }] | |
| }, | |
| options: { | |
| responsive: true, | |
| maintainAspectRatio: false, | |
| plugins: { | |
| legend: { display: false }, | |
| tooltip: { | |
| backgroundColor: 'rgba(15, 23, 42, 0.95)', | |
| titleColor: '#fff', | |
| bodyColor: '#e2e8f0', | |
| borderColor: 'rgba(143, 136, 255, 0.5)', | |
| borderWidth: 1, | |
| padding: 16, | |
| displayColors: false, | |
| callbacks: { | |
| label: function(context) { | |
| return 'Price: $' + context.parsed.y.toLocaleString(); | |
| } | |
| } | |
| } | |
| }, | |
| scales: { | |
| x: { | |
| grid: { display: false }, | |
| ticks: { | |
| color: '#94a3b8', | |
| maxRotation: 0, | |
| autoSkip: true, | |
| maxTicksLimit: 8 | |
| } | |
| }, | |
| y: { | |
| grid: { | |
| color: 'rgba(255, 255, 255, 0.05)', | |
| drawBorder: false | |
| }, | |
| ticks: { | |
| color: '#94a3b8', | |
| callback: function(value) { | |
| return '$' + value.toLocaleString(); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| }); | |
| }); | |
| } | |
| /** | |
| * Initialize Volume Chart | |
| */ | |
| export function initVolumeChart(coinId, days = 7) { | |
| const ctx = document.getElementById('volume-chart'); | |
| if (!ctx) return; | |
| if (chartInstances.volume) { | |
| chartInstances.volume.destroy(); | |
| } | |
| fetch(`https://api.coingecko.com/api/v3/coins/${coinId}/market_chart?vs_currency=usd&days=${days}`) | |
| .then(res => res.json()) | |
| .then(data => { | |
| const labels = data.total_volumes.map(v => new Date(v[0]).toLocaleDateString()); | |
| const volumes = data.total_volumes.map(v => v[1]); | |
| chartInstances.volume = new Chart(ctx, { | |
| type: 'bar', | |
| data: { | |
| labels, | |
| datasets: [{ | |
| label: 'Volume', | |
| data: volumes, | |
| backgroundColor: 'rgba(74, 222, 128, 0.6)', | |
| borderColor: '#4ade80', | |
| borderWidth: 2, | |
| borderRadius: 8, | |
| borderSkipped: false | |
| }] | |
| }, | |
| options: { | |
| responsive: true, | |
| maintainAspectRatio: false, | |
| plugins: { | |
| legend: { display: false }, | |
| tooltip: { | |
| backgroundColor: 'rgba(15, 23, 42, 0.95)', | |
| padding: 16, | |
| callbacks: { | |
| label: function(context) { | |
| return 'Volume: $' + (context.parsed.y / 1000000).toFixed(2) + 'M'; | |
| } | |
| } | |
| } | |
| }, | |
| scales: { | |
| x: { | |
| grid: { display: false }, | |
| ticks: { | |
| color: '#94a3b8', | |
| maxRotation: 0, | |
| autoSkip: true, | |
| maxTicksLimit: 8 | |
| } | |
| }, | |
| y: { | |
| grid: { | |
| color: 'rgba(255, 255, 255, 0.05)', | |
| drawBorder: false | |
| }, | |
| ticks: { | |
| color: '#94a3b8', | |
| callback: function(value) { | |
| return '$' + (value / 1000000).toFixed(0) + 'M'; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| }); | |
| }); | |
| } | |
| /** | |
| * Initialize Sentiment Doughnut Chart | |
| */ | |
| export function initSentimentChart() { | |
| const ctx = document.getElementById('sentiment-chart'); | |
| if (!ctx) return; | |
| if (chartInstances.sentiment) { | |
| chartInstances.sentiment.destroy(); | |
| } | |
| chartInstances.sentiment = new Chart(ctx, { | |
| type: 'doughnut', | |
| data: { | |
| labels: ['Very Bullish', 'Bullish', 'Neutral', 'Bearish', 'Very Bearish'], | |
| datasets: [{ | |
| data: [25, 35, 20, 15, 5], | |
| backgroundColor: [ | |
| '#4ade80', | |
| '#16d9fa', | |
| '#facc15', | |
| '#f472b6', | |
| '#ef4444' | |
| ], | |
| borderWidth: 0, | |
| hoverOffset: 10 | |
| }] | |
| }, | |
| options: { | |
| responsive: true, | |
| maintainAspectRatio: false, | |
| plugins: { | |
| legend: { | |
| position: 'bottom', | |
| labels: { | |
| padding: 20, | |
| usePointStyle: true, | |
| pointStyle: 'circle', | |
| font: { | |
| size: 13, | |
| weight: 600 | |
| } | |
| } | |
| }, | |
| tooltip: { | |
| backgroundColor: 'rgba(15, 23, 42, 0.95)', | |
| padding: 16, | |
| callbacks: { | |
| label: function(context) { | |
| return context.label + ': ' + context.parsed + '%'; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| }); | |
| } | |
| /** | |
| * Initialize Market Dominance Pie Chart | |
| */ | |
| export function initDominanceChart(data) { | |
| const ctx = document.getElementById('dominance-chart'); | |
| if (!ctx) return; | |
| if (chartInstances.dominance) { | |
| chartInstances.dominance.destroy(); | |
| } | |
| const btc = data.find(c => c.id === 'bitcoin'); | |
| const eth = data.find(c => c.id === 'ethereum'); | |
| const bnb = data.find(c => c.id === 'binancecoin'); | |
| const totalMarketCap = data.reduce((sum, coin) => sum + coin.market_cap, 0); | |
| const btcDominance = ((btc?.market_cap || 0) / totalMarketCap * 100).toFixed(1); | |
| const ethDominance = ((eth?.market_cap || 0) / totalMarketCap * 100).toFixed(1); | |
| const bnbDominance = ((bnb?.market_cap || 0) / totalMarketCap * 100).toFixed(1); | |
| const othersDominance = (100 - btcDominance - ethDominance - bnbDominance).toFixed(1); | |
| chartInstances.dominance = new Chart(ctx, { | |
| type: 'pie', | |
| data: { | |
| labels: ['Bitcoin', 'Ethereum', 'BNB', 'Others'], | |
| datasets: [{ | |
| data: [btcDominance, ethDominance, bnbDominance, othersDominance], | |
| backgroundColor: [ | |
| '#facc15', | |
| '#8f88ff', | |
| '#f472b6', | |
| '#94a3b8' | |
| ], | |
| borderWidth: 0, | |
| hoverOffset: 10 | |
| }] | |
| }, | |
| options: { | |
| responsive: true, | |
| maintainAspectRatio: false, | |
| plugins: { | |
| legend: { | |
| position: 'bottom', | |
| labels: { | |
| padding: 20, | |
| usePointStyle: true, | |
| font: { | |
| size: 13, | |
| weight: 600 | |
| } | |
| } | |
| }, | |
| tooltip: { | |
| backgroundColor: 'rgba(15, 23, 42, 0.95)', | |
| padding: 16, | |
| callbacks: { | |
| label: function(context) { | |
| return context.label + ': ' + context.parsed + '%'; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| }); | |
| } | |
| // Export chart instances for external access | |
| export { chartInstances }; | |