document.addEventListener('DOMContentLoaded', function () { const POLLING_INTERVAL = 3000; // 3 seconds for price/analysis const ALERT_POLLING_INTERVAL = 10000; // 10 seconds for alerts const liveTickers = [ { symbol: 'BITCOIN', elementId: 'live-crypto-row-btc', lastPrice: 0 }, { symbol: 'ETHEREUM', elementId: 'live-crypto-row-eth', lastPrice: 0 } ]; async function fetchAndupdate(ticker, rowElement) { try { const response = await fetch(`api/ticker.php?symbol=${ticker.symbol}`); if (!response.ok) throw new Error(`HTTP error! Status: ${response.status}`); const data = await response.json(); if (data.error) throw new Error(`API Error: ${data.error}`); updateRow(data, ticker, rowElement); } catch (error) { console.error(`Failed to fetch live price for ${ticker.symbol}:`, error); const priceCell = rowElement.querySelector('.price'); if (priceCell) priceCell.textContent = 'Error'; } } function updateRow(data, ticker, rowElement) { const priceCell = rowElement.querySelector('.price'); const symbolCell = rowElement.querySelector('.symbol'); const exchangeCell = rowElement.querySelector('.exchange'); if (!priceCell || !symbolCell || !exchangeCell) { console.error(`One or more required elements not found in row for ${ticker.symbol}`); return; } const currentPrice = parseFloat(data.price); priceCell.textContent = `${currentPrice.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`; symbolCell.textContent = data.symbol.replace('_SPBL', ''); exchangeCell.textContent = data.exchange; if (ticker.lastPrice !== 0 && currentPrice !== ticker.lastPrice) { const flashClass = currentPrice > ticker.lastPrice ? 'flash-success' : 'flash-danger'; priceCell.classList.add(flashClass); setTimeout(() => priceCell.classList.remove(flashClass), 750); } ticker.lastPrice = currentPrice; } async function fetchAnalysis(ticker, rowElement) { try { const response = await fetch(`api/analysis.php?symbol=${ticker.symbol}&indicators=sma,rsi,macd&period=20`); if (!response.ok) throw new Error(`HTTP error! Status: ${response.status}`); const data = await response.json(); if (data.error) throw new Error(`API Error: ${data.error}`); updateAnalysisData(data, rowElement, ticker.lastPrice); } catch (error) { console.error(`Failed to fetch analysis for ${ticker.symbol}:`, error); rowElement.querySelector('.sma').textContent = 'Error'; rowElement.querySelector('.rsi').textContent = 'Error'; } } function updateAnalysisData(data, rowElement, currentPrice) { const smaCell = rowElement.querySelector('.sma'); const signalCell = rowElement.querySelector('.signal'); const rsiCell = rowElement.querySelector('.rsi'); const macdCell = rowElement.querySelector('.macd'); const macdSignalCell = rowElement.querySelector('.macd-signal'); if (!smaCell || !signalCell || !rsiCell || !macdCell || !macdSignalCell) return; const sma = parseFloat(data.values.sma); const rsi = parseFloat(data.values.rsi); const macdData = data.values.macd; if (!isNaN(sma)) { smaCell.textContent = sma.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 }); if (currentPrice > sma) { signalCell.innerHTML = 'Above SMA'; } else if (currentPrice < sma) { signalCell.innerHTML = 'Below SMA'; } else { signalCell.innerHTML = 'At SMA'; } } else { smaCell.textContent = '-'; signalCell.textContent = '-'; } if (!isNaN(rsi)) { rsiCell.textContent = rsi.toFixed(2); } else { rsiCell.textContent = '-'; } if (macdData && typeof macdData.macd !== 'undefined' && typeof macdData.signal !== 'undefined') { const macd = parseFloat(macdData.macd); const macdSignal = parseFloat(macdData.signal); if (!isNaN(macd)) { macdCell.textContent = macd.toFixed(4); } if (!isNaN(macdSignal)) { macdSignalCell.textContent = macdSignal.toFixed(4); } } else { macdCell.textContent = '-'; macdSignalCell.textContent = '-'; } } async function fetchAndDisplayAlerts() { const alertsContainer = document.getElementById('alerts-container'); if (!alertsContainer) return; let allAlerts = []; for (const ticker of liveTickers) { try { const response = await fetch(`api/alerts.php?symbol=${ticker.symbol}`); if (!response.ok) throw new Error(`HTTP error! Status: ${response.status}`); const data = await response.json(); if (data.alerts && data.alerts.length > 0) { data.alerts.forEach(alert => { allAlerts.push({ symbol: data.symbol, ...alert }); }); } } catch (error) { console.error(`Failed to fetch alerts for ${ticker.symbol}:`, error); } } if (allAlerts.length > 0) { alertsContainer.innerHTML = ''; // Clear previous alerts allAlerts.forEach(alert => { const alertEl = document.createElement('div'); alertEl.className = 'alert alert-danger d-flex align-items-center'; alertEl.innerHTML = `
${alert.type} on ${alert.symbol} ${alert.message}
`; alertsContainer.appendChild(alertEl); }); } else { alertsContainer.innerHTML = '

No alerts triggered yet. The system is monitoring the market.

'; } } function initializeLiveTickers() { liveTickers.forEach(ticker => { const rowElement = document.getElementById(ticker.elementId); if (!rowElement) { console.error(`Element with ID #${ticker.elementId} not found for symbol ${ticker.symbol}.`); return; } const fetchAll = () => { fetchAndupdate(ticker, rowElement).then(() => { fetchAnalysis(ticker, rowElement); }); }; fetchAll(); setInterval(fetchAll, POLLING_INTERVAL); }); // Setup alert polling fetchAndDisplayAlerts(); setInterval(fetchAndDisplayAlerts, ALERT_POLLING_INTERVAL); } initializeLiveTickers(); });