Withhold stale-score trade recommendations

This commit is contained in:
2026-07-11 16:56:52 +02:00
parent 292b9934b1
commit 727b147c81
3 changed files with 115 additions and 1 deletions
@@ -689,6 +689,31 @@ async def test_live_recommendation_payload_uses_current_score_and_sentiment(
assert persisted.reasoning == old_reasoning
@pytest.mark.asyncio
async def test_live_recommendation_withholds_stale_scores(
db_session: AsyncSession,
):
stale_setup = await _seed_stale_setup_with_current_scores(db_session)
composite = (
await db_session.execute(
select(CompositeScore).where(CompositeScore.ticker_id == stale_setup.ticker_id)
)
).scalar_one()
composite.is_stale = True
await db_session.flush()
rows = await get_trade_setups(
db_session,
symbol="TTWO",
live_recommendation=True,
)
assert len(rows) == 1
assert rows[0]["confidence_score"] is None
assert rows[0]["recommended_action"] == "NEUTRAL"
assert rows[0]["reasoning"] == "Score refresh pending; recommendation withheld."
@pytest.mark.asyncio
async def test_live_recommendation_filters_apply_to_live_values(
db_session: AsyncSession,
+46
View File
@@ -3,7 +3,10 @@
from __future__ import annotations
import pytest
from datetime import datetime, timezone
from sqlalchemy import select
from app.models.score import CompositeScore, DimensionScore
from app.models.ticker import Ticker
from app.services import rr_scanner_service, scoring_service
from tests.conftest import _test_session_factory # type: ignore
@@ -42,6 +45,49 @@ async def test_scan_proceeds_when_score_refresh_fails(session, monkeypatch):
assert setups == []
async def test_scan_marks_scores_stale_when_refresh_fails(session, monkeypatch):
ticker = Ticker(symbol="AAA")
session.add(ticker)
await session.flush()
now = datetime.now(timezone.utc)
session.add_all([
DimensionScore(
ticker_id=ticker.id,
dimension="technical",
score=70.0,
is_stale=False,
computed_at=now,
),
CompositeScore(
ticker_id=ticker.id,
score=70.0,
is_stale=False,
weights_json="{}",
computed_at=now,
),
])
await session.commit()
async def _boom(db, symbol):
raise RuntimeError("scoring unavailable")
async def _fake_scan_ticker(db, symbol, *args, **kwargs):
comp = (
await db.execute(select(CompositeScore).where(CompositeScore.ticker_id == ticker.id))
).scalar_one()
dimensions = (
await db.execute(select(DimensionScore).where(DimensionScore.ticker_id == ticker.id))
).scalars().all()
assert comp.is_stale is True
assert all(score.is_stale for score in dimensions)
return []
monkeypatch.setattr(scoring_service, "compute_all_dimensions", _boom)
monkeypatch.setattr(rr_scanner_service, "scan_ticker", _fake_scan_ticker)
await rr_scanner_service.scan_all_tickers(session)
async def test_scan_error_does_not_stop_later_tickers(session, monkeypatch):
session.add_all([Ticker(symbol="AAA"), Ticker(symbol="BBB")])
await session.commit()