|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export async function loadFearGreedIndex() {
|
|
|
try {
|
|
|
console.log('[Fear & Greed] Loading index...');
|
|
|
|
|
|
|
|
|
let response = await fetch('https://api.alternative.me/fng/?limit=1');
|
|
|
|
|
|
if (!response.ok) {
|
|
|
console.warn('[Fear & Greed] Primary API failed, trying fallback...');
|
|
|
|
|
|
response = await fetch('/api/sentiment/global');
|
|
|
}
|
|
|
|
|
|
if (!response.ok) {
|
|
|
throw new Error('All APIs failed');
|
|
|
}
|
|
|
|
|
|
const data = await response.json();
|
|
|
|
|
|
|
|
|
let value = 50;
|
|
|
let timestamp = new Date().toISOString();
|
|
|
|
|
|
if (data.data && data.data[0]) {
|
|
|
|
|
|
value = parseInt(data.data[0].value);
|
|
|
timestamp = data.data[0].timestamp;
|
|
|
} else if (data.fear_greed_index) {
|
|
|
|
|
|
value = data.fear_greed_index;
|
|
|
}
|
|
|
|
|
|
console.log('[Fear & Greed] Loaded value:', value);
|
|
|
|
|
|
|
|
|
renderFearGreedGauge(value);
|
|
|
|
|
|
|
|
|
updateFearGreedText(value, timestamp);
|
|
|
|
|
|
return { value, timestamp };
|
|
|
} catch (error) {
|
|
|
console.error('[Fear & Greed] Load error:', error);
|
|
|
|
|
|
|
|
|
const fallbackValue = 50;
|
|
|
renderFearGreedGauge(fallbackValue);
|
|
|
updateFearGreedText(fallbackValue, new Date().toISOString());
|
|
|
|
|
|
return { value: fallbackValue, timestamp: new Date().toISOString() };
|
|
|
}
|
|
|
}
|
|
|
|
|
|
function renderFearGreedGauge(value) {
|
|
|
const gauge = document.getElementById('sentiment-gauge');
|
|
|
if (!gauge) {
|
|
|
console.warn('[Fear & Greed] Gauge element not found');
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
let label = 'Neutral', color = '#eab308';
|
|
|
if (value < 25) { label = 'Extreme Fear'; color = '#ef4444'; }
|
|
|
else if (value < 45) { label = 'Fear'; color = '#f97316'; }
|
|
|
else if (value < 55) { label = 'Neutral'; color = '#eab308'; }
|
|
|
else if (value < 75) { label = 'Greed'; color = '#22c55e'; }
|
|
|
else { label = 'Extreme Greed'; color = '#10b981'; }
|
|
|
|
|
|
gauge.innerHTML = `
|
|
|
<div class="gauge-container">
|
|
|
<div class="gauge-bar">
|
|
|
<div class="gauge-fill" style="width: ${value}%; background: ${color}; transition: width 0.5s ease;"></div>
|
|
|
<div class="gauge-indicator" style="left: ${value}%; transition: left 0.5s ease;">
|
|
|
<span class="gauge-value">${value}</span>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="gauge-labels">
|
|
|
<span style="color: #ef4444;">Extreme Fear</span>
|
|
|
<span style="color: #eab308;">Neutral</span>
|
|
|
<span style="color: #10b981;">Extreme Greed</span>
|
|
|
</div>
|
|
|
<div class="gauge-result" style="color: ${color}; font-size: 1.25rem; font-weight: 700; margin-top: 1rem;">
|
|
|
${label}
|
|
|
</div>
|
|
|
</div>
|
|
|
`;
|
|
|
}
|
|
|
|
|
|
function updateFearGreedText(value, timestamp) {
|
|
|
|
|
|
const valueEl = document.getElementById('fng-value');
|
|
|
if (valueEl) {
|
|
|
valueEl.textContent = value;
|
|
|
valueEl.style.fontSize = '2rem';
|
|
|
valueEl.style.fontWeight = '700';
|
|
|
}
|
|
|
|
|
|
|
|
|
const sentimentEl = document.getElementById('fng-sentiment');
|
|
|
if (sentimentEl) {
|
|
|
let label = 'Neutral';
|
|
|
if (value < 25) label = 'Extreme Fear';
|
|
|
else if (value < 45) label = 'Fear';
|
|
|
else if (value < 55) label = 'Neutral';
|
|
|
else if (value < 75) label = 'Greed';
|
|
|
else label = 'Extreme Greed';
|
|
|
|
|
|
sentimentEl.textContent = label;
|
|
|
}
|
|
|
|
|
|
|
|
|
const timeEl = document.getElementById('fng-timestamp');
|
|
|
if (timeEl) {
|
|
|
const date = new Date(timestamp);
|
|
|
timeEl.textContent = `Updated: ${date.toLocaleTimeString()}`;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
export function startFearGreedAutoRefresh() {
|
|
|
loadFearGreedIndex();
|
|
|
setInterval(() => {
|
|
|
loadFearGreedIndex();
|
|
|
}, 5 * 60 * 1000);
|
|
|
}
|
|
|
|
|
|
|
|
|
window.loadFearGreedIndex = loadFearGreedIndex;
|
|
|
window.startFearGreedAutoRefresh = startFearGreedAutoRefresh;
|
|
|
|