import { Asset } from './Asset.js'; export class Stock extends Asset { constructor(data) { super(data); // console.log('Data:', data); this.sector = this._mapToStandardSector(data || {}); this.metrics = { sector: this.sector, // Valuation peRatio: data.peRatio ?? null, pegRatio: data.pegRatio ?? null, priceToBook: data.priceToBook ?? null, // Profitability netProfitMargin: data.netProfitMargin ?? null, operatingMargin: data.operatingMargin ?? null, returnOnEquity: data.returnOnEquity ?? null, // Growth revenueGrowth: data.revenueGrowth ?? null, earningsGrowth: data.earningsGrowth ?? null, // Financial health debtToEquity: data.debtToEquity ?? null, quickRatio: data.quickRatio ?? null, // Cash flow fcfYield: data.fcfYield ?? null, pFFO: data.pFFO ?? null, // Income dividendYield: data.dividendYield ?? null, // Risk & momentum beta: data.beta ?? null, week52High: data.week52High ?? null, week52Low: data.week52Low ?? null, currentPrice: data.currentPrice ?? 0, }; } _mapToStandardSector(data) { const profile = data.assetProfile || {}; const industry = (profile.industry || '').toLowerCase(); const sector = (profile.sector || '').toLowerCase(); const combined = `${industry} ${sector}`; // Yahoo Finance sector/industry strings mapped to our internal sector constants. // Order matters — more specific matches first. if ( combined.includes('technology') || combined.includes('electronic') || combined.includes('semiconductor') || combined.includes('software') ) return 'TECHNOLOGY'; if (combined.includes('real estate') || combined.includes('reit')) return 'REIT'; if ( combined.includes('financial') || combined.includes('bank') || combined.includes('insurance') || combined.includes('asset management') ) return 'FINANCIAL'; if ( combined.includes('energy') || combined.includes('oil') || combined.includes('gas') || combined.includes('petroleum') ) return 'ENERGY'; if ( combined.includes('health') || combined.includes('biotech') || combined.includes('pharmaceutical') || combined.includes('medical') ) return 'HEALTHCARE'; // Yahoo calls this "Communication Services" — covers META, GOOGL, NFLX, DIS, T if ( combined.includes('communication') || combined.includes('media') || combined.includes('entertainment') || combined.includes('telecom') ) return 'COMMUNICATION'; if ( combined.includes('consumer defensive') || combined.includes('consumer staples') || combined.includes('household') || combined.includes('beverage') || combined.includes('food') ) return 'CONSUMER_STAPLES'; if ( combined.includes('consumer cyclical') || combined.includes('consumer discretionary') || combined.includes('retail') || combined.includes('apparel') || combined.includes('auto') ) return 'CONSUMER_DISCRETIONARY'; return 'GENERAL'; } getDisplayMetrics() { const fmt = (v, dec = 1, suffix = '') => (v != null ? `${v.toFixed(dec)}${suffix}` : null); const m = this.metrics; const w52pos = m.week52High > 0 && m.week52Low != null && m.currentPrice > 0 ? (((m.currentPrice - m.week52Low) / (m.week52High - m.week52Low)) * 100).toFixed(0) + '%' : null; // Only include fields that have actual data — null fields are omitted const display = { Ticker: this.ticker, Price: this.formatCurrency(this.currentPrice), Sector: this.sector, }; if (m.peRatio != null) display['P/E'] = fmt(m.peRatio, 1); if (m.pegRatio != null) display['PEG'] = fmt(m.pegRatio, 2); if (m.priceToBook != null) display['P/B'] = fmt(m.priceToBook, 2); if (m.returnOnEquity != null) display['ROE%'] = fmt(m.returnOnEquity, 1, '%'); if (m.operatingMargin != null) display['OpMgn%'] = fmt(m.operatingMargin, 1, '%'); if (m.netProfitMargin != null) display['NetMgn%'] = fmt(m.netProfitMargin, 1, '%'); if (m.revenueGrowth != null) display['Rev%'] = fmt(m.revenueGrowth, 1, '%'); if (m.fcfYield != null) display['FCF Yld%'] = fmt(m.fcfYield, 1, '%'); if (m.dividendYield != null) display['Div%'] = fmt(m.dividendYield, 2, '%'); if (m.debtToEquity != null) display['D/E'] = fmt(m.debtToEquity, 2); if (m.quickRatio != null) display['Quick'] = fmt(m.quickRatio, 2); if (m.beta != null) display['Beta'] = fmt(m.beta, 2); if (w52pos != null) display['52W Pos'] = w52pos; if (m.pFFO != null) display['P/FFO'] = fmt(m.pFFO, 1); return display; } }