"""Tests for sentiment-collection scoping (``_get_sentiment_priority_tickers``). The activation gate qualifies setups on 12-1 momentum percentile, a different axis than composite score. These tests pin the fix that adds the gate's momentum leaders to the sentiment relevant-set so a freshly-qualifying ticker isn't left without sentiment. """ from __future__ import annotations from datetime import date, datetime, timedelta, timezone import pytest from app import scheduler from app.models.ohlcv import OHLCVRecord from app.models.settings import SystemSetting from app.models.ticker import Ticker @pytest.fixture async def session(): from tests.conftest import _test_session_factory async with _test_session_factory() as s: yield s async def _seed_history(session, symbol: str, rate: float, n: int = 280) -> Ticker: """Seed a ticker with a full year+ of daily closes growing at ``rate``.""" t = Ticker(symbol=symbol) session.add(t) await session.flush() base = date(2024, 1, 1) for i in range(n): close = 100.0 * (rate ** i) session.add(OHLCVRecord( ticker_id=t.id, date=base + timedelta(days=i), open=close, high=close, low=close, close=close, volume=1_000_000, )) await session.commit() return t async def _set_min_momentum(session, value: str) -> None: session.add(SystemSetting( key="activation_min_momentum_percentile", value=value, updated_at=datetime.now(timezone.utc), )) await session.commit() async def test_momentum_leader_is_included_without_composite_or_watchlist(session): """A top-percentile momentum ticker is fetched even when it has no composite score, no watchlist entry, and no open trade — the case that previously left qualifying setups with no sentiment.""" await _seed_history(session, "LEADER", rate=1.010) # strong uptrend → pct 100 await _seed_history(session, "LAGGARD", rate=0.999) # declining → pct 0 await _set_min_momentum(session, "80") symbols = await scheduler._get_sentiment_priority_tickers(session) assert "LEADER" in symbols # Below the gate's percentile and not otherwise relevant → not fetched. assert "LAGGARD" not in symbols async def test_momentum_leaders_skipped_when_gate_disabled(session): """With the momentum gate off (min percentile 0), the leader is no longer pulled in solely on momentum — scoping falls back to the base relevant set.""" await _seed_history(session, "LEADER", rate=1.010) await _seed_history(session, "LAGGARD", rate=0.999) await _set_min_momentum(session, "0") symbols = await scheduler._get_sentiment_priority_tickers(session) assert "LEADER" not in symbols assert "LAGGARD" not in symbols