35761-vm/assets/js/main.js
Flatlogic Bot 766cac14c7 V.2.2
2025-11-16 16:03:04 +00:00

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();
});