from __future__ import annotations import datetime as dt from types import SimpleNamespace from zoneinfo import ZoneInfo import requests from libs.backtest.attention import AttentionFilterService from libs.backtest.domain import Candidate, SignalConfig _UTC = ZoneInfo("UTC") def _make_candidate(symbol: str, event_id: str) -> Candidate: return Candidate( event_id=event_id, symbol=symbol, score=0.8, sector="Technology", event_type="earnings_release", event_timestamp=dt.datetime(2026, 1, 5, 21, 0, tzinfo=_UTC), event_date=dt.date(2026, 1, 5), filing_time_bucket="post_market", reaction_date=dt.date(2026, 1, 6), execution_date=dt.date(2026, 1, 7), entry_price_est=100.0, avg_dollar_volume=10_000_000.0, score_bucket="high", ) def _make_engine(**overrides): defaults = { "direction": "long_only", "attention_min_wiki_spike_10d": None, "attention_min_wiki_zscore_20d": None, "attention_max_wiki_spike_10d": None, "attention_max_wiki_zscore_20d": None, "attention_min_article_count_3d": None, "attention_min_us_article_count_3d": None, "attention_min_resolver_confidence": None, "score_threshold_override": None, } defaults.update(overrides) return SimpleNamespace(**defaults) class _TimeoutSession: def __init__(self) -> None: self.calls = 0 self.timeouts: list[float] = [] def get(self, *_args, **kwargs): self.calls += 1 self.timeouts.append(float(kwargs["timeout"])) raise requests.ReadTimeout("oracle unavailable") def close(self) -> None: return None def test_attention_service_disables_run_after_network_timeout_for_max_only_gates(): service = AttentionFilterService("http://oracle:18001", "default", timeout=1.5) session = _TimeoutSession() service._session = session engine = _make_engine(attention_max_wiki_spike_10d=6.0) signal = SignalConfig(score_threshold=0.5, max_candidates_per_day=10) candidates = [ _make_candidate("AAPL", "EVT::1"), _make_candidate("MSFT", "EVT::2"), ] filtered = service.apply_filters(candidates, engine, signal) assert [candidate.symbol for candidate in filtered] == ["AAPL", "MSFT"] assert session.calls == 1 assert session.timeouts == [1.5] assert service._service_unavailable is True def test_attention_service_disables_run_after_network_timeout_for_min_gates(): service = AttentionFilterService("http://oracle:18001", "default", timeout=2.0) session = _TimeoutSession() service._session = session engine = _make_engine(attention_min_wiki_spike_10d=1.0) signal = SignalConfig(score_threshold=0.5, max_candidates_per_day=10) candidates = [ _make_candidate("AAPL", "EVT::1"), _make_candidate("MSFT", "EVT::2"), ] filtered = service.apply_filters(candidates, engine, signal) assert filtered == [] assert session.calls == 1 assert session.timeouts == [2.0] assert service._service_unavailable is True