feat(news): 6h cache, politics-only, max 5/cycle, 2s sleep between calls
CI/CD / build-and-push (push) Successful in 1m32s

- CACHE_TTL: 4h → 6h (≤36 req/day with ≤9 politics markets)
- GNews only called for is_politics markets (BTC/F&G cover crypto/macro)
- MAX_NEWS_QUERIES_PER_CYCLE=5: BayesianStrategy.reset_cycle() called each
  iteration; counter increments only on actual API call (cache hits free)
- 2s asyncio.sleep in news.py finally block after each real HTTP request
- main.py sorts markets: politics first by end_date ascending, so soonest-
  resolving markets consume the 5-query budget before others

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
chemavx
2026-04-14 12:33:26 +00:00
parent d642dbd9cf
commit 33ad86f352
3 changed files with 54 additions and 11 deletions
+11 -4
View File
@@ -1,13 +1,16 @@
"""
News sentiment client for GNews API.
Free tier: 100 requests/day — we stay well within this by caching each
unique query for CACHE_TTL seconds (4 hours). With ~9 political markets
refreshed every 4 h that is 9 × 6 = 54 requests/day.
Free tier: 100 requests/day. Budget:
- Cache TTL: 6 hours — same query is never repeated within 6 h
- Max 5 queries per trading cycle (politics markets only)
- 2-second sleep between actual API calls to avoid burst 429s
With ≤9 politics markets and 6 h cache → ≤9 requests per 6 h = ≤36/day.
Score returned: -1.0 (very negative headlines) → +1.0 (very positive).
Returns 0.0 on any error or missing API key so the caller degrades gracefully.
"""
import asyncio
import logging
import os
import re
@@ -18,7 +21,8 @@ import httpx
log = logging.getLogger(__name__)
GNEWS_API = "https://gnews.io/api/v4/search"
CACHE_TTL = 4 * 3600 # seconds — fits 100 req/day free tier
CACHE_TTL = 6 * 3600 # seconds — ≤9 politics markets × 4 cycles/day = ≤36 req/day
_INTER_REQUEST_SLEEP = 2 # seconds between consecutive real API calls
# ---------------------------------------------------------------------------
# Keyword lists for headline sentiment
@@ -127,6 +131,9 @@ class NewsClient:
except Exception as exc:
log.warning("GNews network error for %r: %s", query, exc)
return 0.0
finally:
# Always sleep after a real network attempt to avoid burst 429s
await asyncio.sleep(_INTER_REQUEST_SLEEP)
log.info("GNews HTTP %d for query %r", resp.status_code, query)