import { YahooClient } from '../market/YahooClient.js'; import type { Logger } from '../types.js'; interface Story { title: string; publisher: string; link: string; relatedTickers: string[]; } interface CatalystResult { tickers: string[]; stories: Story[]; } const NEWS_QUERIES = ['stock market today', 'earnings report', 'market news']; const MAX_STORIES = 15; const TICKER_REGEX = /^[A-Z]{1,6}$/; export class CatalystAnalyst { private client: YahooClient; private logger: Pick; constructor({ logger }: { logger?: Pick } = {}) { this.client = new YahooClient(); this.logger = logger ?? { write: (msg: string) => process.stdout.write(msg) }; } async run(): Promise { this.logger.write('🔍 Fetching market news...'); const stories = await this._fetchNews(); const tickers = this._extractTickers(stories); this.logger.write(` ${stories.length} stories, ${tickers.length} tickers\n`); return { tickers, stories }; } private async _fetchNews(): Promise { const seen = new Map(); for (const query of NEWS_QUERIES) { try { const { news = [] } = await (this.client as any).yf.search(query, { newsCount: 8, quotesCount: 0, }); for (const s of news as any[]) { if (!seen.has(s.title)) { seen.set(s.title, { title: s.title, publisher: s.publisher, link: s.link, relatedTickers: s.relatedTickers ?? [], }); } } } catch { /* skip failed query */ } } return [...seen.values()].slice(0, MAX_STORIES); } private _extractTickers(stories: Story[]): string[] { const tickers = new Set(); for (const { relatedTickers } of stories) { for (const t of relatedTickers) { const clean = t.split(':')[0].toUpperCase(); if (TICKER_REGEX.test(clean)) tickers.add(clean); } } return [...tickers]; } }