/** * BIST Predictor — API İletişim Modülü * Backend REST API ve SSE stream yönetimi. */ const API = { BASE_URL: window.location.origin, /** SSE event source instance */ _eventSource: null, _sseListeners: [], // ─── HTTP İstekleri ───────────────────────────────────────────────────── async _fetch(endpoint, options = {}) { try { const url = `${this.BASE_URL}/api${endpoint}`; const response = await fetch(url, { headers: { 'Content-Type': 'application/json' }, ...options, }); if (!response.ok) { const error = await response.json().catch(() => ({ detail: response.statusText })); throw new Error(error.detail || `HTTP ${response.status}`); } return await response.json(); } catch (error) { console.error(`API Error [${endpoint}]:`, error); throw error; } }, async get(endpoint) { return this._fetch(endpoint); }, async post(endpoint, data = null) { const options = { method: 'POST' }; if (data) options.body = JSON.stringify(data); return this._fetch(endpoint, options); }, // ─── Dashboard ────────────────────────────────────────────────────────── async getDashboard(horizon = 10) { return this.get(`/dashboard?horizon=${horizon}`); }, async getStocks() { return this.get('/stocks'); }, async getStockDetail(symbol, horizon = 10) { return this.get(`/stock/${symbol}?horizon=${horizon}`); }, // ─── Tahminler ────────────────────────────────────────────────────────── async getPredictions(symbol, horizon = 10) { return this.get(`/predictions/${symbol}?horizon=${horizon}`); }, async triggerPrediction(symbol, horizons = '10') { return this.post(`/predict/${symbol}?horizons=${horizons}`); }, async triggerAllPredictions() { return this.post('/predict-all'); }, // ─── Güven Puanı ──────────────────────────────────────────────────────── async getConfidence(symbol, horizon = 10) { return this.get(`/confidence/${symbol}?horizon=${horizon}`); }, async getConfidenceRanking(horizon = 10) { return this.get(`/confidence-ranking?horizon=${horizon}`); }, // ─── Karşılaştırma ────────────────────────────────────────────────────── async getComparison(symbol, horizon = 10) { return this.get(`/comparison/${symbol}?horizon=${horizon}`); }, // ─── Veri Yönetimi ────────────────────────────────────────────────────── async loadInitialData() { return this.post('/data/load'); }, async updateStockData(symbol) { return this.post(`/data/update/${symbol}`); }, async triggerComparison() { return this.post('/data/compare'); }, // ─── Sistem ───────────────────────────────────────────────────────────── async getSystemStatus() { return this.get('/system/status'); }, async getSystemLogs(limit = 50) { return this.get(`/system/logs?limit=${limit}`); }, // ─── SSE (Server-Sent Events) ──────────────────────────────────────────── connectSSE() { if (this._eventSource) { this._eventSource.close(); } this._eventSource = new EventSource(`${this.BASE_URL}/api/stream`); this._eventSource.onopen = () => { console.log('SSE bağlantısı kuruldu'); this._notifyListeners('connected', {}); }; this._eventSource.onmessage = (event) => { try { const parsed = JSON.parse(event.data); const { type, data, timestamp } = parsed; this._notifyListeners(type, data, timestamp); } catch (e) { console.warn('SSE parse hatası:', e); } }; this._eventSource.onerror = (error) => { console.warn('SSE bağlantı hatası, yeniden bağlanılıyor...'); this._notifyListeners('disconnected', {}); // Otomatik yeniden bağlanma (EventSource bunu otomatik yapar) }; }, disconnectSSE() { if (this._eventSource) { this._eventSource.close(); this._eventSource = null; } }, onSSE(callback) { this._sseListeners.push(callback); }, offSSE(callback) { this._sseListeners = this._sseListeners.filter(cb => cb !== callback); }, _notifyListeners(type, data, timestamp) { for (const listener of this._sseListeners) { try { listener(type, data, timestamp); } catch (e) { console.error('SSE listener hatası:', e); } } }, };