Your Name
feat: UI improvements and error suppression - Enhanced dashboard and market pages with improved header buttons, logo, and currency symbol display - Stopped animated ticker - Removed pie chart legends - Added error suppressor for external service errors (SSE, Permissions-Policy warnings) - Improved header button prominence and icon appearance - Enhanced logo with glow effects and better design - Fixed currency symbol visibility in market tables
8b7b267
| /** | |
| * 🚀 REAL DATA Trading Assistant | |
| * 100% Real Data - NO FAKE DATA - NO MOCK DATA | |
| * @version 7.0.0 - REAL DATA ONLY | |
| */ | |
| import HTSEngine from './hts-engine.js'; | |
| // Configuration - ONLY REAL DATA | |
| const CONFIG = { | |
| binance: 'https://api.binance.com/api/v3', | |
| updateInterval: 5000, // 5 seconds | |
| agentInterval: 60000, // 60 seconds | |
| maxSignals: 50, | |
| timeout: 10000 | |
| }; | |
| // Crypto Assets | |
| const CRYPTOS = [ | |
| { symbol: 'BTC', name: 'Bitcoin', binance: 'BTCUSDT', icon: '₿' }, | |
| { symbol: 'ETH', name: 'Ethereum', binance: 'ETHUSDT', icon: 'Ξ' }, | |
| { symbol: 'BNB', name: 'BNB', binance: 'BNBUSDT', icon: '🔸' }, | |
| { symbol: 'SOL', name: 'Solana', binance: 'SOLUSDT', icon: '◎' }, | |
| { symbol: 'XRP', name: 'Ripple', binance: 'XRPUSDT', icon: '✕' }, | |
| { symbol: 'ADA', name: 'Cardano', binance: 'ADAUSDT', icon: '₳' } | |
| ]; | |
| // Strategies | |
| const STRATEGIES = { | |
| 'hts-hybrid': { | |
| name: '🔥 HTS Hybrid System', | |
| description: 'RSI+MACD (40%) + SMC (25%) + Patterns + Sentiment + ML', | |
| badge: 'PREMIUM', | |
| accuracy: '85%', | |
| timeframe: '1h-4h', | |
| risk: 'Medium', | |
| avgReturn: '+12.5%' | |
| }, | |
| 'trend-momentum': { | |
| name: '📈 Trend + Momentum', | |
| description: 'RSI, MACD, EMA for trending markets', | |
| badge: 'STANDARD', | |
| accuracy: '78%', | |
| timeframe: '4h-1d', | |
| risk: 'Low', | |
| avgReturn: '+8.3%' | |
| }, | |
| 'breakout-pro': { | |
| name: '⚡ Breakout Pro', | |
| description: 'Volatility breakout with volume confirmation', | |
| badge: 'STANDARD', | |
| accuracy: '75%', | |
| timeframe: '1h-4h', | |
| risk: 'Medium-High', | |
| avgReturn: '+15.2%' | |
| } | |
| }; | |
| /** | |
| * Real Data Trading System | |
| */ | |
| class RealDataTradingSystem { | |
| constructor() { | |
| this.selectedCrypto = 'BTC'; | |
| this.selectedStrategy = 'hts-hybrid'; | |
| this.isAgentRunning = false; | |
| this.signals = []; | |
| this.marketData = {}; // Store all real market data | |
| this.technicalData = {}; // Store technical indicators | |
| this.chart = null; | |
| this.htsEngine = new HTSEngine(); | |
| this.agentInterval = null; | |
| this.priceInterval = null; | |
| } | |
| /** | |
| * Initialize | |
| */ | |
| async init() { | |
| console.log('[REAL] 🚀 Initializing with 100% Real Data...'); | |
| this.renderCryptos(); | |
| this.renderStrategies(); | |
| this.bindEvents(); | |
| // Load real data | |
| await this.loadAllMarketData(); | |
| // Initialize chart | |
| await this.initChart(); | |
| // Start updates | |
| this.startPriceUpdates(); | |
| this.showToast('✅ System Ready - 100% Real Data from Binance!', 'success'); | |
| this.updateTime(); | |
| console.log('[REAL] ✅ Ready with real data!'); | |
| } | |
| /** | |
| * Load ALL market data from Binance | |
| */ | |
| async loadAllMarketData() { | |
| console.log('[REAL] Loading all market data from Binance...'); | |
| for (const crypto of CRYPTOS) { | |
| try { | |
| // Get 24hr ticker data (REAL) | |
| const ticker = await this.fetch24hrTicker(crypto.binance); | |
| // Get klines for technical analysis (REAL) | |
| const klines = await this.fetchKlines(crypto.binance, '1h', 100); | |
| // Calculate technical indicators from REAL data | |
| const technical = this.calculateTechnicalIndicators(klines); | |
| // Store everything | |
| this.marketData[crypto.symbol] = { | |
| symbol: crypto.symbol, | |
| binance: crypto.binance, | |
| price: parseFloat(ticker.lastPrice), | |
| change24h: parseFloat(ticker.priceChangePercent), | |
| high24h: parseFloat(ticker.highPrice), | |
| low24h: parseFloat(ticker.lowPrice), | |
| volume24h: parseFloat(ticker.volume), | |
| quoteVolume24h: parseFloat(ticker.quoteVolume), | |
| trades24h: parseInt(ticker.count), | |
| openPrice: parseFloat(ticker.openPrice), | |
| closePrice: parseFloat(ticker.lastPrice), | |
| klines: klines, | |
| timestamp: Date.now() | |
| }; | |
| this.technicalData[crypto.symbol] = technical; | |
| // Update display | |
| this.updateCryptoDisplay(crypto.symbol); | |
| console.log(`[REAL] ${crypto.symbol}: $${ticker.lastPrice} (${ticker.priceChangePercent}%)`); | |
| } catch (error) { | |
| console.error(`[REAL] Error loading ${crypto.symbol}:`, error); | |
| } | |
| } | |
| } | |
| /** | |
| * Fetch 24hr ticker from Binance (REAL DATA) | |
| */ | |
| async fetch24hrTicker(symbol) { | |
| const url = `${CONFIG.binance}/ticker/24hr?symbol=${symbol}`; | |
| console.log(`[REAL] Fetching 24hr ticker: ${url}`); | |
| const response = await fetch(url, { | |
| signal: AbortSignal.timeout(CONFIG.timeout) | |
| }); | |
| if (!response.ok) { | |
| throw new Error(`HTTP ${response.status}`); | |
| } | |
| return await response.json(); | |
| } | |
| /** | |
| * Fetch klines from Binance (REAL DATA) | |
| */ | |
| async fetchKlines(symbol, interval = '1h', limit = 100) { | |
| const url = `${CONFIG.binance}/klines?symbol=${symbol}&interval=${interval}&limit=${limit}`; | |
| console.log(`[REAL] Fetching klines: ${url}`); | |
| const response = await fetch(url, { | |
| signal: AbortSignal.timeout(CONFIG.timeout) | |
| }); | |
| if (!response.ok) { | |
| throw new Error(`HTTP ${response.status}`); | |
| } | |
| const data = await response.json(); | |
| return data.map(candle => ({ | |
| timestamp: candle[0], | |
| open: parseFloat(candle[1]), | |
| high: parseFloat(candle[2]), | |
| low: parseFloat(candle[3]), | |
| close: parseFloat(candle[4]), | |
| volume: parseFloat(candle[5]), | |
| closeTime: candle[6], | |
| quoteVolume: parseFloat(candle[7]), | |
| trades: parseInt(candle[8]) | |
| })); | |
| } | |
| /** | |
| * Calculate technical indicators from REAL data | |
| */ | |
| calculateTechnicalIndicators(klines) { | |
| if (!klines || klines.length < 50) { | |
| return null; | |
| } | |
| const closes = klines.map(k => k.close); | |
| const highs = klines.map(k => k.high); | |
| const lows = klines.map(k => k.low); | |
| const volumes = klines.map(k => k.volume); | |
| // RSI (14) | |
| const rsi = this.calculateRSI(closes, 14); | |
| // MACD | |
| const macd = this.calculateMACD(closes); | |
| // EMA (20, 50, 200) | |
| const ema20 = this.calculateEMA(closes, 20); | |
| const ema50 = this.calculateEMA(closes, 50); | |
| const ema200 = closes.length >= 200 ? this.calculateEMA(closes, 200) : null; | |
| // Support/Resistance | |
| const support = Math.min(...lows.slice(-20)); | |
| const resistance = Math.max(...highs.slice(-20)); | |
| // Volume analysis | |
| const avgVolume = volumes.reduce((a, b) => a + b, 0) / volumes.length; | |
| const currentVolume = volumes[volumes.length - 1]; | |
| const volumeRatio = currentVolume / avgVolume; | |
| return { | |
| rsi: rsi, | |
| macd: macd, | |
| ema20: ema20, | |
| ema50: ema50, | |
| ema200: ema200, | |
| support: support, | |
| resistance: resistance, | |
| avgVolume: avgVolume, | |
| currentVolume: currentVolume, | |
| volumeRatio: volumeRatio, | |
| trend: ema20 > ema50 ? 'bullish' : 'bearish' | |
| }; | |
| } | |
| /** | |
| * Calculate RSI | |
| */ | |
| calculateRSI(prices, period = 14) { | |
| if (prices.length < period + 1) return null; | |
| let gains = 0; | |
| let losses = 0; | |
| for (let i = prices.length - period; i < prices.length; i++) { | |
| const change = prices[i] - prices[i - 1]; | |
| if (change > 0) { | |
| gains += change; | |
| } else { | |
| losses -= change; | |
| } | |
| } | |
| const avgGain = gains / period; | |
| const avgLoss = losses / period; | |
| if (avgLoss === 0) return 100; | |
| const rs = avgGain / avgLoss; | |
| const rsi = 100 - (100 / (1 + rs)); | |
| return rsi; | |
| } | |
| /** | |
| * Calculate MACD | |
| */ | |
| calculateMACD(prices) { | |
| if (prices.length < 26) return null; | |
| const ema12 = this.calculateEMA(prices, 12); | |
| const ema26 = this.calculateEMA(prices, 26); | |
| if (!ema12 || !ema26) return null; | |
| const macdLine = ema12 - ema26; | |
| return { | |
| value: macdLine, | |
| signal: macdLine > 0 ? 'bullish' : 'bearish' | |
| }; | |
| } | |
| /** | |
| * Calculate EMA | |
| */ | |
| calculateEMA(prices, period) { | |
| if (prices.length < period) return null; | |
| const multiplier = 2 / (period + 1); | |
| let ema = prices.slice(0, period).reduce((a, b) => a + b, 0) / period; | |
| for (let i = period; i < prices.length; i++) { | |
| ema = (prices[i] - ema) * multiplier + ema; | |
| } | |
| return ema; | |
| } | |
| /** | |
| * Update crypto display with REAL data | |
| */ | |
| updateCryptoDisplay(symbol) { | |
| const data = this.marketData[symbol]; | |
| if (!data) return; | |
| const priceEl = document.getElementById(`price-${symbol}`); | |
| const changeEl = document.getElementById(`change-${symbol}`); | |
| if (priceEl) { | |
| priceEl.textContent = this.formatPrice(data.price); | |
| } | |
| if (changeEl) { | |
| const changeText = data.change24h >= 0 ? `+${data.change24h.toFixed(2)}%` : `${data.change24h.toFixed(2)}%`; | |
| changeEl.textContent = changeText; | |
| changeEl.className = `crypto-change ${data.change24h >= 0 ? 'positive' : 'negative'}`; | |
| } | |
| // Update current price if selected | |
| if (symbol === this.selectedCrypto) { | |
| const currentPriceEl = document.getElementById('current-price'); | |
| if (currentPriceEl) { | |
| currentPriceEl.textContent = this.formatPrice(data.price); | |
| } | |
| } | |
| } | |
| /** | |
| * Open crypto modal with REAL data | |
| */ | |
| openCryptoModal(symbol) { | |
| const crypto = CRYPTOS.find(c => c.symbol === symbol); | |
| const data = this.marketData[symbol]; | |
| const technical = this.technicalData[symbol]; | |
| if (!crypto || !data) { | |
| this.showToast('❌ No data available', 'error'); | |
| return; | |
| } | |
| // Update modal with REAL data | |
| document.getElementById('crypto-modal-title').textContent = `${crypto.name} (${symbol})`; | |
| document.getElementById('modal-price').textContent = this.formatPrice(data.price); | |
| const changeEl = document.getElementById('modal-change'); | |
| changeEl.textContent = data.change24h >= 0 ? `+${data.change24h.toFixed(2)}%` : `${data.change24h.toFixed(2)}%`; | |
| changeEl.className = `info-value ${data.change24h >= 0 ? 'success' : 'danger'}`; | |
| // REAL 24h data | |
| document.getElementById('modal-high').textContent = this.formatPrice(data.high24h); | |
| document.getElementById('modal-low').textContent = this.formatPrice(data.low24h); | |
| document.getElementById('modal-volume').textContent = this.formatVolume(data.volume24h); | |
| document.getElementById('modal-mcap').textContent = this.formatVolume(data.quoteVolume24h); | |
| // REAL technical indicators | |
| if (technical) { | |
| document.getElementById('modal-rsi').textContent = technical.rsi ? technical.rsi.toFixed(1) : 'N/A'; | |
| document.getElementById('modal-macd').textContent = technical.macd ? technical.macd.signal : 'N/A'; | |
| document.getElementById('modal-ema').textContent = technical.ema50 ? this.formatPrice(technical.ema50) : 'N/A'; | |
| document.getElementById('modal-support').textContent = technical.support ? this.formatPrice(technical.support) : 'N/A'; | |
| document.getElementById('modal-resistance').textContent = technical.resistance ? this.formatPrice(technical.resistance) : 'N/A'; | |
| } | |
| window.openModal('crypto-modal'); | |
| } | |
| /** | |
| * Open strategy modal with REAL data | |
| */ | |
| openStrategyModal(strategyKey) { | |
| const strategy = STRATEGIES[strategyKey]; | |
| if (!strategy) return; | |
| document.getElementById('strategy-modal-title').textContent = strategy.name; | |
| document.getElementById('modal-success-rate').textContent = strategy.accuracy; | |
| document.getElementById('modal-timeframe').textContent = strategy.timeframe; | |
| document.getElementById('modal-risk').textContent = strategy.risk; | |
| document.getElementById('modal-return').textContent = strategy.avgReturn; | |
| document.getElementById('strategy-description').textContent = strategy.description; | |
| window.openModal('strategy-modal'); | |
| } | |
| /** | |
| * Open signal modal with REAL data | |
| */ | |
| openSignalModal(index) { | |
| const signal = this.signals[index]; | |
| if (!signal) return; | |
| document.getElementById('signal-modal-title').textContent = `${signal.symbol} ${signal.signal.toUpperCase()} Signal`; | |
| const typeEl = document.getElementById('signal-type'); | |
| typeEl.textContent = signal.signal.toUpperCase(); | |
| typeEl.className = `info-value ${signal.signal === 'buy' ? 'success' : 'danger'}`; | |
| document.getElementById('signal-confidence').textContent = signal.confidence.toFixed(0) + '%'; | |
| document.getElementById('signal-entry').textContent = this.formatPrice(signal.price); | |
| document.getElementById('signal-sl').textContent = this.formatPrice(signal.stopLoss); | |
| document.getElementById('signal-tp').textContent = this.formatPrice(signal.takeProfit); | |
| const rr = Math.abs((signal.takeProfit - signal.price) / (signal.price - signal.stopLoss)); | |
| document.getElementById('signal-rr').textContent = `1:${rr.toFixed(1)}`; | |
| window.openModal('signal-modal'); | |
| } | |
| /** | |
| * Analyze with REAL data | |
| */ | |
| async analyze() { | |
| const btn = document.getElementById('analyze-btn'); | |
| if (!btn) return; | |
| btn.disabled = true; | |
| btn.innerHTML = '<span class="loading"></span> ANALYZING REAL DATA...'; | |
| try { | |
| const crypto = CRYPTOS.find(c => c.symbol === this.selectedCrypto); | |
| const data = this.marketData[this.selectedCrypto]; | |
| if (!data || !data.klines) { | |
| throw new Error('No real data available'); | |
| } | |
| this.showToast(`Analyzing ${this.selectedCrypto} with real data...`, 'info'); | |
| // Use REAL klines data | |
| const analysis = await this.htsEngine.analyze(data.klines, this.selectedCrypto); | |
| this.addSignal({ | |
| symbol: this.selectedCrypto, | |
| signal: analysis.finalSignal, | |
| confidence: analysis.confidence, | |
| price: analysis.currentPrice, | |
| stopLoss: analysis.stopLoss, | |
| takeProfit: analysis.takeProfitLevels[0]?.level || 0, | |
| strategy: STRATEGIES[this.selectedStrategy].name, | |
| timestamp: new Date(), | |
| realData: true // Mark as real data | |
| }); | |
| this.showToast(`✅ Analysis Complete (Real Data)!`, 'success'); | |
| } catch (error) { | |
| console.error('[REAL] Analysis error:', error); | |
| this.showToast(`❌ Analysis failed: ${error.message}`, 'error'); | |
| } finally { | |
| btn.disabled = false; | |
| btn.innerHTML = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/></svg><span>ANALYZE NOW</span>'; | |
| } | |
| } | |
| /** | |
| * Start agent with REAL data | |
| */ | |
| async startAgent() { | |
| if (this.isAgentRunning) return; | |
| this.isAgentRunning = true; | |
| document.getElementById('start-agent').style.display = 'none'; | |
| document.getElementById('stop-agent').style.display = 'block'; | |
| document.getElementById('agent-status').textContent = 'Active 🟢'; | |
| document.getElementById('agent-pairs').textContent = CRYPTOS.length; | |
| this.showToast('🤖 AI Agent Started (Real Data Only)!', 'success'); | |
| // Scan immediately | |
| await this.agentScan(); | |
| // Then scan periodically | |
| this.agentInterval = setInterval(() => { | |
| this.agentScan(); | |
| }, CONFIG.agentInterval); | |
| console.log('[REAL] Agent started with real data'); | |
| } | |
| /** | |
| * Agent scan with REAL data | |
| */ | |
| async agentScan() { | |
| console.log('[REAL] 🔍 Agent scanning with real data...'); | |
| for (const crypto of CRYPTOS) { | |
| try { | |
| // Refresh real data | |
| const ticker = await this.fetch24hrTicker(crypto.binance); | |
| const klines = await this.fetchKlines(crypto.binance, '1h', 100); | |
| // Analyze with REAL data | |
| const analysis = await this.htsEngine.analyze(klines, crypto.symbol); | |
| if (analysis.confidence >= 75 && analysis.finalSignal !== 'hold') { | |
| this.addSignal({ | |
| symbol: crypto.symbol, | |
| signal: analysis.finalSignal, | |
| confidence: analysis.confidence, | |
| price: analysis.currentPrice, | |
| stopLoss: analysis.stopLoss, | |
| takeProfit: analysis.takeProfitLevels[0]?.level || 0, | |
| strategy: 'HTS Hybrid', | |
| timestamp: new Date(), | |
| realData: true | |
| }); | |
| console.log(`[REAL] Signal: ${crypto.symbol} ${analysis.finalSignal.toUpperCase()} (${analysis.confidence.toFixed(0)}%)`); | |
| } | |
| } catch (error) { | |
| console.error(`[REAL] Agent error for ${crypto.symbol}:`, error); | |
| } | |
| } | |
| } | |
| /** | |
| * Stop agent | |
| */ | |
| stopAgent() { | |
| if (!this.isAgentRunning) return; | |
| this.isAgentRunning = false; | |
| document.getElementById('start-agent').style.display = 'block'; | |
| document.getElementById('stop-agent').style.display = 'none'; | |
| document.getElementById('agent-status').textContent = 'Stopped 🔴'; | |
| if (this.agentInterval) { | |
| clearInterval(this.agentInterval); | |
| this.agentInterval = null; | |
| } | |
| this.showToast('🤖 AI Agent Stopped', 'info'); | |
| console.log('[REAL] Agent stopped'); | |
| } | |
| /** | |
| * Start price updates with REAL data | |
| */ | |
| startPriceUpdates() { | |
| if (this.priceInterval) return; | |
| this.priceInterval = setInterval(async () => { | |
| await this.loadAllMarketData(); | |
| this.updateTime(); | |
| }, CONFIG.updateInterval); | |
| console.log('[REAL] Price updates started (every 5s with real data)'); | |
| } | |
| /** | |
| * Add signal | |
| */ | |
| addSignal(signal) { | |
| this.signals.unshift(signal); | |
| if (this.signals.length > CONFIG.maxSignals) { | |
| this.signals = this.signals.slice(0, CONFIG.maxSignals); | |
| } | |
| this.renderSignals(); | |
| document.getElementById('total-signals').textContent = this.signals.length; | |
| } | |
| /** | |
| * Render signals | |
| */ | |
| renderSignals() { | |
| const container = document.getElementById('signals-container'); | |
| if (!container) return; | |
| if (this.signals.length === 0) { | |
| container.innerHTML = ` | |
| <div class="empty-state"> | |
| <svg class="empty-icon" viewBox="0 0 24 24"> | |
| <circle cx="12" cy="12" r="2"/> | |
| <path d="M16.24 7.76a6 6 0 0 1 0 8.49m-8.48-.01a6 6 0 0 1 0-8.49m11.31-2.82a10 10 0 0 1 0 14.14m-14.14 0a10 10 0 0 1 0-14.14"/> | |
| </svg> | |
| <div class="empty-text">No signals yet</div> | |
| <div class="empty-subtext">Start the agent or analyze manually</div> | |
| </div> | |
| `; | |
| return; | |
| } | |
| container.innerHTML = this.signals.map((signal, index) => ` | |
| <div class="signal-card ${signal.signal}" ondblclick="window.realSystem.openSignalModal(${index})" style="cursor: pointer;"> | |
| <div class="signal-header"> | |
| <div class="signal-left"> | |
| <span class="signal-badge ${signal.signal}"> | |
| <svg viewBox="0 0 24 24" fill="none" stroke="currentColor"> | |
| ${signal.signal === 'buy' ? | |
| '<polyline points="18 15 12 9 6 15"/>' : | |
| '<polyline points="6 9 12 15 18 9"/>'} | |
| </svg> | |
| ${signal.signal.toUpperCase()} ${signal.realData ? '✓' : ''} | |
| </span> | |
| <span class="signal-symbol">${signal.symbol}</span> | |
| </div> | |
| <div class="signal-time"> | |
| <svg viewBox="0 0 24 24"> | |
| <circle cx="12" cy="12" r="10"/> | |
| <polyline points="12 6 12 12 16 14"/> | |
| </svg> | |
| ${signal.timestamp.toLocaleTimeString()} | |
| </div> | |
| </div> | |
| <div class="signal-body"> | |
| <div class="signal-item"> | |
| <div class="signal-item-label"> | |
| <svg viewBox="0 0 24 24"> | |
| <line x1="12" y1="1" x2="12" y2="23"/> | |
| <path d="M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6"/> | |
| </svg> | |
| Entry Price | |
| </div> | |
| <div class="signal-item-value value-price">${this.formatPrice(signal.price)}</div> | |
| </div> | |
| <div class="signal-item"> | |
| <div class="signal-item-label"> | |
| <svg viewBox="0 0 24 24"> | |
| <circle cx="12" cy="12" r="10"/> | |
| </svg> | |
| Confidence | |
| </div> | |
| <div class="signal-item-value value-confidence">${signal.confidence.toFixed(0)}%</div> | |
| </div> | |
| <div class="signal-item"> | |
| <div class="signal-item-label"> | |
| <svg viewBox="0 0 24 24"> | |
| <path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/> | |
| </svg> | |
| Stop Loss | |
| </div> | |
| <div class="signal-item-value value-stop">${this.formatPrice(signal.stopLoss)}</div> | |
| </div> | |
| <div class="signal-item"> | |
| <div class="signal-item-label"> | |
| <svg viewBox="0 0 24 24"> | |
| <polyline points="22 7 13.5 15.5 8.5 10.5 2 17"/> | |
| </svg> | |
| Take Profit | |
| </div> | |
| <div class="signal-item-value value-target">${this.formatPrice(signal.takeProfit)}</div> | |
| </div> | |
| </div> | |
| </div> | |
| `).join(''); | |
| } | |
| /** | |
| * Render cryptos | |
| */ | |
| renderCryptos() { | |
| const container = document.getElementById('crypto-grid'); | |
| if (!container) return; | |
| container.innerHTML = CRYPTOS.map(crypto => ` | |
| <div class="crypto-card ${crypto.symbol === this.selectedCrypto ? 'active' : ''}" | |
| data-symbol="${crypto.symbol}"> | |
| <div class="crypto-header"> | |
| <div class="crypto-icon">${crypto.icon}</div> | |
| <div class="crypto-info"> | |
| <div class="crypto-symbol">${crypto.symbol}</div> | |
| <div class="crypto-name">${crypto.name}</div> | |
| </div> | |
| </div> | |
| <div class="crypto-price" id="price-${crypto.symbol}">Loading...</div> | |
| <div class="crypto-change" id="change-${crypto.symbol}">--</div> | |
| </div> | |
| `).join(''); | |
| // Add event listeners | |
| container.querySelectorAll('.crypto-card').forEach(card => { | |
| card.addEventListener('click', (e) => { | |
| if (e.detail === 1) { | |
| setTimeout(() => { | |
| if (e.detail === 1) { | |
| this.selectCrypto(card.dataset.symbol); | |
| } | |
| }, 200); | |
| } | |
| }); | |
| card.addEventListener('dblclick', () => { | |
| this.openCryptoModal(card.dataset.symbol); | |
| }); | |
| }); | |
| } | |
| /** | |
| * Render strategies | |
| */ | |
| renderStrategies() { | |
| const container = document.getElementById('strategy-grid'); | |
| if (!container) return; | |
| container.innerHTML = Object.entries(STRATEGIES).map(([key, strategy]) => ` | |
| <div class="strategy-card ${strategy.badge === 'PREMIUM' ? 'hts' : ''} ${key === this.selectedStrategy ? 'active' : ''}" | |
| data-strategy="${key}"> | |
| <div class="strategy-header"> | |
| <div class="strategy-info"> | |
| <div class="strategy-name"> | |
| <svg viewBox="0 0 24 24"> | |
| <circle cx="12" cy="12" r="10"/> | |
| <path d="M12 16v-4"/> | |
| <path d="M12 8h.01"/> | |
| </svg> | |
| ${strategy.name} | |
| </div> | |
| <div class="strategy-desc">${strategy.description}</div> | |
| </div> | |
| <div class="strategy-badge badge-${strategy.badge.toLowerCase()}">${strategy.badge}</div> | |
| </div> | |
| <div class="strategy-stats"> | |
| <div class="strategy-stat"> | |
| <svg viewBox="0 0 24 24"> | |
| <circle cx="12" cy="12" r="10"/> | |
| </svg> | |
| <span>${strategy.accuracy}</span> | |
| </div> | |
| <div class="strategy-stat"> | |
| <svg viewBox="0 0 24 24"> | |
| <circle cx="12" cy="12" r="10"/> | |
| <polyline points="12 6 12 12 16 14"/> | |
| </svg> | |
| <span>${strategy.timeframe}</span> | |
| </div> | |
| </div> | |
| </div> | |
| `).join(''); | |
| // Add event listeners | |
| container.querySelectorAll('.strategy-card').forEach(card => { | |
| card.addEventListener('click', (e) => { | |
| if (e.detail === 1) { | |
| setTimeout(() => { | |
| if (e.detail === 1) { | |
| this.selectStrategy(card.dataset.strategy); | |
| } | |
| }, 200); | |
| } | |
| }); | |
| card.addEventListener('dblclick', () => { | |
| this.openStrategyModal(card.dataset.strategy); | |
| }); | |
| }); | |
| } | |
| /** | |
| * Select crypto | |
| */ | |
| selectCrypto(symbol) { | |
| this.selectedCrypto = symbol; | |
| document.querySelectorAll('.crypto-card').forEach(card => { | |
| card.classList.toggle('active', card.dataset.symbol === symbol); | |
| }); | |
| if (this.chart) { | |
| const crypto = CRYPTOS.find(c => c.symbol === symbol); | |
| this.chart.setSymbol(`BINANCE:${crypto.binance}`, '60'); | |
| } | |
| const data = this.marketData[symbol]; | |
| if (data) { | |
| document.getElementById('current-price').textContent = this.formatPrice(data.price); | |
| } | |
| this.showToast(`Selected ${symbol}`, 'info'); | |
| } | |
| /** | |
| * Select strategy | |
| */ | |
| selectStrategy(strategy) { | |
| this.selectedStrategy = strategy; | |
| document.querySelectorAll('.strategy-card').forEach(card => { | |
| card.classList.toggle('active', card.dataset.strategy === strategy); | |
| }); | |
| this.showToast(`Strategy: ${STRATEGIES[strategy].name}`, 'info'); | |
| } | |
| /** | |
| * Bind events | |
| */ | |
| bindEvents() { | |
| document.getElementById('start-agent')?.addEventListener('click', () => this.startAgent()); | |
| document.getElementById('stop-agent')?.addEventListener('click', () => this.stopAgent()); | |
| document.getElementById('analyze-btn')?.addEventListener('click', () => this.analyze()); | |
| document.getElementById('refresh-btn')?.addEventListener('click', () => this.refresh()); | |
| } | |
| /** | |
| * Initialize chart | |
| */ | |
| async initChart() { | |
| const crypto = CRYPTOS.find(c => c.symbol === this.selectedCrypto); | |
| try { | |
| this.chart = new TradingView.widget({ | |
| autosize: true, | |
| symbol: `BINANCE:${crypto.binance}`, | |
| interval: '60', | |
| timezone: 'Etc/UTC', | |
| theme: 'dark', | |
| style: '1', | |
| locale: 'en', | |
| toolbar_bg: '#0f172a', | |
| enable_publishing: false, | |
| hide_side_toolbar: false, | |
| allow_symbol_change: true, | |
| container_id: 'chart-container', | |
| studies: ['RSI@tv-basicstudies', 'MACD@tv-basicstudies', 'Volume@tv-basicstudies'], | |
| disabled_features: ['use_localstorage_for_settings'], | |
| enabled_features: ['study_templates'], | |
| overrides: { | |
| 'paneProperties.background': '#020617', | |
| 'paneProperties.backgroundType': 'solid', | |
| 'mainSeriesProperties.candleStyle.upColor': '#10b981', | |
| 'mainSeriesProperties.candleStyle.downColor': '#ef4444', | |
| 'mainSeriesProperties.candleStyle.borderUpColor': '#10b981', | |
| 'mainSeriesProperties.candleStyle.borderDownColor': '#ef4444', | |
| 'mainSeriesProperties.candleStyle.wickUpColor': '#10b981', | |
| 'mainSeriesProperties.candleStyle.wickDownColor': '#ef4444' | |
| } | |
| }); | |
| console.log('[REAL] TradingView chart initialized'); | |
| } catch (error) { | |
| console.error('[REAL] Chart error:', error); | |
| } | |
| } | |
| /** | |
| * Refresh | |
| */ | |
| async refresh() { | |
| this.showToast('🔄 Refreshing real data...', 'info'); | |
| await this.loadAllMarketData(); | |
| this.showToast('✅ Real data refreshed!', 'success'); | |
| } | |
| /** | |
| * Update time | |
| */ | |
| updateTime() { | |
| const now = new Date(); | |
| document.getElementById('last-update').textContent = now.toLocaleTimeString(); | |
| } | |
| /** | |
| * Format price | |
| */ | |
| formatPrice(price) { | |
| if (typeof price !== 'number') return '$0.00'; | |
| if (price < 1) { | |
| return `$${price.toFixed(4)}`; | |
| } else if (price < 100) { | |
| return `$${price.toFixed(2)}`; | |
| } else { | |
| return `$${price.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`; | |
| } | |
| } | |
| /** | |
| * Format volume | |
| */ | |
| formatVolume(volume) { | |
| if (typeof volume !== 'number') return '$0'; | |
| if (volume >= 1e9) { | |
| return `$${(volume / 1e9).toFixed(2)}B`; | |
| } else if (volume >= 1e6) { | |
| return `$${(volume / 1e6).toFixed(2)}M`; | |
| } else if (volume >= 1e3) { | |
| return `$${(volume / 1e3).toFixed(2)}K`; | |
| } else { | |
| return `$${volume.toFixed(2)}`; | |
| } | |
| } | |
| /** | |
| * Show toast | |
| */ | |
| showToast(message, type = 'info') { | |
| const container = document.getElementById('toast-container'); | |
| if (!container) return; | |
| const icons = { | |
| success: '✅', | |
| error: '❌', | |
| info: 'ℹ️', | |
| warning: '⚠️' | |
| }; | |
| const toast = document.createElement('div'); | |
| toast.className = 'toast'; | |
| toast.innerHTML = ` | |
| <div class="toast-content"> | |
| <div class="toast-icon">${icons[type]}</div> | |
| <div class="toast-message">${message}</div> | |
| </div> | |
| `; | |
| container.appendChild(toast); | |
| setTimeout(() => { | |
| toast.style.animation = 'toastSlideIn 0.3s ease-out reverse'; | |
| setTimeout(() => toast.remove(), 300); | |
| }, 3000); | |
| } | |
| } | |
| // Initialize | |
| document.addEventListener('DOMContentLoaded', () => { | |
| const system = new RealDataTradingSystem(); | |
| system.init(); | |
| window.realSystem = system; | |
| }); | |