feat(news): 6h cache, politics-only, max 5/cycle, 2s sleep between calls
CI/CD / build-and-push (push) Successful in 1m32s
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:
@@ -32,6 +32,10 @@ MIN_CONFIDENCE = 0.55 # Minimum confidence in our estimate
|
||||
# which moves a 50% prior to ~18%/82% — strong but not overwhelming.
|
||||
NEWS_LOGODDS_WEIGHT = 1.5
|
||||
|
||||
# GNews free tier: 100 req/day. We limit to 5 queries per trading cycle
|
||||
# (politics markets only) and rely on 6 h cache to stay within budget.
|
||||
MAX_NEWS_QUERIES_PER_CYCLE = 5
|
||||
|
||||
|
||||
@dataclass
|
||||
class TradingSignal:
|
||||
@@ -62,6 +66,11 @@ class BayesianStrategy:
|
||||
def __init__(self, news: Optional[NewsClient] = None) -> None:
|
||||
self._signal_count = 0
|
||||
self._news = news # Optional; degrades gracefully when None or key missing
|
||||
self._news_queries_this_cycle = 0
|
||||
|
||||
def reset_cycle(self) -> None:
|
||||
"""Call once at the start of each trading cycle to reset the per-cycle GNews counter."""
|
||||
self._news_queries_this_cycle = 0
|
||||
|
||||
async def evaluate(
|
||||
self,
|
||||
@@ -172,16 +181,26 @@ class BayesianStrategy:
|
||||
adjustments.append(dom_adj)
|
||||
sources.append(f"BTC dom: {ext.btc_dominance:.1f}% (low → alt season)")
|
||||
|
||||
# Signal 4: GNews sentiment (politics / tech / events only)
|
||||
# Signal 4: GNews sentiment — politics markets only.
|
||||
# BTC/F&G already cover crypto and macro; GNews budget is too tight to
|
||||
# waste on tech/events. Cap at MAX_NEWS_QUERIES_PER_CYCLE per cycle so
|
||||
# we prioritise the soonest-resolving markets (caller sorts by end_date).
|
||||
# Applied as a direct log-odds shift — stronger signal than macro proxies.
|
||||
# Weight NEWS_LOGODDS_WEIGHT=1.5 means a ±1.0 sentiment score shifts
|
||||
# log-odds by ±1.5 (e.g. 50% prior → ~82% / ~18%).
|
||||
news_log_adj = 0.0
|
||||
if (is_politics or is_tech or is_events) and self._news is not None:
|
||||
sentiment = await self._news.get_sentiment(market.question)
|
||||
if abs(sentiment) > 0.05:
|
||||
news_log_adj = sentiment * NEWS_LOGODDS_WEIGHT
|
||||
sources.append(f"GNews: {sentiment:+.2f}")
|
||||
if is_politics and self._news is not None:
|
||||
if self._news_queries_this_cycle < MAX_NEWS_QUERIES_PER_CYCLE:
|
||||
self._news_queries_this_cycle += 1
|
||||
sentiment = await self._news.get_sentiment(market.question)
|
||||
if abs(sentiment) > 0.05:
|
||||
news_log_adj = sentiment * NEWS_LOGODDS_WEIGHT
|
||||
sources.append(f"GNews: {sentiment:+.2f}")
|
||||
else:
|
||||
log.debug(
|
||||
"GNews cycle limit (%d) reached — skipping news for %r",
|
||||
MAX_NEWS_QUERIES_PER_CYCLE, market.question[:50],
|
||||
)
|
||||
|
||||
# Macro/politics/tech/events: cap confidence lower to reflect weaker signal quality
|
||||
if is_macro or is_politics or is_tech or is_events:
|
||||
|
||||
Reference in New Issue
Block a user