phase-2: extract shared utils
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user