fix: market data rate limiting and backtest price lookup
- Increase market_api polling cadence from 60s to 900s (15 min). The prev-day bar endpoint returns the same data all day, so polling every minute wastes API quota. 50 tickers at 15-min cadence = ~3.3 req/min, well within the 5/min rate limit. - Reduce market_api rate limit from 30/min to 5/min to match. - Fix backtest replay to query market_snapshots with data->>'c' for close prices instead of nonexistent market_data.close_price column. - Enrich backtest recommendations with prices from market_snapshots and sectors from companies table.
This commit is contained in:
@@ -45,7 +45,7 @@ def _ensure_dict(val: Any) -> Optional[dict]:
|
|||||||
# Default polling cadences by source class (seconds).
|
# Default polling cadences by source class (seconds).
|
||||||
# Individual sources can override via config.polling_interval_seconds.
|
# Individual sources can override via config.polling_interval_seconds.
|
||||||
DEFAULT_CADENCES: dict[str, int] = {
|
DEFAULT_CADENCES: dict[str, int] = {
|
||||||
"market_api": 60,
|
"market_api": 900,
|
||||||
"news_api": 300,
|
"news_api": 300,
|
||||||
"filings_api": 3600,
|
"filings_api": 3600,
|
||||||
"web_scrape": 1800,
|
"web_scrape": 1800,
|
||||||
@@ -55,7 +55,7 @@ DEFAULT_CADENCES: dict[str, int] = {
|
|||||||
|
|
||||||
# Default rate limits per source type (requests per minute)
|
# Default rate limits per source type (requests per minute)
|
||||||
DEFAULT_RATE_LIMITS: dict[str, int] = {
|
DEFAULT_RATE_LIMITS: dict[str, int] = {
|
||||||
"market_api": 30,
|
"market_api": 5,
|
||||||
"news_api": 20,
|
"news_api": 20,
|
||||||
"filings_api": 10,
|
"filings_api": 10,
|
||||||
"web_scrape": 10,
|
"web_scrape": 10,
|
||||||
|
|||||||
@@ -86,6 +86,32 @@ class BacktestReplay:
|
|||||||
prev_value = config.initial_capital
|
prev_value = config.initial_capital
|
||||||
trade_log: list[dict] = []
|
trade_log: list[dict] = []
|
||||||
|
|
||||||
|
# Pre-load company sectors and latest prices for enrichment
|
||||||
|
company_sectors: dict[str, str] = {}
|
||||||
|
company_prices: dict[str, float] = {}
|
||||||
|
if self.pool is not None:
|
||||||
|
try:
|
||||||
|
sector_rows = await self.pool.fetch(
|
||||||
|
"SELECT ticker, sector FROM companies WHERE active = TRUE"
|
||||||
|
)
|
||||||
|
for sr in sector_rows:
|
||||||
|
company_sectors[sr["ticker"]] = sr["sector"] or "Unknown"
|
||||||
|
except Exception:
|
||||||
|
logger.debug("Could not load company sectors")
|
||||||
|
|
||||||
|
# Load latest market prices (use most recent close from market_snapshots JSONB)
|
||||||
|
try:
|
||||||
|
price_rows = await self.pool.fetch(
|
||||||
|
"SELECT DISTINCT ON (ticker) ticker, (data->>'c')::float as close_price "
|
||||||
|
"FROM market_snapshots WHERE snapshot_type = 'bar' "
|
||||||
|
"ORDER BY ticker, captured_at DESC"
|
||||||
|
)
|
||||||
|
for pr in price_rows:
|
||||||
|
if pr["close_price"]:
|
||||||
|
company_prices[pr["ticker"]] = float(pr["close_price"])
|
||||||
|
except Exception:
|
||||||
|
logger.debug("Could not load market prices — using portfolio_pct fallback")
|
||||||
|
|
||||||
# Group recommendations by date
|
# Group recommendations by date
|
||||||
recs_by_date: dict[date, list[dict]] = {}
|
recs_by_date: dict[date, list[dict]] = {}
|
||||||
for rec in recs:
|
for rec in recs:
|
||||||
@@ -94,6 +120,14 @@ class BacktestReplay:
|
|||||||
d = rec_date.date()
|
d = rec_date.date()
|
||||||
else:
|
else:
|
||||||
d = rec_date
|
d = rec_date
|
||||||
|
|
||||||
|
# Enrich rec with price and sector if missing
|
||||||
|
ticker = rec.get("ticker", "")
|
||||||
|
if "current_price" not in rec or not rec.get("current_price"):
|
||||||
|
rec["current_price"] = company_prices.get(ticker, 50.0)
|
||||||
|
if "sector" not in rec or not rec.get("sector"):
|
||||||
|
rec["sector"] = company_sectors.get(ticker, "Unknown")
|
||||||
|
|
||||||
recs_by_date.setdefault(d, []).append(rec)
|
recs_by_date.setdefault(d, []).append(rec)
|
||||||
|
|
||||||
# Iterate through each trading day
|
# Iterate through each trading day
|
||||||
|
|||||||
Reference in New Issue
Block a user