feat: 90-day price range Y-axis scaling with breakthrough annotations

Backend:
- GET /api/market/prices/{ticker} now returns { bars, range_90d }
  with 90-day low/high computed from market_snapshots
- POST /api/market/backfill/{ticker} fetches 90 days of daily bars
  from Polygon and inserts missing bars into market_snapshots
- POST /api/market/backfill-all does the same for all active tickers

Frontend:
- Right Y-axis domain scaled to 90-day min/max (with 3% padding)
- Green dashed reference line at 90-day high
- Red dashed reference line at 90-day low
- Labels show exact price on each reference line
- Default limit bumped to 200 bars
This commit is contained in:
Celes Renata
2026-04-30 06:09:05 +00:00
parent 5209cc522e
commit bddaf44ffc
3 changed files with 170 additions and 10 deletions
+19 -2
View File
@@ -256,8 +256,13 @@ export interface MarketPrice {
captured_at: string;
}
export function useMarketPrices(ticker: string | undefined, limit = 30) {
return useGet<MarketPrice[]>(
export interface MarketPriceResponse {
bars: MarketPrice[];
range_90d: { low: number | null; high: number | null };
}
export function useMarketPrices(ticker: string | undefined, limit = 200) {
return useGet<MarketPriceResponse>(
['market-prices', ticker, limit],
'query',
`/api/market/prices/${ticker}?limit=${limit}`,
@@ -265,6 +270,18 @@ export function useMarketPrices(ticker: string | undefined, limit = 30) {
);
}
/** Backfill 90 days of daily bars from Polygon for a single ticker. */
export function useBackfillMarketPrices() {
const qc = useQueryClient();
return useMutation({
mutationFn: (ticker: string) =>
apiPost<{ ticker: string; inserted: number; total_bars: number }>('query', `/api/market/backfill/${ticker}`, {}),
onSuccess: (_data, ticker) => {
qc.invalidateQueries({ queryKey: ['market-prices', ticker] });
},
});
}
export function useTrend(id: string | undefined) {
return useGet<TrendSummary>(['trend', id], 'query', `/api/trends/${id}`, !!id);
}