sentiment: LLM buy/hold/avoid + full analysis, and search-budget scoping
Deploy / lint (push) Successful in 6s
Deploy / test (push) Successful in 34s
Deploy / deploy (push) Successful in 21s

Richer LLM output (same grounded call, ~no extra cost):
- All providers now also return a recommendation (buy/hold/avoid) and a thorough
  reasoning paragraph; Gemini now actually captures reasoning + grounding
  citations (it was dropping them). Stored on sentiment_scores (migration 008),
  exposed in the API; display-only — NOT fed into the composite/EV.
- Ticker Sentiment panel shows an "LLM view" badge and a "Full analysis & sources"
  expander with the complete reasoning + citations.

Search-budget scoping (Gemini grounding free tier = 5000/mo):
- collect_sentiment now targets only watchlist + open paper trades + top-N by
  composite, skips tickers refreshed within sentiment_fresh_hours (72h), and caps
  per run (sentiment_max_per_run). Once the relevant set is fresh, runs spend 0
  searches until it ages out — bounding monthly usage well under the free tier.
- Widened sentiment lookback to 7d (scoring + display) so sparser collection
  still feeds the dimension score.

Deploy: alembic upgrade (sentiment_scores.recommendation). Switch provider to
Gemini Flash in Admin for the cost win (grounded, cheapest).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-16 16:34:19 +02:00
parent a69557f5d8
commit e5166ed668
16 changed files with 219 additions and 36 deletions
+1 -1
View File
@@ -347,7 +347,7 @@ async def _compute_sentiment_score(
get_sentiment_scores,
)
lookback_hours: float = 24
lookback_hours: float = 168 # 7 days — sentiment is collected sparsely to stay in free tier
decay_rate: float = 0.1
try:
+2
View File
@@ -37,6 +37,7 @@ async def store_sentiment(
timestamp: datetime | None = None,
reasoning: str = "",
citations: list[dict] | None = None,
recommendation: str | None = None,
) -> SentimentScore:
"""Store a new sentiment record for a ticker."""
ticker = await _get_ticker(db, symbol)
@@ -55,6 +56,7 @@ async def store_sentiment(
timestamp=timestamp,
reasoning=reasoning,
citations_json=json.dumps(citations),
recommendation=recommendation,
)
db.add(record)
await db.commit()