fix(news): strip GNews operator dashes and stop phantom query-budget counting
Two minor faults found during the GNews capture/prioritisation diagnostic: 1. Hyphens/dashes reached the GNews query verbatim. '-' is GNews's exclusion operator, so a token like "El-Sayed" returned HTTP 400 and wasted a query. _PUNCT_RE now strips '-', en dash and em dash to spaces. 2. The per-cycle GNews budget counter incremented in evaluate() before get_sentiment() checked the API key, so with no key configured the [CYCLE SUMMARY] reported a phantom "gnews_queries_used: 5/5" with zero real requests. Added NewsClient.enabled and gated the GNews block on it; with no key the counter stays 0/5 and no spurious SKIP_GNEWS_PRIORITY is logged. No behaviour change when a key is present. Prioritisation itself was confirmed correct and is left untouched: politics markets are sorted by gnews_priority DESC and prior-extreme markets return before the budget is consumed, so no query is ever spent on a market that cannot trade. Tests: tests/test_news_query.py (4 new); full suite 66 passed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
b6153f5859
commit
54fc8fa11a
@@ -0,0 +1,77 @@
|
||||
"""Tests for the GNews layer minor fixes.
|
||||
|
||||
Two faults found during the GNews capture/prioritisation diagnostic:
|
||||
|
||||
1. Hyphens/dashes in a market question reached the GNews query verbatim and,
|
||||
because '-' is GNews's exclusion operator, produced HTTP 400
|
||||
(e.g. "Abdul El-Sayed Michigan Democratic Primary").
|
||||
|
||||
2. The per-cycle GNews budget counter incremented in evaluate() *before*
|
||||
get_sentiment() checked the API key, so with no key configured the
|
||||
[CYCLE SUMMARY] reported a phantom "gnews_queries_used: 5/5" even though
|
||||
zero real requests left the process.
|
||||
"""
|
||||
import asyncio
|
||||
|
||||
from bot.data.news import NewsClient
|
||||
from bot.data.external import ExternalSignals
|
||||
from bot.data.polymarket import Market
|
||||
from bot.strategy.bayesian import BayesianStrategy
|
||||
|
||||
|
||||
# ── Fix 1: query sanitisation ────────────────────────────────────────────────
|
||||
|
||||
def test_build_query_strips_hyphen_that_breaks_gnews():
|
||||
q = NewsClient._build_query(
|
||||
"Will Abdul El-Sayed win the 2026 Michigan Democratic Primary?"
|
||||
)
|
||||
assert "-" not in q # the exclusion operator must be gone
|
||||
assert "El-Sayed" not in q
|
||||
assert "Sayed" in q # the meaningful token survives as its own word
|
||||
|
||||
|
||||
def test_build_query_strips_unicode_dashes():
|
||||
q = NewsClient._build_query("Trump–Putin summit — final outcome")
|
||||
assert "–" not in q and "—" not in q
|
||||
assert "Trump" in q and "Putin" in q
|
||||
|
||||
|
||||
# ── Fix 2: enabled property + budget accounting ──────────────────────────────
|
||||
|
||||
def test_enabled_reflects_api_key(monkeypatch):
|
||||
monkeypatch.delenv("GNEWS_API_KEY", raising=False)
|
||||
assert NewsClient().enabled is False
|
||||
monkeypatch.setenv("GNEWS_API_KEY", "deadbeefdeadbeefdeadbeefdeadbeef")
|
||||
assert NewsClient().enabled is True
|
||||
|
||||
|
||||
def _politics_market() -> Market:
|
||||
return Market(
|
||||
id="m1", condition_id="c1",
|
||||
question="Will candidate X win the 2026 governor election?",
|
||||
yes_token_id="y", no_token_id="n",
|
||||
yes_price=0.50, no_price=0.50, volume_24h=10_000.0,
|
||||
end_date="2026-07-15T00:00:00Z", active=True, category="politics",
|
||||
)
|
||||
|
||||
|
||||
def _signals() -> ExternalSignals:
|
||||
return ExternalSignals(
|
||||
btc_price=1.0, btc_change_24h=0.0, eth_price=1.0, eth_change_24h=0.0,
|
||||
btc_dominance=50.0, fear_greed_index=50, fear_greed_label="neutral",
|
||||
total_market_cap_change=0.0, valid=True,
|
||||
)
|
||||
|
||||
|
||||
def test_disabled_news_consumes_no_gnews_budget(monkeypatch):
|
||||
"""Regression: no API key → gnews_queries_used stays 0 (was a phantom 1+)."""
|
||||
monkeypatch.delenv("GNEWS_API_KEY", raising=False)
|
||||
news = NewsClient()
|
||||
assert news.enabled is False
|
||||
|
||||
strategy = BayesianStrategy(news=news, manifold=None, db=None)
|
||||
strategy.reset_cycle()
|
||||
asyncio.run(
|
||||
strategy.evaluate(_politics_market(), _signals(), occupied_families=set())
|
||||
)
|
||||
assert strategy.get_cycle_stats()["gnews_queries_used"] == 0
|
||||
Reference in New Issue
Block a user