85 lines
2.5 KiB
TypeScript
85 lines
2.5 KiB
TypeScript
/**
|
|
* bin/screen.ts — Market Screener CLI
|
|
*
|
|
* Fetches today's catalyst tickers from Yahoo Finance news,
|
|
* screens them under both Market-Adjusted and Fundamental lenses,
|
|
* and saves a full HTML report.
|
|
*
|
|
* Usage:
|
|
* npm start → Yahoo news → catalyst tickers → screen
|
|
* npm start -- watch → default watchlist
|
|
* npm start -- AAPL MSFT VOO → specific tickers
|
|
*/
|
|
|
|
import 'dotenv/config';
|
|
import { CatalystAnalyst } from '../server/analyst/CatalystAnalyst.js';
|
|
import { ScreenerEngine } from '../server/screener/ScreenerEngine.js';
|
|
import { HtmlReporter } from '../server/reporters/HtmlReporter.js';
|
|
|
|
const DEFAULT_WATCHLIST: string[] = [
|
|
'PLTR',
|
|
'AAPL',
|
|
'MSFT',
|
|
'TSLA',
|
|
'O',
|
|
'VOO',
|
|
'QQQ',
|
|
'BND',
|
|
'LQD',
|
|
'TLT',
|
|
'IEF',
|
|
'SHY',
|
|
'GOVT',
|
|
'AGG',
|
|
'MUB',
|
|
];
|
|
|
|
async function main(): Promise<void> {
|
|
const args = process.argv.slice(2);
|
|
let tickers: string[] = [];
|
|
|
|
if (args.length > 0 && args[0] !== 'watch') {
|
|
tickers = args.map((t) => t.toUpperCase());
|
|
console.log(`📋 Screening: ${tickers.join(', ')}\n`);
|
|
} else if (args[0] === 'watch') {
|
|
tickers = DEFAULT_WATCHLIST;
|
|
console.log(`📋 Screening default watchlist (${tickers.length} tickers)\n`);
|
|
} else {
|
|
try {
|
|
const { tickers: newsTickers, stories } = await new CatalystAnalyst().run();
|
|
if (newsTickers.length === 0) {
|
|
console.warn("⚠ No tickers in today's news — using default watchlist\n");
|
|
tickers = DEFAULT_WATCHLIST;
|
|
} else {
|
|
tickers = newsTickers;
|
|
console.log("\n📰 Stories driving today's screen:");
|
|
stories.slice(0, 5).forEach((s) => {
|
|
const tags = s.relatedTickers.slice(0, 3).join(', ');
|
|
console.log(` • ${s.title}${tags ? ` [${tags}]` : ''}`);
|
|
});
|
|
console.log(`\n📋 Tickers: ${tickers.join(', ')}\n`);
|
|
}
|
|
} catch (err) {
|
|
console.warn(
|
|
`⚠ Catalyst analysis failed (${(err as Error).message}) — using default watchlist\n`,
|
|
);
|
|
tickers = DEFAULT_WATCHLIST;
|
|
}
|
|
}
|
|
|
|
try {
|
|
const { STOCK, ETF, BOND, ERROR, marketContext } =
|
|
await new ScreenerEngine().screenWithProgress(tickers);
|
|
const reportPath = new HtmlReporter().generate(
|
|
{ STOCK, ETF, BOND, ERROR } as any,
|
|
marketContext,
|
|
);
|
|
console.log(`\n✅ Done — report saved to: ${reportPath}\n`);
|
|
} catch (err) {
|
|
console.error('Screener failed:', (err as Error).message);
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
main().catch(console.error);
|