feat: add GNews sentiment signal for politics/tech/events markets
CI/CD / build-and-push (push) Successful in 1m28s
CI/CD / build-and-push (push) Successful in 1m28s
bot/data/news.py (new): - NewsClient with in-memory cache (TTL=4h) to stay within 100 req/day limit - _build_query(): strips dates, punctuation and stopwords from market question - _score_headlines(): keyword-based pos/neg vote per article, averaged ∈ [-1, +1] - Degrades to 0.0 on missing key, 403 quota, or network error bot/strategy/bayesian.py: - BayesianStrategy(news=NewsClient) — optional, backwards compatible - Signal 4: GNews sentiment applied as direct log-odds shift (weight=1.5) so a ±1.0 sentiment score moves a 50% prior to 82%/18% - +0.10 confidence boost when news signal is present - NEWS_LOGODDS_WEIGHT constant documented at module level bot/main.py: - Instantiate NewsClient, pass to BayesianStrategy, close in finally block Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -17,6 +17,7 @@ from typing import Optional
|
||||
|
||||
from bot.data.polymarket import Market
|
||||
from bot.data.external import ExternalSignals
|
||||
from bot.data.news import NewsClient
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -26,6 +27,11 @@ log = logging.getLogger(__name__)
|
||||
MIN_EDGE = 0.10 # 10% edge minimum
|
||||
MIN_CONFIDENCE = 0.55 # Minimum confidence in our estimate
|
||||
|
||||
# Log-odds weight applied to the GNews sentiment score (range ±1.0).
|
||||
# A weight of 1.5 means a fully negative/positive signal shifts log-odds by ±1.5,
|
||||
# which moves a 50% prior to ~18%/82% — strong but not overwhelming.
|
||||
NEWS_LOGODDS_WEIGHT = 1.5
|
||||
|
||||
|
||||
@dataclass
|
||||
class TradingSignal:
|
||||
@@ -53,8 +59,9 @@ class BayesianStrategy:
|
||||
to justify the fee + slippage cost (MIN_EDGE).
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
def __init__(self, news: Optional[NewsClient] = None) -> None:
|
||||
self._signal_count = 0
|
||||
self._news = news # Optional; degrades gracefully when None or key missing
|
||||
|
||||
async def evaluate(
|
||||
self,
|
||||
@@ -165,16 +172,29 @@ 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)
|
||||
# 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}")
|
||||
|
||||
# Macro/politics/tech/events: cap confidence lower to reflect weaker signal quality
|
||||
if is_macro or is_politics or is_tech or is_events:
|
||||
confidence_cap = 0.65
|
||||
else:
|
||||
confidence_cap = 0.90
|
||||
|
||||
# Compute posterior using log-odds updating
|
||||
# Compute posterior using log-odds updating.
|
||||
# total_adj (BTC/F&G/dominance) is amplified ×2 because those are weak proxies.
|
||||
# news_log_adj is applied at face value — it IS a direct log-odds signal.
|
||||
log_odds_prior = math.log(prior / (1 - prior))
|
||||
total_adj = sum(adjustments)
|
||||
estimated_prob = _sigmoid(log_odds_prior + total_adj * 2)
|
||||
estimated_prob = _sigmoid(log_odds_prior + total_adj * 2 + news_log_adj)
|
||||
estimated_prob = max(0.05, min(0.95, estimated_prob))
|
||||
|
||||
# Compute edge
|
||||
@@ -185,6 +205,9 @@ class BayesianStrategy:
|
||||
# Confidence based on signal agreement
|
||||
agreement = sum(1 for a in adjustments if (a > 0) == (total_adj > 0))
|
||||
confidence = min(confidence_cap, 0.4 + (agreement / max(len(adjustments), 1)) * 0.5)
|
||||
# News signal available → boost confidence by 0.10 (news corroborates macro signals)
|
||||
if news_log_adj != 0.0:
|
||||
confidence = min(confidence_cap, confidence + 0.10)
|
||||
|
||||
# Log evaluation result for every market
|
||||
action = "TRADE" if (abs_edge >= MIN_EDGE and confidence >= MIN_CONFIDENCE) else "SKIP"
|
||||
|
||||
Reference in New Issue
Block a user