feat: short interest panel Phase 1 Yahoo Finance
- Add short_interest TtlClass (24h) to CacheRepository.ts with shortInterestHandler KindHandler (kv_cache JSON pattern), register in HANDLERS and del switch case - Add case 'shortinterest' to YFinanceAdapter.fetchOne using quoteSummary(defaultKeyStatistics): parses sharesShort, shortPercentOfFloat, shortRatio, dateShortInterest, sharesShortPriorMonth, floatShares, sharesOutstanding - Add institutional.shortInterest tRPC procedure (cache-read pattern, returns stale flag + provenance) - Add api.institutional.shortInterest to frontend trpc client - Create ShortInterestPanel.tsx: mirrors AnalystRatings pattern with loading/error/retry states, formatted shares/ratio/percentage display with source date - Mount ShortInterestPanel on overview page after AnalystRatings
This commit is contained in:
@@ -45,6 +45,34 @@ export class YFinanceAdapter implements SourceFetch {
|
||||
const raw = await yf.quoteSummary(id, { modules: ['summaryProfile'] });
|
||||
return { value: parseSymbol(raw, id), ttlClass: 'symbol_meta', provenance: { fetchedAt, sourceKind: 'yfinance', rawSourceId: id } };
|
||||
}
|
||||
case 'shortinterest': {
|
||||
const raw = await yf.quoteSummary(id, { modules: ['defaultKeyStatistics'] });
|
||||
const stats = (raw as Record<string, unknown>)?.defaultKeyStatistics as Record<string, unknown> | undefined;
|
||||
if (!stats) throw new Error('No defaultKeyStatistics module available');
|
||||
const parseNum = (v: unknown): number | null => {
|
||||
if (v === null || v === undefined) return null;
|
||||
if (typeof v === 'number') return v;
|
||||
const s = String(v).replace(/[,]/g, '');
|
||||
const n = parseFloat(s);
|
||||
return Number.isNaN(n) ? null : n;
|
||||
};
|
||||
return {
|
||||
value: {
|
||||
sharesShort: parseNum(stats.sharesShort),
|
||||
sharesShortPriorMonth: parseNum(stats.sharesShortPriorMonth),
|
||||
sharesShortPreviousMonthDate: stats.sharesShortPreviousMonthDate ? String(stats.sharesShortPreviousMonthDate).slice(0, 10) : null,
|
||||
dateShortInterest: stats.dateShortInterest ? String(stats.dateShortInterest).slice(0, 10) : null,
|
||||
sharesPercentSharesOut: parseNum(stats.sharesPercentSharesOut),
|
||||
shortRatio: parseNum(stats.shortRatio),
|
||||
shortPercentOfFloat: parseNum(stats.shortPercentOfFloat),
|
||||
floatShares: parseNum(stats.floatShares),
|
||||
sharesOutstanding: parseNum(stats.sharesOutstanding),
|
||||
sourceTickerKind: (stats as Record<string, unknown>).optionMethod?.toString() ?? null,
|
||||
},
|
||||
ttlClass: 'short_interest',
|
||||
provenance: { fetchedAt, sourceKind: 'yfinance', rawSourceId: id },
|
||||
};
|
||||
}
|
||||
default: throw new Error(`YFinanceAdapter: unknown kind '${kind}'`);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user