You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

198 lines
5.6 KiB
Python

from __future__ import annotations
import pytest
import libs.intraday.catalyst as catalyst_mod
from libs.intraday.catalyst import (
AttentionEventCache,
FilingEventCache,
PriorEventFeatureSnapshotCache,
fetch_attention_features_bulk,
fetch_filing_event_features_bulk,
)
from libs.oracle_client.models import (
EntityInfo,
EventAttentionResponse,
FilingEventEntry,
FilingEventsResponse,
NewsFeatures,
WikiFeatures,
)
def test_filing_event_cache_roundtrip(tmp_path) -> None:
cache = FilingEventCache(str(tmp_path))
cache.put(
"ABC",
"2026-01-01",
"2026-03-31",
[
{
"ticker": "ABC",
"filing_date": "2026-02-10",
"accession_number": "1",
"event_type": "other_material_event",
"item_number": "8.01",
"form_type": "8-K",
"title": "Material event",
}
],
)
hit = cache.get("ABC", "2026-01-02", "2026-03-01")
miss = cache.get("ABC", "2025-12-31", "2026-03-01")
assert hit is not None and hit[0]["event_type"] == "other_material_event"
assert miss is None
@pytest.mark.asyncio
async def test_fetch_filing_event_features_bulk_uses_cache_after_first_hit(tmp_path, monkeypatch) -> None:
cache = FilingEventCache(str(tmp_path))
calls = {"count": 0}
async def fake_get_events(self, ticker, start_date=None, end_date=None):
calls["count"] += 1
return FilingEventsResponse(
ticker=ticker,
events=[
FilingEventEntry(
id="evt-1",
ticker=ticker,
accession_number="acc-1",
form_type="8-K",
filing_date="2026-02-10",
item_number="8.01",
event_type="other_material_event",
title="Material event",
)
],
total_count=1,
)
monkeypatch.setattr(catalyst_mod.FilingsService, "get_filing_events", fake_get_events)
first = await fetch_filing_event_features_bulk(
["ABC"],
"2026-01-01",
"2026-03-31",
client=object(), # patched method ignores the client
cache=cache,
concurrency=1,
)
second = await fetch_filing_event_features_bulk(
["ABC"],
"2026-01-01",
"2026-03-31",
client=object(),
cache=cache,
concurrency=1,
)
assert first["ABC"]["2026-02-10"]["event_flag"] is True
assert first["ABC"]["2026-02-10"]["event_score"] >= 1.0
assert second == first
assert calls["count"] == 1
def test_attention_event_cache_roundtrip(tmp_path) -> None:
cache = AttentionEventCache(str(tmp_path))
cache.put(
"ABC",
"2026-02-10",
{
"attention_wiki_spike_10d": 2.3,
"attention_wiki_zscore_20d": 3.1,
"attention_article_count_3d": 5,
"attention_us_article_count_3d": 3,
"attention_resolver_confidence": 0.95,
},
)
hit = cache.get("ABC", "2026-02-10")
miss = cache.get("ABC", "2026-02-11")
assert hit is not None and hit["attention_article_count_3d"] == 5
assert miss is None
def test_prior_event_feature_snapshot_cache_roundtrip_normalizes_request(tmp_path) -> None:
cache = PriorEventFeatureSnapshotCache(str(tmp_path), snapshot_id="V46 Research 2026-04-23")
features = {
"ABC": {
"2026-02-10": {
"event_flag": True,
"event_score": 1.0,
}
}
}
cache.put(
["abc", "ABC"],
["2026-02-10", "2026-02-11"],
10,
("guidance_update", "earnings_release"),
features,
)
hit = cache.get(
["ABC"],
["2026-02-10", "2026-02-11"],
10,
("earnings_release", "guidance_update"),
)
miss = cache.get(
["ABC"],
["2026-02-10", "2026-02-11"],
7,
("earnings_release", "guidance_update"),
)
assert hit == features
assert miss is None
assert cache.snapshot_id == "v46-research-2026-04-23"
manifest = tmp_path / "v46-research-2026-04-23" / "manifest.json"
assert manifest.exists()
assert '"snapshot_id": "v46-research-2026-04-23"' in manifest.read_text()
@pytest.mark.asyncio
async def test_fetch_attention_features_bulk_uses_cache_after_first_hit(tmp_path, monkeypatch) -> None:
cache = AttentionEventCache(str(tmp_path))
calls = {"count": 0}
async def fake_get_attention(self, ticker, event_date):
calls["count"] += 1
return EventAttentionResponse(
ticker=ticker,
event_date=event_date,
entity=EntityInfo(
ticker=ticker,
canonical_name=ticker,
resolver_confidence=0.91,
),
wiki=WikiFeatures(spike_10d=2.8, zscore_20d=3.4),
news=NewsFeatures(article_count_3d=6, us_article_count_3d=4),
metadata={},
)
monkeypatch.setattr(catalyst_mod.AttentionService, "get_event_attention", fake_get_attention)
first = await fetch_attention_features_bulk(
[("ABC", "2026-02-10")],
client=object(),
cache=cache,
concurrency=1,
)
second = await fetch_attention_features_bulk(
[("ABC", "2026-02-10")],
client=object(),
cache=cache,
concurrency=1,
)
assert first["ABC"]["2026-02-10"]["attention_wiki_spike_10d"] == 2.8
assert first["ABC"]["2026-02-10"]["attention_us_article_count_3d"] == 4
assert second == first
assert calls["count"] == 1