"""Pick journal + scorer: the smallest checks that fail if the logic breaks.""" from datetime import datetime, timedelta from zoneinfo import ZoneInfo import pytest from research.pick_journal import add_pick, ensure_schema, list_picks from research.pick_scoring import score_pick from storage.db_pg import get_connection ET_ = ZoneInfo("America/New_York") @pytest.fixture() def con(): c = get_connection(":memory:") yield c c.close() def test_pick_validity_locks_at_open(con): early = datetime(2026, 8, 20, 9, 29, tzinfo=ET_) late = datetime(2026, 8, 20, 9, 31, tzinfo=ET_) assert add_pick(con, "aapl", now=early)["valid"] is True assert add_pick(con, "TSLA", now=late)["valid"] is False picks = list_picks(con, early.date()) assert [p["symbol"] for p in picks] == ["AAPL", "TSLA"] assert [p["valid"] for p in picks] == [True, False] def test_pick_rejects_garbage(con): with pytest.raises(ValueError): add_pick(con, "") with pytest.raises(ValueError): add_pick(con, "TOOLONGG") with pytest.raises(ValueError): add_pick(con, "AAPL", direction="sideways") def test_score_pick_long_and_short(con): ensure_schema(con) d = datetime(2026, 8, 20).date() base = datetime(2026, 8, 20, 9, 30) for i, (o, c) in enumerate([(10.0, 10.2), (10.2, 10.5), (10.5, 11.0)]): con.execute( "INSERT INTO minute_bars (symbol, timestamp, session_date, is_premarket, " "is_regular_hours, is_afterhours, open, high, low, close, volume) " "VALUES (?, ?, ?, false, true, false, ?, ?, ?, ?, 1000)", ["TEST", base + timedelta(minutes=i), d, o, max(o, c), min(o, c), c]) s = score_pick(con, "TEST", d, "long") # entry 10.0 open, exit 11.0 close: raw +10%, minus 2% cost (>=$3) assert abs(s["net"] - 0.08) < 1e-9 s2 = score_pick(con, "TEST", d, "short") assert abs(s2["net"] - (-0.12)) < 1e-9 assert score_pick(con, "NOPE", d, "long") is None