File size: 6,079 Bytes
27815f8 e46f337 27815f8 d7d21d7 27815f8 d7d21d7 27815f8 e46f337 27815f8 e46f337 27815f8 e46f337 27815f8 e46f337 27815f8 e46f337 27815f8 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 |
// API Configuration
const API_URL = 'https://api.coingecko.com/api/v3/coins/markets';
const API_PARAMS = {
vs_currency: 'usd',
order: 'market_cap_desc',
per_page: 50,
page: 1,
sparkline: true,
price_change_percentage: '1h,24h,7d'
};
// DOM Elements
const cryptoGrid = document.getElementById('crypto-grid');
// Fetch crypto data
async function fetchCryptoData() {
try {
const response = await fetch(`${API_URL}?${new URLSearchParams(API_PARAMS)}`);
const data = await response.json();
return data;
} catch (error) {
console.error('Error fetching crypto data:', error);
return [];
}
}
// Calculate RSI (Relative Strength Index)
function calculateRSI(priceHistory, period = 14) {
if (!priceHistory || priceHistory.length < period + 1) return 50; // Default neutral value
// Calculate price changes
const changes = [];
for (let i = 1; i < priceHistory.length; i++) {
changes.push(priceHistory[i] - priceHistory[i-1]);
}
// Separate gains and losses
const gains = changes.map(change => change > 0 ? change : 0);
const losses = changes.map(change => change < 0 ? Math.abs(change) : 0);
// Calculate average gains and losses for the period
let avgGain = gains.slice(0, period).reduce((sum, gain) => sum + gain, 0) / period;
let avgLoss = losses.slice(0, period).reduce((sum, loss) => sum + loss, 0) / period;
// Calculate subsequent average gains/losses using smoothing
for (let i = period; i < changes.length; i++) {
avgGain = (avgGain * (period - 1) + gains[i]) / period;
avgLoss = (avgLoss * (period - 1) + losses[i]) / period;
}
// Avoid division by zero
if (avgLoss === 0) return 100;
// Calculate relative strength and RSI
const relativeStrength = avgGain / avgLoss;
const rsi = 100 - (100 / (1 + relativeStrength));
return Math.round(rsi * 100) / 100; // Round to 2 decimal places
}
// Create crypto card
function createCryptoCard(crypto) {
const rsi = calculateRSI(crypto.sparkline_in_7d.price);
const isPumping = crypto.price_change_percentage_24h > 0;
let rsiClass = 'rsi-medium';
if (rsi < 30) rsiClass = 'rsi-low';
if (rsi > 70) rsiClass = 'rsi-high';
const card = document.createElement('div');
card.className = `bg-gray-800 rounded-xl overflow-hidden shadow-lg smooth-transition hover:shadow-xl hover:-translate-y-1 ${isPumping ? 'border-l-4 border-primary-500' : 'border-l-4 border-red-500'}`;
card.innerHTML = `
<div class="p-6">
<div class="flex justify-between items-start">
<div class="flex items-center space-x-3">
<img src="${crypto.image}" alt="${crypto.name}" class="w-10 h-10 rounded-full">
<div>
<h3 class="font-bold text-lg">${crypto.symbol.toUpperCase()}</h3>
<p class="text-gray-400 text-sm">${crypto.name}</p>
</div>
</div>
<span class="px-3 py-1 rounded-full text-xs font-semibold ${rsiClass}">
RSI: ${rsi}
</span>
</div>
<div class="mt-4 h-16">
<canvas class="sparkline" data-prices="${crypto.sparkline_in_7d.price.join(',')}" data-color="${isPumping ? '#10b981' : '#ef4444'}"></canvas>
</div>
<div class="mt-6">
<div class="flex justify-between items-center mb-2">
<span class="text-gray-400">Price</span>
<span class="font-bold">${crypto.current_price.toLocaleString()}</span>
</div>
<div class="flex justify-between items-center mb-2">
<span class="text-gray-400">Market Cap</span>
<span class="font-bold">${(crypto.market_cap / 1000000000).toFixed(2)}B</span>
</div>
<div class="flex justify-between items-center">
<span class="text-gray-400">24h Change</span>
<span class="font-bold ${isPumping ? 'text-secondary-500' : 'text-red-500'}">
${isPumping ? '+' : ''}${crypto.price_change_percentage_24h.toFixed(2)}%
<i data-feather="${isPumping ? 'trending-up' : 'trending-down'}" class="inline ml-1"></i>
</span>
</div>
</div>
</div>
`;
return card;
}
// Initialize app
async function initApp() {
const cryptos = await fetchCryptoData();
cryptoGrid.innerHTML = '';
cryptos.forEach(crypto => {
const card = createCryptoCard(crypto);
cryptoGrid.appendChild(card);
});
// Render sparklines
document.querySelectorAll('.sparkline').forEach(canvas => {
const prices = canvas.dataset.prices.split(',').map(Number);
const color = canvas.dataset.color;
const ctx = canvas.getContext('2d');
// Set canvas size
canvas.width = canvas.offsetWidth;
canvas.height = canvas.offsetHeight;
// Calculate max and min for scaling
const max = Math.max(...prices);
const min = Math.min(...prices);
const range = max - min || 1; // Avoid division by zero
// Draw sparkline
ctx.beginPath();
ctx.strokeStyle = color;
ctx.lineWidth = 2;
prices.forEach((price, i) => {
const x = (canvas.width / (prices.length - 1)) * i;
const y = canvas.height - ((price - min) / range) * canvas.height;
if (i === 0) {
ctx.moveTo(x, y);
} else {
ctx.lineTo(x, y);
}
});
ctx.stroke();
});
feather.replace();
}
// Refresh data every 60 seconds
setInterval(initApp, 60000);
// Initial load
document.addEventListener('DOMContentLoaded', initApp); |