import { YahooClient } from './YahooClient.js'; import { REGIME } from '../config/constants.js'; const TTL_MS = 60 * 60 * 1000; const DEFAULTS = { sp500Price: 5000, riskFreeRate: 4.5, vixLevel: 20, rateRegime: REGIME.HIGH, volatilityRegime: REGIME.NORMAL, benchmarks: { marketPE: 22, techPE: 30, reitYield: 3.5, igSpread: 1.0 }, }; const rateRegime = (rate) => (rate < 2 ? REGIME.LOW : rate <= 5 ? REGIME.NORMAL : REGIME.HIGH); const volRegime = (vix) => (vix < 15 ? REGIME.LOW : vix <= 25 ? REGIME.NORMAL : REGIME.HIGH); const pe = (summary) => summary.summaryDetail?.trailingPE ?? summary.defaultKeyStatistics?.forwardPE; export class BenchmarkProvider { // logger: object with .warn() — defaults to console so CLI behaviour is unchanged. constructor({ logger = console } = {}) { this.client = new YahooClient(); this.cache = { data: null, expiresAt: 0 }; this.logger = logger; } async getMarketContext() { if (this.cache.data && Date.now() < this.cache.expiresAt) return this.cache.data; try { const [sp500, tn10y, vix, spy, xlk, xlre, lqd] = await Promise.all([ this.client.fetchSummary('^GSPC'), this.client.fetchSummary('^TNX'), this.client.fetchSummary('^VIX'), this.client.fetchSummary('SPY'), this.client.fetchSummary('XLK'), this.client.fetchSummary('XLRE'), this.client.fetchSummary('LQD'), ]); const riskFreeRate = tn10y.price?.regularMarketPrice ?? 0; const sp500Price = sp500.price?.regularMarketPrice ?? 0; const vixLevel = vix.price?.regularMarketPrice ?? 0; if (!sp500Price || !riskFreeRate) throw new Error('Invalid market data (zero values)'); const lqdYield = (lqd.summaryDetail?.trailingAnnualDividendYield ?? 0) * 100; const context = { sp500Price, riskFreeRate, vixLevel, rateRegime: rateRegime(riskFreeRate), volatilityRegime: volRegime(vixLevel), benchmarks: { marketPE: pe(spy) ?? 22, techPE: pe(xlk) ?? 30, reitYield: (xlre.summaryDetail?.trailingAnnualDividendYield ?? 0.035) * 100, igSpread: Math.max(0.1, lqdYield - riskFreeRate), }, timestamp: new Date().toISOString(), }; this.cache = { data: context, expiresAt: Date.now() + TTL_MS }; return context; } catch (err) { this.logger.warn('Market data fetch failed, using defaults:', err.message); return this.cache.data ?? DEFAULTS; } } }