|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
const chartInstances = {};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function initMarketOverviewChart(data) {
|
|
|
const ctx = document.getElementById('market-overview-chart');
|
|
|
if (!ctx) return;
|
|
|
|
|
|
|
|
|
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();
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 }
|
|
|
}
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function initPriceChart(coinId, days = 7) {
|
|
|
const ctx = document.getElementById('price-chart');
|
|
|
if (!ctx) return;
|
|
|
|
|
|
|
|
|
if (chartInstances.price) {
|
|
|
chartInstances.price.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.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();
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
});
|
|
|
});
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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';
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
});
|
|
|
});
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 + '%';
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 { chartInstances };
|
|
|
|