feat: intraday hourly price bars via Polygon range endpoint

- New 'intraday_bars' endpoint in PolygonMarketAdapter: fetches hourly
  bars for today using range_bars URL with timespan=hour, sort=asc
- Scheduler expands intraday_bars global source into per-ticker jobs
  for all active companies (every 15 minutes via polling_interval)
- Migration 025 inserts the intraday source with 900s cadence
- Frontend price matching uses closest-timestamp instead of date-string
  matching, with 2h tolerance for intraday and 36h for daily windows
- Bumped market price fetch limit to 200 for intraday granularity
This commit is contained in:
Celes Renata
2026-04-17 01:13:24 +00:00
parent c4206b3f4c
commit 2360c501e4
4 changed files with 70 additions and 15 deletions
+16 -5
View File
@@ -453,11 +453,22 @@ async def schedule_cycle(pool: asyncpg.Pool, rds: aioredis.Redis) -> int:
# Build job with ticker="_MARKET" for global sources
job = build_job_payload(src, [], now)
job["ticker"] = "_MARKET"
await rds.rpush(queue_key(QUEUE_INGESTION), json.dumps(job))
enqueued += 1
logger.info("Enqueued grouped daily market data job")
if endpoint == "intraday_bars":
# Expand intraday source into per-ticker jobs for all active companies
tickers = await pool.fetch(
"SELECT ticker FROM companies WHERE active = TRUE"
)
for t_row in tickers:
ticker_job = dict(job)
ticker_job["ticker"] = t_row["ticker"]
await rds.rpush(queue_key(QUEUE_INGESTION), json.dumps(ticker_job))
enqueued += 1
logger.info("Enqueued %d intraday bar jobs", len(tickers))
else:
job["ticker"] = "_MARKET"
await rds.rpush(queue_key(QUEUE_INGESTION), json.dumps(job))
enqueued += 1
logger.info("Enqueued grouped daily market data job")
logger.info(
"Cycle complete: enqueued=%d skipped_not_due=%d skipped_rate_limit=%d total_sources=%d",