- 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
Alpaca returns 404 when you don't hold a position in a ticker.
The ingestion worker was logging this as an error and incrementing
the failure count. Now returns an empty items list instead, since
'no position' is a valid state, not an error.
The alpaca.url config file contains https://paper-api.alpaca.markets/v2
but the adapter code also prepends /v2/ to all paths, resulting in
/v2/v2/positions which returns 404. Now strips trailing /v2 or /v1
from the configured base URL since the adapter manages API versioning.
This was causing 1,017 consecutive broker sync failures.
Two tiers of market data:
1. Per-ticker prev bars (existing 50 sources, 15-min cadence) for
watchlist detail — trading decisions, stop-loss, position sizing
2. Grouped daily (new single source, once per day) for broad market
context — correlation analysis, sector rotation, competitive intel
Changes:
- Add grouped_daily endpoint to PolygonMarketAdapter with auto date
calculation (previous trading day, skip weekends)
- Add fetch_global_market_sources() to scheduler for sources without
company_id, scheduled once daily (86400s cadence)
- Update _persist_market_items to use item-level ticker from T field
and look up company_id dynamically for grouped daily bars
- Migration 020: make company_id nullable on sources and
market_snapshots tables, add grouped daily source row
- Fix backtest replay to query market_snapshots data->>'c' for prices