174 lines
7.3 KiB
JavaScript
174 lines
7.3 KiB
JavaScript
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 = '<span class="badge bg-success-subtle text-success-emphasis">Above SMA</span>';
|
|
} else if (currentPrice < sma) {
|
|
signalCell.innerHTML = '<span class="badge bg-danger-subtle text-danger-emphasis">Below SMA</span>';
|
|
} else {
|
|
signalCell.innerHTML = '<span class="badge bg-secondary-subtle text-secondary-emphasis">At SMA</span>';
|
|
}
|
|
} 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 = `
|
|
<i class="bi bi-exclamation-triangle-fill me-3"></i>
|
|
<div>
|
|
<strong class="d-block">${alert.type} on ${alert.symbol}</strong>
|
|
${alert.message}
|
|
</div>
|
|
`;
|
|
alertsContainer.appendChild(alertEl);
|
|
});
|
|
} else {
|
|
alertsContainer.innerHTML = '<p class="text-muted">No alerts triggered yet. The system is monitoring the market.</p>';
|
|
}
|
|
}
|
|
|
|
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();
|
|
});
|