// Crypto Intelligence Hub - Main JavaScript with Sidebar Navigation // Enhanced Pro Trading Terminal UI // ============================================================================= // Toast Notification System // ============================================================================= const ToastManager = { container: null, init() { this.container = document.getElementById('toast-container'); if (!this.container) { this.container = document.createElement('div'); this.container.id = 'toast-container'; this.container.className = 'toast-container'; document.body.appendChild(this.container); } }, show(message, type = 'info', duration = 5000) { if (!this.container) this.init(); const toast = document.createElement('div'); toast.className = `toast ${type}`; const icons = { success: 'fa-check-circle', error: 'fa-times-circle', warning: 'fa-exclamation-triangle', info: 'fa-info-circle' }; const titles = { success: 'Success', error: 'Error', warning: 'Warning', info: 'Info' }; toast.innerHTML = `
${titles[type] || titles.info}
${message}
`; this.container.appendChild(toast); // Auto remove after duration if (duration > 0) { setTimeout(() => this.remove(toast), duration); } return toast; }, remove(toast) { if (!toast) return; toast.classList.add('removing'); setTimeout(() => { if (toast.parentElement) { toast.parentElement.removeChild(toast); } }, 300); }, success(message, duration) { return this.show(message, 'success', duration); }, error(message, duration) { return this.show(message, 'error', duration); }, warning(message, duration) { return this.show(message, 'warning', duration); }, info(message, duration) { return this.show(message, 'info', duration); } }; // ============================================================================= // Global State // ============================================================================= const AppState = { currentTab: 'dashboard', data: {}, charts: {}, isLoading: false, sidebarOpen: false }; // ============================================================================= // Sidebar Navigation // ============================================================================= function toggleSidebar() { const sidebar = document.getElementById('sidebar'); const overlay = document.getElementById('sidebar-overlay'); if (sidebar && overlay) { sidebar.classList.toggle('active'); overlay.classList.toggle('active'); AppState.sidebarOpen = !AppState.sidebarOpen; } } function switchTab(tabId) { // Update nav items const navItems = document.querySelectorAll('.nav-item'); navItems.forEach(item => { if (item.dataset.tab === tabId) { item.classList.add('active'); } else { item.classList.remove('active'); } }); // Update tab panels const tabPanels = document.querySelectorAll('.tab-panel'); tabPanels.forEach(panel => { if (panel.id === `tab-${tabId}`) { panel.classList.add('active'); } else { panel.classList.remove('active'); } }); // Update page title const pageTitles = { 'dashboard': { title: 'Dashboard', subtitle: 'System Overview' }, 'market': { title: 'Market Data', subtitle: 'Real-time Cryptocurrency Prices' }, 'models': { title: 'AI Models', subtitle: 'Hugging Face Models' }, 'sentiment': { title: 'Sentiment Analysis', subtitle: 'AI-Powered Sentiment Detection' }, 'trading-assistant': { title: 'Trading Signals', subtitle: 'AI Trading Assistant' }, 'news': { title: 'Crypto News', subtitle: 'Latest News & Updates' }, 'settings': { title: 'Settings', subtitle: 'System Configuration' } }; const pageTitle = document.getElementById('page-title'); const pageSubtitle = document.getElementById('page-subtitle'); if (pageTitle && pageTitles[tabId]) { pageTitle.textContent = pageTitles[tabId].title; } if (pageSubtitle && pageTitles[tabId]) { pageSubtitle.textContent = pageTitles[tabId].subtitle; } // Update state AppState.currentTab = tabId; // Load tab data loadTabData(tabId); // Close sidebar on mobile after selection if (window.innerWidth <= 768) { toggleSidebar(); } } // ============================================================================= // Initialize App // ============================================================================= document.addEventListener('DOMContentLoaded', () => { console.log('🚀 Initializing Crypto Intelligence Hub...'); // Initialize toast manager ToastManager.init(); // Check API status checkAPIStatus(); // Load initial dashboard immediately setTimeout(() => { loadDashboard(); }, 100); // Auto-refresh every 30 seconds setInterval(() => { if (AppState.currentTab === 'dashboard') { loadDashboard(); } }, 30000); // Listen for trading pairs loaded event document.addEventListener('tradingPairsLoaded', function(e) { console.log('Trading pairs loaded:', e.detail.pairs.length); initTradingPairSelectors(); }); console.log('✅ App initialized successfully'); }); // Initialize trading pair selectors after pairs are loaded function initTradingPairSelectors() { // Initialize asset symbol selector const assetSymbolContainer = document.getElementById('asset-symbol-container'); if (assetSymbolContainer && window.TradingPairsLoader) { const pairs = window.TradingPairsLoader.getTradingPairs(); if (pairs && pairs.length > 0) { assetSymbolContainer.innerHTML = window.TradingPairsLoader.createTradingPairCombobox( 'asset-symbol', 'Select or type trading pair', 'BTCUSDT' ); } } // Initialize trading symbol selector const tradingSymbolContainer = document.getElementById('trading-symbol-container'); if (tradingSymbolContainer && window.TradingPairsLoader) { const pairs = window.TradingPairsLoader.getTradingPairs(); if (pairs && pairs.length > 0) { tradingSymbolContainer.innerHTML = window.TradingPairsLoader.createTradingPairCombobox( 'trading-symbol', 'Select or type trading pair', 'BTCUSDT' ); } } } // ============================================================================= // Tab Data Loading // ============================================================================= function loadTabData(tabId) { console.log(`Loading data for tab: ${tabId}`); switch(tabId) { case 'dashboard': loadDashboard(); break; case 'market': loadMarketData(); break; case 'models': loadModels(); break; case 'sentiment': // Sentiment tab is interactive, no auto-load needed break; case 'trading-assistant': // Trading assistant tab is interactive, no auto-load needed break; case 'news': loadNews(); break; case 'settings': loadSettings(); break; default: console.log('No specific loader for tab:', tabId); } } function refreshCurrentTab() { loadTabData(AppState.currentTab); ToastManager.success('Data refreshed successfully'); } // ============================================================================= // API Status Check // ============================================================================= async function checkAPIStatus() { try { const response = await fetch('/health'); const data = await response.json(); const statusIndicator = document.getElementById('sidebar-status'); if (statusIndicator) { if (data.status === 'healthy') { statusIndicator.textContent = 'System Active'; statusIndicator.parentElement.style.background = 'rgba(16, 185, 129, 0.15)'; statusIndicator.parentElement.style.borderColor = 'rgba(16, 185, 129, 0.3)'; } else { statusIndicator.textContent = 'System Error'; statusIndicator.parentElement.style.background = 'rgba(239, 68, 68, 0.15)'; statusIndicator.parentElement.style.borderColor = 'rgba(239, 68, 68, 0.3)'; } } } catch (error) { console.error('Error checking API status:', error); const statusIndicator = document.getElementById('sidebar-status'); if (statusIndicator) { statusIndicator.textContent = 'Connection Failed'; statusIndicator.parentElement.style.background = 'rgba(239, 68, 68, 0.15)'; statusIndicator.parentElement.style.borderColor = 'rgba(239, 68, 68, 0.3)'; } } } // ============================================================================= // Dashboard Loading // ============================================================================= async function loadDashboard() { console.log('📊 Loading dashboard...'); // Show loading state const statsElements = [ 'stat-total-resources', 'stat-free-resources', 'stat-models', 'stat-providers' ]; statsElements.forEach(id => { const el = document.getElementById(id); if (el) el.textContent = '...'; }); const systemStatusDiv = document.getElementById('system-status'); if (systemStatusDiv) { systemStatusDiv.innerHTML = '
Loading system status...
'; } try { // Load resources const resourcesRes = await fetch('/api/resources'); if (!resourcesRes.ok) { throw new Error(`Resources API returned ${resourcesRes.status}`); } const resourcesData = await resourcesRes.json(); console.log('Resources data:', resourcesData); if (resourcesData.success && resourcesData.summary) { const totalResources = resourcesData.summary.total_resources || 0; const freeResources = resourcesData.summary.free_resources || 0; const modelsAvailable = resourcesData.summary.models_available || 0; document.getElementById('stat-total-resources').textContent = totalResources; document.getElementById('stat-free-resources').textContent = freeResources; document.getElementById('stat-models').textContent = modelsAvailable; // Update sidebar stats const sidebarResources = document.getElementById('sidebar-resources'); const sidebarModels = document.getElementById('sidebar-models'); if (sidebarResources) sidebarResources.textContent = totalResources; if (sidebarModels) sidebarModels.textContent = modelsAvailable; // Load categories chart if (resourcesData.summary.categories) { createCategoriesChart(resourcesData.summary.categories); } } else { console.warn('Resources data format unexpected:', resourcesData); document.getElementById('stat-total-resources').textContent = '0'; document.getElementById('stat-free-resources').textContent = '0'; document.getElementById('stat-models').textContent = '0'; } // Load system status try { const statusRes = await fetch('/api/status'); if (statusRes.ok) { const statusData = await statusRes.json(); const providers = statusData.total_apis || statusData.total_providers || 0; document.getElementById('stat-providers').textContent = providers; // Display system status const systemStatusDiv = document.getElementById('system-status'); const healthStatus = statusData.system_health || 'unknown'; const healthClass = healthStatus === 'healthy' ? 'alert-success' : healthStatus === 'degraded' ? 'alert-warning' : 'alert-error'; systemStatusDiv.innerHTML = `
System Status: ${healthStatus}
Online APIs: ${statusData.online || 0}
Degraded APIs: ${statusData.degraded || 0}
Offline APIs: ${statusData.offline || 0}
Avg Response Time: ${statusData.avg_response_time_ms || 0}ms
Last Update: ${new Date(statusData.last_update || Date.now()).toLocaleString('en-US')}
`; } else { throw new Error('Status endpoint not available'); } } catch (statusError) { console.warn('Status endpoint error:', statusError); document.getElementById('stat-providers').textContent = '-'; const systemStatusDiv = document.getElementById('system-status'); if (systemStatusDiv) { systemStatusDiv.innerHTML = '
System status unavailable. Core features are operational.
'; } } console.log('✅ Dashboard loaded successfully'); } catch (error) { console.error('❌ Error loading dashboard:', error); ToastManager.error('Failed to load dashboard. Please check the backend.'); // Show error state const systemStatusDiv = document.getElementById('system-status'); if (systemStatusDiv) { systemStatusDiv.innerHTML = `
Failed to load dashboard data: ${error.message}
Please refresh or check backend status.
`; } // Set default values statsElements.forEach(id => { const el = document.getElementById(id); if (el) el.textContent = '0'; }); } } // Create Categories Chart function createCategoriesChart(categories) { const ctx = document.getElementById('categories-chart'); if (!ctx) return; // Check if Chart.js is loaded if (typeof Chart === 'undefined') { console.error('Chart.js is not loaded'); ctx.parentElement.innerHTML = '

Chart library not loaded

'; return; } if (AppState.charts.categories) { AppState.charts.categories.destroy(); } const labels = Object.keys(categories); const values = Object.values(categories); if (labels.length === 0) { ctx.parentElement.innerHTML = '

No category data available

'; return; } AppState.charts.categories = new Chart(ctx, { type: 'bar', data: { labels: labels, datasets: [{ label: 'Total Resources', data: values, backgroundColor: 'rgba(102, 126, 234, 0.6)', borderColor: 'rgba(102, 126, 234, 1)', borderWidth: 2 }] }, options: { responsive: true, maintainAspectRatio: true, plugins: { legend: { display: false } }, scales: { y: { beginAtZero: true } } } }); } // ============================================================================= // Market Data Loading // ============================================================================= async function loadMarketData() { console.log('💰 Loading market data...'); const marketDiv = document.getElementById('market-data'); const trendingDiv = document.getElementById('trending-coins'); const fgDiv = document.getElementById('fear-greed'); if (marketDiv) marketDiv.innerHTML = '
Loading market data...
'; if (trendingDiv) trendingDiv.innerHTML = '
Loading trending coins...
'; if (fgDiv) fgDiv.innerHTML = '
Loading Fear & Greed Index...
'; try { const response = await fetch('/api/market'); if (!response.ok) { throw new Error(`Market API returned ${response.status}`); } const data = await response.json(); if (data.cryptocurrencies && data.cryptocurrencies.length > 0) { marketDiv.innerHTML = `
${data.cryptocurrencies.map(coin => ` `).join('')}
# Name Price (USD) 24h Change 24h Volume Market Cap
${coin.rank || '-'} ${coin.image ? `` : ''} ${coin.symbol} ${coin.name} $${formatNumber(coin.price)} ${coin.change_24h >= 0 ? '↑' : '↓'} ${Math.abs(coin.change_24h || 0).toFixed(2)}% $${formatNumber(coin.volume_24h)} $${formatNumber(coin.market_cap)}
${data.total_market_cap ? `
Total Market Cap: $${formatNumber(data.total_market_cap)} | BTC Dominance: ${(data.btc_dominance || 0).toFixed(2)}%
` : ''} `; } else { marketDiv.innerHTML = '
No market data available
'; } // Load trending coins try { const trendingRes = await fetch('/api/trending'); if (trendingRes.ok) { const trendingData = await trendingRes.json(); if (trendingData.trending && trendingData.trending.length > 0) { trendingDiv.innerHTML = `
${trendingData.trending.map((coin, index) => `
#${index + 1}
${coin.symbol || coin.id} - ${coin.name || 'Unknown'} ${coin.market_cap_rank ? `
Market Cap Rank: ${coin.market_cap_rank}
` : ''}
${coin.score ? coin.score.toFixed(2) : 'N/A'}
`).join('')}
`; } else { trendingDiv.innerHTML = '
No trending data available
'; } } else { throw new Error('Trending endpoint not available'); } } catch (trendingError) { console.warn('Trending endpoint error:', trendingError); trendingDiv.innerHTML = '
Trending data unavailable
'; } // Load Fear & Greed Index try { const sentimentRes = await fetch('/api/sentiment'); if (sentimentRes.ok) { const sentimentData = await sentimentRes.json(); if (sentimentData.fear_greed_index !== undefined) { const fgValue = sentimentData.fear_greed_index; const fgLabel = sentimentData.fear_greed_label || 'Unknown'; let fgColor = 'var(--warning)'; if (fgValue >= 75) fgColor = 'var(--success)'; else if (fgValue >= 50) fgColor = 'var(--info)'; else if (fgValue >= 25) fgColor = 'var(--warning)'; else fgColor = 'var(--danger)'; fgDiv.innerHTML = `
${fgValue}
${fgLabel}
Market Fear & Greed Index
${sentimentData.timestamp ? `
Last Update: ${new Date(sentimentData.timestamp).toLocaleString('en-US')}
` : ''}
`; } else { fgDiv.innerHTML = '
Fear & Greed Index unavailable
'; } } else { throw new Error('Sentiment endpoint not available'); } } catch (fgError) { console.warn('Fear & Greed endpoint error:', fgError); fgDiv.innerHTML = '
Fear & Greed Index unavailable
'; } console.log('✅ Market data loaded successfully'); } catch (error) { console.error('❌ Error loading market data:', error); ToastManager.error('Failed to load market data'); if (marketDiv) marketDiv.innerHTML = `
Error loading market data: ${error.message}
`; if (trendingDiv) trendingDiv.innerHTML = '
Error loading trending coins
'; if (fgDiv) fgDiv.innerHTML = '
Error loading Fear & Greed Index
'; } } // ============================================================================= // News Loading (FIXED) // ============================================================================= async function fetchNewsFromAPI() { console.log('📥 Fetching news from CryptoCompare API...'); ToastManager.info('Fetching latest news from CryptoCompare...'); const newsListDiv = document.getElementById('news-list'); if (!newsListDiv) return; newsListDiv.innerHTML = '
Fetching news from CryptoCompare API...
'; try { const response = await fetch('/api/news/fetch?limit=50', { method: 'POST' }); if (!response.ok) { throw new Error(`Fetch API returned ${response.status}`); } const data = await response.json(); console.log('Fetch news result:', data); if (data.success) { ToastManager.success(`Successfully fetched and saved ${data.saved} news articles!`); // Reload news list loadNews(); } else { ToastManager.error(`Failed to fetch news: ${data.error}`); newsListDiv.innerHTML = `
Error: ${data.error}
`; } } catch (error) { console.error('❌ Error fetching news:', error); ToastManager.error('Failed to fetch news from API'); newsListDiv.innerHTML = `
Error fetching news: ${error.message}
`; } } // Utility function to format time ago function formatTimeAgo(dateString) { if (!dateString) return 'Unknown time'; const date = new Date(dateString); const now = new Date(); const diffMs = now - date; const diffSecs = Math.floor(diffMs / 1000); const diffMins = Math.floor(diffSecs / 60); const diffHours = Math.floor(diffMins / 60); const diffDays = Math.floor(diffHours / 24); if (diffSecs < 60) return 'Just now'; if (diffMins < 60) return `${diffMins} minute${diffMins > 1 ? 's' : ''} ago`; if (diffHours < 24) return `${diffHours} hour${diffHours > 1 ? 's' : ''} ago`; if (diffDays < 7) return `${diffDays} day${diffDays > 1 ? 's' : ''} ago`; return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: date.getFullYear() !== now.getFullYear() ? 'numeric' : undefined }); } async function loadNews() { console.log('📰 Loading news...'); const newsListDiv = document.getElementById('news-list'); if (!newsListDiv) return; newsListDiv.innerHTML = '
Loading news...
'; try { const response = await fetch('/api/news?limit=50'); if (!response.ok) { throw new Error(`News API returned ${response.status}`); } const data = await response.json(); console.log('News data:', data); if (data.success && data.news && data.news.length > 0) { // Sort by latest timestamp (published_date or analyzed_at) const sortedNews = [...data.news].sort((a, b) => { const dateA = new Date(a.published_date || a.analyzed_at || 0); const dateB = new Date(b.published_date || b.analyzed_at || 0); return dateB - dateA; }); newsListDiv.innerHTML = `
${sortedNews.map(article => { const timeAgo = formatTimeAgo(article.published_date || article.analyzed_at); const symbols = Array.isArray(article.related_symbols) ? article.related_symbols : (typeof article.related_symbols === 'string' ? (article.related_symbols.startsWith('[') ? JSON.parse(article.related_symbols) : article.related_symbols.split(',').map(s => s.trim())) : []); return `

${article.url ? `${article.title || 'Untitled'}` : (article.title || 'Untitled')}

${article.content ? `

${article.content.substring(0, 200)}${article.content.length > 200 ? '...' : ''}

` : ''}
${article.source || 'Unknown'}
${timeAgo}
${article.sentiment_label ? `${article.sentiment_label}` : ''}
${symbols.length > 0 ? `
${symbols.slice(0, 5).map(symbol => `${symbol}`).join('')} ${symbols.length > 5 ? `+${symbols.length - 5}` : ''}
` : ''}
`; }).join('')}
Showing ${sortedNews.length} article${sortedNews.length !== 1 ? 's' : ''}${data.source ? ` from ${data.source}` : ''}
`; console.log('✅ News loaded successfully'); ToastManager.success(`Loaded ${sortedNews.length} news articles`); } else { newsListDiv.innerHTML = '
No news articles available at the moment. Click "Fetch Latest News" to load articles.
'; console.warn('No news data available'); } } catch (error) { console.error('❌ Error loading news:', error); ToastManager.error('Failed to load news'); newsListDiv.innerHTML = `
Error loading news: ${error.message}
Please check your internet connection and try again.
`; } } // ============================================================================= // Models Loading // ============================================================================= async function loadModels() { console.log('🤖 Loading models...'); const modelsStatusDiv = document.getElementById('models-status'); const modelsListDiv = document.getElementById('models-list'); if (modelsStatusDiv) modelsStatusDiv.innerHTML = '
Loading models status...
'; if (modelsListDiv) modelsListDiv.innerHTML = '
Loading models list...
'; try { // Load models status const statusRes = await fetch('/api/models/status'); if (statusRes.ok) { const statusData = await statusRes.json(); modelsStatusDiv.innerHTML = `
Status: ${statusData.ok ? '✅ Active' : '⚠️ Partial'}
Pipelines Loaded: ${statusData.pipelines_loaded || 0}
Available Models: ${statusData.available_models?.length || 0}
HF Mode: ${statusData.hf_mode || 'unknown'}
Transformers Available: ${statusData.transformers_available ? '✅' : '❌'}
`; } else { throw new Error('Models status endpoint not available'); } // Load models list const listRes = await fetch('/api/models/list'); if (listRes.ok) { const listData = await listRes.json(); if (listData.models && listData.models.length > 0) { modelsListDiv.innerHTML = `
${listData.models.map(model => ` `).join('')}
Model ID Task Category Status
${model.model_id || model.key} ${model.task || 'N/A'} ${model.category || 'N/A'} ${model.loaded ? '✅ Loaded' : '❌ Not Loaded'}
`; } else { modelsListDiv.innerHTML = '
No models available
'; } } else { throw new Error('Models list endpoint not available'); } console.log('✅ Models loaded successfully'); } catch (error) { console.error('❌ Error loading models:', error); ToastManager.error('Failed to load models'); if (modelsStatusDiv) modelsStatusDiv.innerHTML = `
Error loading models status: ${error.message}
`; if (modelsListDiv) modelsListDiv.innerHTML = '
Error loading models list
'; } } async function initializeModels() { ToastManager.info('Initializing models... This may take a moment.'); try { const response = await fetch('/api/models/initialize', { method: 'POST' }); if (!response.ok) { throw new Error(`Initialize returned ${response.status}`); } const data = await response.json(); if (data.status === 'ok') { ToastManager.success('Models initialized successfully!'); loadModels(); // Reload models list } else { ToastManager.warning('Models partially initialized'); } } catch (error) { console.error('Error initializing models:', error); ToastManager.error('Failed to initialize models'); } } // ============================================================================= // Settings // ============================================================================= function loadSettings() { const apiInfoDiv = document.getElementById('api-info'); if (apiInfoDiv) { apiInfoDiv.innerHTML = `
API Base URL: ${window.location.origin}
Documentation: /docs
Health Check: /health
`; } } function saveSettings() { ToastManager.success('Settings saved successfully!'); } function toggleTheme() { document.body.classList.toggle('light-theme'); const themeSelect = document.getElementById('theme-select'); if (themeSelect) { themeSelect.value = document.body.classList.contains('light-theme') ? 'light' : 'dark'; } } function changeTheme(theme) { if (theme === 'light') { document.body.classList.add('light-theme'); } else { document.body.classList.remove('light-theme'); } } // ============================================================================= // Sentiment Analysis Functions with Visualizations // ============================================================================= // Create sentiment gauge chart function createSentimentGauge(containerId, sentimentValue, sentimentClass) { const container = document.getElementById(containerId); if (!container) return null; // Clear previous chart container.innerHTML = ''; // Create canvas const canvas = document.createElement('canvas'); canvas.id = `gauge-${containerId}`; canvas.width = 300; canvas.height = 150; container.appendChild(canvas); // Calculate gauge value (0-100, where 50 is neutral) let gaugeValue = 50; // neutral if (sentimentClass === 'bullish' || sentimentClass === 'positive') { gaugeValue = 50 + (sentimentValue * 50); // 50-100 } else if (sentimentClass === 'bearish' || sentimentClass === 'negative') { gaugeValue = 50 - (sentimentValue * 50); // 0-50 } gaugeValue = Math.max(0, Math.min(100, gaugeValue)); const ctx = canvas.getContext('2d'); const centerX = canvas.width / 2; const centerY = canvas.height / 2; const radius = 60; // Draw gauge background (semi-circle) ctx.beginPath(); ctx.arc(centerX, centerY + 20, radius, Math.PI, 0, false); ctx.lineWidth = 20; ctx.strokeStyle = 'rgba(31, 41, 55, 0.6)'; ctx.stroke(); // Draw gauge fill const startAngle = Math.PI; const endAngle = Math.PI + (Math.PI * (gaugeValue / 100)); ctx.beginPath(); ctx.arc(centerX, centerY + 20, radius, startAngle, endAngle, false); ctx.lineWidth = 20; ctx.lineCap = 'round'; let gaugeColor; if (gaugeValue >= 70) gaugeColor = '#10b981'; // green else if (gaugeValue >= 50) gaugeColor = '#3b82f6'; // blue else if (gaugeValue >= 30) gaugeColor = '#f59e0b'; // yellow else gaugeColor = '#ef4444'; // red ctx.strokeStyle = gaugeColor; ctx.stroke(); // Draw value text ctx.fillStyle = '#f9fafb'; ctx.font = 'bold 32px Inter, sans-serif'; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; ctx.fillText(Math.round(gaugeValue), centerX, centerY + 15); // Draw labels ctx.fillStyle = '#9ca3af'; ctx.font = '12px Inter, sans-serif'; ctx.textAlign = 'left'; ctx.fillText('Bearish', 20, centerY + 50); ctx.textAlign = 'right'; ctx.fillText('Bullish', canvas.width - 20, centerY + 50); return canvas; } // Get trend arrow SVG function getTrendArrow(sentimentClass) { const color = sentimentClass === 'bullish' ? 'var(--success)' : sentimentClass === 'bearish' ? 'var(--danger)' : 'var(--warning)'; const rotation = sentimentClass === 'bearish' ? 'rotate(180deg)' : sentimentClass === 'neutral' ? 'rotate(90deg)' : ''; return ` `; } // Create confidence bar function createConfidenceBar(confidence) { const confidencePercent = Math.round(confidence * 100); return `
Model Confidence ${confidencePercent}%
`; } async function analyzeGlobalSentiment() { ToastManager.info('Analyzing global market sentiment...'); const resultDiv = document.getElementById('global-sentiment-result'); if (resultDiv) { resultDiv.innerHTML = '
Analyzing...
'; } try { const response = await fetch('/api/sentiment/analyze', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ text: 'Overall cryptocurrency market sentiment analysis', mode: 'crypto' }) }); if (!response.ok) throw new Error(`API returned ${response.status}`); const data = await response.json(); if (data.available && data.sentiment) { const sentiment = data.sentiment.toUpperCase(); const confidence = data.confidence || 0; const sentimentClass = sentiment.includes('POSITIVE') || sentiment.includes('BULLISH') ? 'bullish' : sentiment.includes('NEGATIVE') || sentiment.includes('BEARISH') ? 'bearish' : 'neutral'; resultDiv.innerHTML = `

Global Market Sentiment

${sentiment}
${getTrendArrow(sentimentClass)} ${sentiment} ${getTrendArrow(sentimentClass)}
${createConfidenceBar(confidence)}

Model: ${data.model || 'AI Sentiment Analysis'} | Engine: ${data.engine || 'N/A'}

`; // Create gauge chart after DOM update setTimeout(() => { createSentimentGauge('global-sentiment-gauge', confidence, sentimentClass); }, 100); ToastManager.success('Sentiment analysis complete!'); } else { resultDiv.innerHTML = '
Sentiment analysis unavailable
'; } } catch (error) { console.error('Error analyzing sentiment:', error); resultDiv.innerHTML = `
Error: ${error.message}
`; ToastManager.error('Failed to analyze sentiment'); } } async function analyzeAssetSentiment() { const symbol = document.getElementById('asset-symbol')?.value; const text = document.getElementById('asset-sentiment-text')?.value; if (!symbol) { ToastManager.warning('Please select a trading pair'); return; } ToastManager.info('Analyzing asset sentiment...'); const resultDiv = document.getElementById('asset-sentiment-result'); if (resultDiv) { resultDiv.innerHTML = '
Analyzing...
'; } try { const response = await fetch('/api/sentiment/analyze', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ text: text || `Sentiment analysis for ${symbol}`, mode: 'crypto', symbol: symbol }) }); if (!response.ok) throw new Error(`API returned ${response.status}`); const data = await response.json(); if (data.available && data.sentiment) { const sentiment = data.sentiment.toUpperCase(); const confidence = data.confidence || 0; const sentimentClass = sentiment.includes('POSITIVE') || sentiment.includes('BULLISH') ? 'bullish' : sentiment.includes('NEGATIVE') || sentiment.includes('BEARISH') ? 'bearish' : 'neutral'; resultDiv.innerHTML = `

${symbol} Sentiment

${sentiment}
${getTrendArrow(sentimentClass)} ${sentiment} ${getTrendArrow(sentimentClass)}
${createConfidenceBar(confidence)}

Model: ${data.model || 'AI Sentiment Analysis'} | Engine: ${data.engine || 'N/A'}

`; // Create gauge chart after DOM update setTimeout(() => { createSentimentGauge('asset-sentiment-gauge', confidence, sentimentClass); }, 100); ToastManager.success('Asset sentiment analysis complete!'); } else { resultDiv.innerHTML = '
Sentiment analysis unavailable
'; } } catch (error) { console.error('Error analyzing asset sentiment:', error); resultDiv.innerHTML = `
Error: ${error.message}
`; ToastManager.error('Failed to analyze asset sentiment'); } } async function analyzeSentiment() { const text = document.getElementById('sentiment-text')?.value; const mode = document.getElementById('sentiment-mode')?.value || 'auto'; if (!text || text.trim() === '') { ToastManager.warning('Please enter text to analyze'); return; } ToastManager.info('Analyzing sentiment...'); const resultDiv = document.getElementById('sentiment-result'); if (resultDiv) { resultDiv.innerHTML = '
Analyzing...
'; } try { const response = await fetch('/api/sentiment/analyze', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ text, mode }) }); if (!response.ok) throw new Error(`API returned ${response.status}`); const data = await response.json(); if (data.available && data.sentiment) { const sentiment = data.sentiment.toUpperCase(); const confidence = data.confidence || 0; const sentimentClass = sentiment.includes('POSITIVE') || sentiment.includes('BULLISH') ? 'bullish' : sentiment.includes('NEGATIVE') || sentiment.includes('BEARISH') ? 'bearish' : 'neutral'; resultDiv.innerHTML = `

Sentiment Analysis Result

${sentiment}
${getTrendArrow(sentimentClass)} ${sentiment} ${getTrendArrow(sentimentClass)}
${createConfidenceBar(confidence)}

Text: ${text.substring(0, 100)}${text.length > 100 ? '...' : ''}
Model: ${data.model || 'AI Sentiment Analysis'} | Engine: ${data.engine || 'N/A'}

`; // Create gauge chart after DOM update setTimeout(() => { createSentimentGauge('sentiment-gauge', confidence, sentimentClass); }, 100); ToastManager.success('Sentiment analysis complete!'); } else { resultDiv.innerHTML = '
Sentiment analysis unavailable
'; } } catch (error) { console.error('Error analyzing sentiment:', error); resultDiv.innerHTML = `
Error: ${error.message}
`; ToastManager.error('Failed to analyze sentiment'); } } // ============================================================================= // Trading Assistant // ============================================================================= async function runTradingAssistant() { const symbol = document.getElementById('trading-symbol')?.value; const context = document.getElementById('trading-context')?.value; if (!symbol) { ToastManager.warning('Please select a trading symbol'); return; } ToastManager.info('Generating trading signal...'); const resultDiv = document.getElementById('trading-assistant-result'); if (resultDiv) { resultDiv.innerHTML = '
Analyzing...
'; } try { const response = await fetch('/api/trading/decision', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ symbol: symbol, context: context || `Trading decision for ${symbol}` }) }); if (!response.ok) throw new Error(`API returned ${response.status}`); const data = await response.json(); if (data.decision) { const decision = data.decision.toUpperCase(); const confidence = data.confidence ? (data.confidence * 100).toFixed(2) : 'N/A'; const decisionClass = decision === 'BUY' ? 'bullish' : decision === 'SELL' ? 'bearish' : 'neutral'; resultDiv.innerHTML = `

${symbol} Trading Signal

${decision}
${confidence}%
Confidence
${data.reasoning ? `

Reasoning: ${data.reasoning}

` : ''}

Model: ${data.model || 'AI Trading Assistant'}

`; ToastManager.success('Trading signal generated!'); } else { resultDiv.innerHTML = '
Trading signal unavailable
'; } } catch (error) { console.error('Error generating trading signal:', error); resultDiv.innerHTML = `
Error: ${error.message}
`; ToastManager.error('Failed to generate trading signal'); } } // ============================================================================= // Utility Functions // ============================================================================= function formatNumber(num) { if (num === null || num === undefined) return '0'; if (num >= 1e12) return (num / 1e12).toFixed(2) + 'T'; if (num >= 1e9) return (num / 1e9).toFixed(2) + 'B'; if (num >= 1e6) return (num / 1e6).toFixed(2) + 'M'; if (num >= 1e3) return (num / 1e3).toFixed(2) + 'K'; return num.toFixed(2); } // ============================================================================= // Export for global access // ============================================================================= window.AppState = AppState; window.ToastManager = ToastManager; window.toggleSidebar = toggleSidebar; window.switchTab = switchTab; window.refreshCurrentTab = refreshCurrentTab; window.loadDashboard = loadDashboard; window.loadMarketData = loadMarketData; window.loadModels = loadModels; window.initializeModels = initializeModels; window.loadNews = loadNews; window.fetchNewsFromAPI = fetchNewsFromAPI; window.loadSettings = loadSettings; window.saveSettings = saveSettings; window.toggleTheme = toggleTheme; window.changeTheme = changeTheme; window.analyzeGlobalSentiment = analyzeGlobalSentiment; window.analyzeAssetSentiment = analyzeAssetSentiment; window.analyzeSentiment = analyzeSentiment; window.runTradingAssistant = runTradingAssistant; window.formatNumber = formatNumber; console.log('✅ App.js loaded successfully');