UI enhancemnts
This commit is contained in:
@@ -7,14 +7,20 @@ export class DataMapper {
|
||||
// ── Public entry point ────────────────────────────────────────────────────
|
||||
static mapToStandardFormat(ticker: string, summary: YahooSummary): MappedData {
|
||||
const quoteType = summary.price?.quoteType as string | undefined;
|
||||
const category = ((summary.assetProfile?.category as string) || '').toLowerCase();
|
||||
const yieldVal = (summary.summaryDetail?.trailingAnnualDividendYield as number) ?? 0;
|
||||
// Prefer fundProfile.categoryName (Morningstar category, e.g. "Intermediate
|
||||
// Core Bond") — assetProfile.category is rarely populated for ETFs. A
|
||||
// dividend-yield heuristic is deliberately NOT used: high-yield equity ETFs
|
||||
// (SCHD, VYM) are not bonds.
|
||||
const category = (
|
||||
(summary.fundProfile?.categoryName as string) ||
|
||||
(summary.assetProfile?.category as string) ||
|
||||
''
|
||||
).toLowerCase();
|
||||
|
||||
const isBond =
|
||||
category.includes('bond') ||
|
||||
category.includes('fixed income') ||
|
||||
category.includes('treasury') ||
|
||||
(quoteType === 'ETF' && yieldVal > 0.02 && category === '');
|
||||
category.includes('treasury');
|
||||
|
||||
if (quoteType === 'ETF') {
|
||||
return isBond
|
||||
@@ -143,17 +149,23 @@ export class DataMapper {
|
||||
}
|
||||
|
||||
// ── ETF ───────────────────────────────────────────────────────────────────
|
||||
// Missing fields are preserved as null (not coerced to 0) so EtfScorer can
|
||||
// skip the corresponding gate instead of auto-failing on absent Yahoo data.
|
||||
private static mapEtfData(summary: YahooSummary) {
|
||||
const num = (v: unknown): number | null =>
|
||||
typeof v === 'number' && Number.isFinite(v) ? v : null;
|
||||
|
||||
const expenseRatio = num(summary.summaryDetail?.expenseRatio);
|
||||
const dividendYield = num(summary.summaryDetail?.trailingAnnualDividendYield);
|
||||
const fiveYearReturn = num(summary.defaultKeyStatistics?.fiveYearAverageReturn);
|
||||
|
||||
return {
|
||||
expenseRatio: ((summary.summaryDetail?.expenseRatio as number) ?? 0) * 100,
|
||||
totalAssets: (summary.summaryDetail?.totalAssets as number) ?? 0,
|
||||
yield: ((summary.summaryDetail?.trailingAnnualDividendYield as number) ?? 0) * 100,
|
||||
fiveYearReturn: ((summary.defaultKeyStatistics?.fiveYearAverageReturn as number) ?? 0) * 100,
|
||||
volume:
|
||||
(summary.summaryDetail?.averageVolume as number) ??
|
||||
(summary.price?.averageVolume as number) ??
|
||||
0,
|
||||
currentPrice: (summary.price?.regularMarketPrice as number) ?? 0,
|
||||
expenseRatio: expenseRatio != null ? expenseRatio * 100 : null,
|
||||
totalAssets: num(summary.summaryDetail?.totalAssets),
|
||||
yield: dividendYield != null ? dividendYield * 100 : null,
|
||||
fiveYearReturn: fiveYearReturn != null ? fiveYearReturn * 100 : null,
|
||||
volume: num(summary.summaryDetail?.averageVolume) ?? num(summary.price?.averageVolume),
|
||||
currentPrice: num(summary.price?.regularMarketPrice) ?? 0,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user