import Anthropic from '@anthropic-ai/sdk'; // LLMAnalyst — uses Claude Haiku to analyze news catalyst stories. // // Given a list of news headlines and the tickers already identified, // it produces: // - A concise market summary (2-3 sentences) // - Industries likely to be affected (beyond the directly mentioned tickers) // - Up to 5 related tickers worth watching // - A risk sentiment assessment (BULLISH / NEUTRAL / BEARISH) // // Requires ANTHROPIC_API_KEY in environment. const SYSTEM_PROMPT = `You are a professional equity analyst. You will be given a list of today's market news headlines and the tickers already identified as catalysts. Your job is to: 1. Write a 2-3 sentence market summary capturing the dominant theme 2. Identify up to 4 industries that are likely to be secondarily affected (not directly mentioned but impacted by contagion, supply chain, regulation, or macro effects) 3. Suggest up to 5 related ticker symbols worth screening that are NOT already in the provided list 4. Assess overall market sentiment as BULLISH, NEUTRAL, or BEARISH based on the news Return ONLY valid JSON in this exact shape — no markdown, no explanation: { "summary": "string", "sentiment": "BULLISH" | "NEUTRAL" | "BEARISH", "affectedIndustries": [ { "name": "string", "reason": "string (one sentence)" } ], "relatedTickers": [ { "ticker": "string", "reason": "string (one sentence)" } ] }`; export class LLMAnalyst { constructor({ logger } = {}) { this.logger = logger ?? { log: console.log, warn: console.warn }; this.client = process.env.ANTHROPIC_API_KEY ? new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY }) : null; } // Analyzes news stories and returns structured market intelligence. // Returns null if ANTHROPIC_API_KEY is not set (graceful degradation). async analyze(stories, existingTickers = []) { if (!this.client) { this.logger.warn('LLMAnalyst: ANTHROPIC_API_KEY not set — skipping analysis'); return null; } if (!stories?.length) return null; const headlines = stories .slice(0, 15) .map((s, i) => `${i + 1}. ${s.title} (${s.publisher ?? 'unknown'})`) .join('\n'); const userMessage = `Today's market news headlines:\n\n${headlines}\n\nAlready identified catalyst tickers: ${existingTickers.join(', ') || 'none'}`; try { const response = await this.client.messages.create({ model: 'claude-haiku-4-5', max_tokens: 1024, system: SYSTEM_PROMPT, messages: [{ role: 'user', content: userMessage }], }); const raw = response.content[0]?.text ?? ''; const cleaned = raw .replace(/^```(?:json)?\s*/i, '') .replace(/```\s*$/i, '') .trim(); return JSON.parse(cleaned); } catch (err) { this.logger.warn('LLMAnalyst: analysis failed —', err.message); return null; } } }