Files
hack/tools/scripts/llm-key-hunter/results/_cache/content/ansh0079_-retailedge-trading-tournament.json
T
OpenCode 68a9071cf1 feat: reconcile local master with origin (exa/hot-platform hunters, leaks ledger, latest results)
Local branch had diverged from origin/master (sibling commits on the same base). Rewrote local history linearly on top of origin/master, folding in all local content: exa/hot-platform discovery hunters, leaks ledger, nightly sweep orchestrator, updated .gitignore and skill docs, plus the latest hunt output and vault state. Remote-only files (vault keys, channel scripts) were restored rather than dropped, so the resulting tree is a full union of both sides.
2026-08-07 12:53:47 +08:00

1 line
23 KiB
JSON

{"repo": "ansh0079/-retailedge-trading-tournament", "files": {"api-service.js": {"58a9bce0fe7c8dc00b12fd210532e5db88f2d45a": {"t": 1786068267.6545653, "n": 18450, "h": "50a7b27942035b09", "c": "// api-service.js - Secure backend API proxy for all financial data\nrequire('dotenv').config();\nconst fetch = require('node-fetch');\n\nclass APIService {\n constructor() {\n // API keys - FMP for financial data, AI models for analysis\n // All keys must be set via environment variables for security\n this.apiKeys = {\n FMP: process.env.FMP_API_KEY,\n ANTHROPIC: process.env.ANTHROPIC_API_KEY,\n DEEPSEEK: process.env.DEEPSEEK_API_KEY,\n KIMI: process.env.KIMI_API_KEY,\n GEMINI: process.env.GEMINI_API_KEY\n };\n\n // Request counters for rate limiting\n this.requestCounts = {};\n\n console.log('✅ API Service initialized');\n console.log(` FMP Key: ${this.apiKeys.FMP ? this.apiKeys.FMP.substring(0, 10) + '...' : 'NOT SET'}`);\n console.log(` Claude Key: ${this.apiKeys.ANTHROPIC ? this.apiKeys.ANTHROPIC.substring(0, 15) + '...' : 'NOT SET'}`);\n console.log(` DeepSeek Key: ${this.apiKeys.DEEPSEEK ? this.apiKeys.DEEPSEEK.substring(0, 10) + '...' : 'NOT SET'}`);\n console.log(` Kimi Key: ${this.apiKeys.KIMI ? 'SET' : 'NOT SET'}`);\n console.log(` Gemini Key: ${this.apiKeys.GEMINI ? 'SET' : 'NOT SET'}`);\n\n // Warn if critical keys are missing\n if (!this.apiKeys.FMP) {\n console.error('⚠️ WARNING: FMP_API_KEY not set - financial data will not work');\n }\n if (!this.apiKeys.ANTHROPIC) {\n console.error('⚠️ WARNING: ANTHROPIC_API_KEY not set - AI analysis will not work');\n }\n }\n\n // ═══════════════════════════════════════════════════════════════════════════\n // FINANCIAL MODELING PREP (FMP) - Primary API\n // ═══════════════════════════════════════════════════════════════════════════\n\n async getFMPData(endpoint, symbol, params = {}) {\n const queryParams = new URLSearchParams({\n symbol,\n apikey: this.apiKeys.FMP,\n ...params\n });\n\n const url = `https://financialmodelingprep.com/stable/${endpoint}?${queryParams}`;\n const response = await fetch(url);\n\n if (!response.ok) {\n throw new Error(`FMP API error: ${response.status}`);\n }\n\n return await response.json();\n }\n\n async getFMPBatch(endpoint, symbols, params = {}) {\n const queryParams = new URLSearchParams({\n symbols: symbols.join(','),\n apikey: this.apiKeys.FMP,\n ...params\n });\n\n const url = `https://financialmodelingprep.com/stable/${endpoint}?${queryParams}`;\n const response = await fetch(url);\n\n if (!response.ok) {\n throw new Error(`FMP Batch API error: ${response.status}`);\n }\n\n return await response.json();\n }\n\n // ═══════════════════════════════════════════════════════════════════════════\n // STOCK QUOTE & PRICE DATA\n // ═══════════════════════════════════════════════════════════════════════════\n\n async getQuote(symbol) {\n return await this.getFMPData('quote', symbol);\n }\n\n async getBatchQuotes(symbols) {\n try {\n // FMP paid tier: 300 calls/minute\n // Fetch enriched data (quote + metrics + ratios + rating) for each stock\n const results = [];\n \n for (let i = 0; i < symbols.length; i++) {\n try {\n const symbol = symbols[i];\n \n // Fetch all data in parallel (4 API calls per stock)\n const [quoteRes, metricsRes, ratiosRes, ratingRes] = await Promise.all([\n fetch(`https://financialmodelingprep.com/stable/quote?symbol=${symbol}&apikey=${this.apiKeys.FMP}`),\n fetch(`https://financialmodelingprep.com/stable/key-metrics-ttm?symbol=${symbol}&apikey=${this.apiKeys.FMP}`),\n fetch(`https://financialmodelingprep.com/stable/ratios?symbol=${symbol}&apikey=${this.apiKeys.FMP}`),\n fetch(`https://financialmodelingprep.com/stable/grades-consensus?symbol=${symbol}&apikey=${this.apiKeys.FMP}`)\n ]);\n \n const quote = quoteRes.ok ? (await quoteRes.json())[0] : null;\n const metrics = metricsRes.ok ? (await metricsRes.json())[0] : null;\n const ratios = ratiosRes.ok ? (await ratiosRes.json())[0] : null;\n const rating = ratingRes.ok ? await ratingRes.json() : null;\n \n if (quote) {\n // Merge enriched data\n const enrichedQuote = {\n ...quote,\n // PE from ratios\n pe: ratios?.priceEarningsRatio || ratios?.priceToEarningsRatio || quote.pe || null,\n // ROE from key-metrics (already percentage)\n roe: metrics?.roeTTM || metrics?.roe || null,\n roa: metrics?.roaTTM || metrics?.roa || null,\n debtToEquity: ratios?.debtEquityRatio || ratios?.debtToEquityRatio || null,\n currentRatio: ratios?.currentRatio || null,\n revenueGrowth: metrics?.revenuePerShareTTM || null,\n // Analyst rating\n rating: rating?.consensus || null,\n analystRating: rating?.consensus || null\n };\n \n results.push(enrichedQuote);\n }\n \n // Delay to stay under 300 calls/minute (100ms = ~150 calls/min with 4 parallel calls)\n if (i < symbols.length - 1) {\n await new Promise(resolve => setTimeout(resolve, 100));\n }\n } catch (err) {\n console.warn(`Failed to fetch ${symbols[i]}:`, err.message);\n }\n }\n \n return results;\n } catch (error) {\n console.error('Batch quotes error:', error);\n return [];\n }\n }\n\n async getEnrichedQuote(symbol) {\n try {\n // Fetch quote, key metrics, ratios, and analyst rating for ONE stock\n const [quoteRes, metricsRes, ratiosRes, ratingRes] = await Promise.all([\n fetch(`https://financialmodelingprep.com/stable/quote?symbol=${symbol}&apikey=${this.apiKeys.FMP}`),\n fetch(`https://financialmodelingprep.com/stable/key-metrics-ttm?symbol=${symbol}&apikey=${this.apiKeys.FMP}`),\n fetch(`https://financialmodelingprep.com/stable/ratios?symbol=${symbol}&apikey=${this.apiKeys.FMP}`),\n fetch(`https://financialmodelingprep.com/stable/grades-consensus?symbol=${symbol}&apikey=${this.apiKeys.FMP}`)\n ]);\n \n const quote = quoteRes.ok ? (await quoteRes.json())[0] : null;\n const metrics = metricsRes.ok ? (await metricsRes.json())[0] : null;\n const ratios = ratiosRes.ok ? (await ratiosRes.json())[0] : null;\n const rating = ratingRes.ok ? await ratingRes.json() : null;\n \n if (!quote) return null;\n \n // Merge data from all sources\n return {\n ...quote,\n // Add PE from ratios\n pe: ratios?.priceToEarningsRatio || quote.pe || null,\n // Add ROE from key-metrics (already in percentage format)\n roe: metrics?.roeTTM || metrics?.roe || null,\n roa: metrics?.roaTTM || metrics?.roa || null,\n debtToEquity: ratios?.debtToEquityRatio || null,\n currentRatio: ratios?.currentRatio || null,\n revenueGrowth: metrics?.revenuePerShareTTM || null,\n // Add analyst rating\n rating: rating?.consensus || null,\n analystRating: rating?.consensus || null\n };\n } catch (error) {\n console.error(`Enriched quote error for ${symbol}:`, error);\n return null;\n }\n }\n\n async getHistoricalData(symbol, timeframe = '1min', params = {}) {\n return await this.getFMPData(`historical-chart/${timeframe}`, symbol, params);\n }\n\n // ═══════════════════════════════════════════════════════════════════════════\n // COMPANY FUNDAMENTALS\n // ═══════════════════════════════════════════════════════════════════════════\n\n async getProfile(symbol) {\n return await this.getFMPData('profile', symbol);\n }\n\n async getRatios(symbol) {\n return await this.getFMPData('ratios', symbol);\n }\n\n async getFinancials(symbol, period = 'annual') {\n return await this.getFMPData('income-statement', symbol, { period });\n }\n\n async getBalanceSheet(symbol, period = 'annual') {\n return await this.getFMPData('balance-sheet-statement', symbol, { period });\n }\n\n async getCashFlow(symbol, period = 'annual') {\n return await this.getFMPData('cash-flow-statement', symbol, { period });\n }\n\n async getEarnings(symbol) {\n return await this.getFMPData('earnings', symbol);\n }\n\n // ═══════════════════════════════════════════════════════════════════════════\n // TECHNICAL INDICATORS\n // ═══════════════════════════════════════════════════════════════════════════\n\n async getTechnicalIndicators(symbol, interval = '1hour') {\n return await this.getFMPData(`technical-indicator/${interval}`, symbol);\n }\n\n // ═══════════════════════════════════════════════════════════════════════════\n // REMOVED: Finnhub, Polygon, and other backup APIs\n // Using only FMP (paid tier) for all financial data\n // ═══════════════════════════════════════════════════════════════════════════\n\n // ═══════════════════════════════════════════════════════════════════════════\n // SOCIAL SENTIMENT - STOCKTWITS\n // ═══════════════════════════════════════════════════════════════════════════\n\n async getStockTwitsSentiment(symbol) {\n try {\n const url = `https://api.stocktwits.com/api/2/streams/symbol/${symbol}.json`;\n const response = await fetch(url);\n\n if (!response.ok) {\n throw new Error(`StockTwits API error: ${response.status}`);\n }\n\n return await response.json();\n } catch (error) {\n console.error('StockTwits error:', error);\n return null;\n }\n }\n\n // ═══════════════════════════════════════════════════════════════════════════\n // SOCIAL SENTIMENT - REDDIT\n // ═══════════════════════════════════════════════════════════════════════════\n\n async getRedditSentiment(symbol, subreddit = 'wallstreetbets') {\n try {\n const url = `https://www.reddit.com/r/${subreddit}/search.json?q=${symbol}&restrict_sr=1&sort=new&limit=50`;\n const response = await fetch(url, {\n headers: {\n 'User-Agent': 'RetailEdge/1.0'\n }\n });\n\n if (!response.ok) {\n throw new Error(`Reddit API error: ${response.status}`);\n }\n\n return await response.json();\n } catch (error) {\n console.error('Reddit error:', error);\n return null;\n }\n }\n\n // ═══════════════════════════════════════════════════════════════════════════\n // NEWS\n // ═══════════════════════════════════════════════════════════════════════════\n\n async getNews(symbol, limit = 10) {\n // Use only FMP for news\n const url = `https://financialmodelingprep.com/stable/stock-news?symbol=${symbol}&limit=${limit}&apikey=${this.apiKeys.FMP}`;\n const response = await fetch(url);\n\n if (!response.ok) {\n throw new Error(`News API error: ${response.status}`);\n }\n\n return await response.json();\n }\n\n // ═══════════════════════════════════════════════════════════════════════════\n // AI MODELS - CLAUDE\n // ═══════════════════════════════════════════════════════════════════════════\n\n async callClaude(messages, options = {}) {\n const url = 'https://api.anthropic.com/v1/messages';\n\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': this.apiKeys.ANTHROPIC,\n 'anthropic-version': '2023-06-01'\n },\n body: JSON.stringify({\n model: options.model || 'claude-3-5-sonnet-20241022',\n max_tokens: options.max_tokens || 4096,\n messages: messages,\n ...options\n })\n });\n\n if (!response.ok) {\n const error = await response.text();\n throw new Error(`Claude API error: ${response.status} - ${error}`);\n }\n\n return await response.json();\n }\n\n // ═══════════════════════════════════════════════════════════════════════════\n // WATCHLIST SEARCH\n // ═══════════════════════════════════════════════════════════════════════════\n\n async searchSymbols(query) {\n const url = `https://financialmodelingprep.com/stable/search?query=${query}&limit=10&apikey=${this.apiKeys.FMP}`;\n const response = await fetch(url);\n\n if (!response.ok) {\n throw new Error(`Symbol search error: ${response.status}`);\n }\n\n return await response.json();\n }\n\n // ═══════════════════════════════════════════════════════════════════════════\n // AI MODELS - DEEPSEEK\n // ═══════════════════════════════════════════════════════════════════════════\n\n async callDeepSeek(messages, options = {}) {\n const url = 'https://api.deepseek.com/v1/chat/completions';\n\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${this.apiKeys.DEEPSEEK}`\n },\n body: JSON.stringify({\n model: options.model || 'deepseek-chat',\n messages: messages,\n max_tokens: options.max_tokens || 4096,\n temperature: options.temperature || 0.7,\n ...options\n })\n });\n\n if (!response.ok) {\n const error = await response.text();\n throw new Error(`DeepSeek API error: ${response.status} - ${error}`);\n }\n\n return await response.json();\n }\n\n // ═══════════════════════════════════════════════════════════════════════════\n // AI MODELS - KIMI (Moonshot AI)\n // ═══════════════════════════════════════════════════════════════════════════\n\n async callKimi(messages, options = {}) {\n const url = 'https://api.moonshot.cn/v1/chat/completions';\n\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${this.apiKeys.KIMI}`\n },\n body: JSON.stringify({\n model: options.model || 'moonshot-v1-8k',\n messages: messages,\n max_tokens: options.max_tokens || 4096,\n temperature: options.temperature || 0.7,\n ...options\n })\n });\n\n if (!response.ok) {\n const error = await response.text();\n throw new Error(`Kimi API error: ${response.status} - ${error}`);\n }\n\n return await response.json();\n }\n\n // ═══════════════════════════════════════════════════════════════════════════\n // AI MODELS - GEMINI (Google)\n // ═══════════════════════════════════════════════════════════════════════════\n\n async callGemini(messages, options = {}) {\n const model = options.model || 'gemini-pro';\n const url = `https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent?key=${this.apiKeys.GEMINI}`;\n\n // Convert messages to Gemini format\n const contents = messages.map(msg => ({\n role: msg.role === 'assistant' ? 'model' : 'user',\n parts: [{ text: msg.content }]\n }));\n\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify({\n contents: contents,\n generationConfig: {\n maxOutputTokens: options.max_tokens || 4096,\n temperature: options.temperature || 0.7\n }\n })\n });\n\n if (!response.ok) {\n const error = await response.text();\n throw new Error(`Gemini API error: ${response.status} - ${error}`);\n }\n\n return await response.json();\n }\n\n // ═══════════════════════════════════════════════════════════════════════════\n // MARKET OVERVIEW\n // ═══════════════════════════════════════════════════════════════════════════\n\n async getMarketIndexes() {\n const indexes = ['^GSPC', '^DJI', '^IXIC', '^RUT']; // S&P500, DOW, NASDAQ, Russell\n return await this.getBatchQuotes(indexes);\n }\n\n async getGainersLosers(type = 'gainers') {\n const url = `https://financialmodelingprep.com/stable/stock-market/${type}?apikey=${this.apiKeys.FMP}`;\n const response = await fetch(url);\n\n if (!response.ok) {\n throw new Error(`Market movers error: ${response.status}`);\n }\n\n return await response.json();\n }\n\n // ═══════════════════════════════════════════════════════════════════════════\n // UTILITY METHODS\n // ═══════════════════════════════════════════════════════════════════════════\n\n checkAPIKeys() {\n const missing = [];\n Object.entries(this.apiKeys).forEach(([key, value]) => {\n if (!value || value === 'your_api_key_here') {\n missing.push(key);\n }\n });\n\n return {\n valid: missing.length === 0,\n missing: missing,\n available: Object.keys(this.apiKeys).filter(k => this.apiKeys[k] && this.apiKeys[k] !== 'your_api_key_here')\n };\n }\n}\n\nmodule.exports = new APIService();\n"}}}}