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:
@@ -1897,9 +1897,32 @@ const institutionalRouter = router({
|
||||
if (cached) return { symbol, ratings: cached.ratings, consensus: cached.consensus, stale: true };
|
||||
return { symbol, ratings: [], consensus: null, error: result.error };
|
||||
}
|
||||
return { symbol, ratings: result.ratings, consensus: result.consensus };
|
||||
}),
|
||||
});
|
||||
return { symbol, ratings: result.ratings, consensus: result.consensus };
|
||||
}),
|
||||
|
||||
/** Short interest (shares short, % float, short ratio) from yahoo-finance2 defaultKeyStatistics. */
|
||||
shortInterest: publicProcedure
|
||||
.input(z.object({ symbol: z.string().min(1) }))
|
||||
.query(async ({ ctx, input }) => {
|
||||
const symbol = input.symbol.toUpperCase();
|
||||
const k = `yfinance:shortinterest:${symbol}`;
|
||||
const entry = await ctx.cache.get<unknown>(k);
|
||||
const value = entry.value as Record<string, unknown> | null;
|
||||
return {
|
||||
symbol,
|
||||
sharesShort: value ? (value.sharesShort as number | null) : null,
|
||||
sharesShortPriorMonth: value ? (value.sharesShortPriorMonth as number | null) : null,
|
||||
sharesPercentSharesOut: value ? (value.sharesPercentSharesOut as number | null) : null,
|
||||
shortRatio: value ? (value.shortRatio as number | null) : null,
|
||||
shortPercentOfFloat: value ? (value.shortPercentOfFloat as number | null) : null,
|
||||
dateShortInterest: value ? (value.dateShortInterest as string | null) : null,
|
||||
floatShares: value ? (value.floatShares as number | null) : null,
|
||||
sharesOutstanding: value ? (value.sharesOutstanding as number | null) : null,
|
||||
stale: entry.isStale,
|
||||
provenance: entry.provenance,
|
||||
};
|
||||
}),
|
||||
};
|
||||
|
||||
// ─── EDGAR / SEC Filings Router (Slice 6 / M6) ────────────────────────────
|
||||
|
||||
|
||||
Reference in New Issue
Block a user