feat: always-fresh sentiment for top picks, watchlist & open trades

Tiered, uncapped sentiment scope so the names that matter are never shown
without sentiment.

- Priority (always fully refreshed): top-pick feeders — momentum leaders with a
  tradeable long setup over the R:R floor (the tickers that are, or could become
  with positive sentiment, the dashboard top pick) — plus the curated watchlist
  and open paper trades.
- Filler: top-N by composite, a discovery net, fetched after the priority set so
  a mid-run rate limit lands the important names first.
- Removed the per-run cap (sentiment_max_per_run): the relevant set is naturally
  bounded (watchlist <= 20, composite <= top_composite), so a full refresh stays
  inside the free tier. extra="ignore" keeps a stale env var from breaking startup.
- Refresh window 72h -> 120h (5 days): sentiment shifts slowly, score window is 7d.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-27 15:59:58 +02:00
parent 65dd53baa3
commit 2f21c685e8
3 changed files with 329 additions and 96 deletions
+9 -5
View File
@@ -2,7 +2,7 @@ from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8")
model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8", extra="ignore")
# Database
database_url: str = "postgresql+asyncpg://stock_backend:changeme@localhost:5432/stock_data_backend"
@@ -49,10 +49,14 @@ class Settings(BaseSettings):
data_collector_frequency: str = "daily"
sentiment_poll_interval_minutes: int = 30
# Sentiment search-budget controls (Gemini grounding free tier = 5000/month).
# Only fetch sentiment for relevant tickers (watchlist + open trades + top-N by
# composite), skip ones refreshed within fresh_hours, and cap per run.
sentiment_fresh_hours: int = 72
sentiment_max_per_run: int = 25
# Scope (see _get_sentiment_priority_tickers): everything that matters is always
# refreshed in full — open paper trades + the curated watchlist + top-pick
# feeders (momentum leaders with a tradeable long setup) — plus a top-N composite
# discovery net. No per-run cap: the set is naturally bounded (watchlist <= 20,
# composite <= top_composite), so a full refresh stays well inside the free tier.
# Skip anything refreshed within fresh_hours (5 days: sentiment shifts slowly and
# the score window is 7 days).
sentiment_fresh_hours: int = 120
sentiment_top_composite: int = 30
fundamental_fetch_frequency: str = "weekly" # quarterly-ish data; weekly conserves API quota
rr_scan_frequency: str = "daily"