Files
All-in-one-Financial-Analysis/atlas-terminal/server/routers/news.py
T

86 lines
2.8 KiB
Python

"""News router -- Finviz scrape + Google News RSS + Yahoo Finance RSS."""
import asyncio
import logging
from typing import List
from fastapi import APIRouter, HTTPException, Query
from server.models.schemas import NewsItem
from server.services.news_aggregator import merge_news_for_router
router = APIRouter()
logger = logging.getLogger(__name__)
@router.get(
"/{ticker}",
response_model=List[NewsItem],
summary="Aggregated news (Finviz + Google + Yahoo)",
)
async def get_news(ticker: str):
"""Return recent articles: Finviz table, Google News RSS, Yahoo headline RSS."""
try:
merged = await asyncio.to_thread(
merge_news_for_router, ticker.upper(), 40
)
return [NewsItem(**item) for item in merged]
except Exception as exc:
logger.exception("News fetch failed for %s", ticker)
raise HTTPException(status_code=500, detail=f"News fetch failed: {exc}") from exc
@router.get(
"/{ticker}/ai-summary",
summary="AI-summarized news (optional)",
)
async def ai_news_summary(
ticker: str,
api_key: str = Query("", description="Google Gemini API key (optional)"),
):
"""Fetch merged news and optionally summarize with Gemini."""
try:
all_items = await asyncio.to_thread(
merge_news_for_router, ticker.upper(), 30
)
headlines: List[str] = []
for item in all_items:
title = (item.get("title") or "").strip()
if title:
headlines.append(title)
headlines = headlines[:20]
all_items = all_items[:20]
if not api_key or not api_key.strip():
return {
"ticker": ticker.upper(),
"summary": None,
"headlines": headlines,
"items": all_items,
}
from server.services.gemini_service import get_gemini_model, _generate_with_retry
model = get_gemini_model(api_key)
headline_text = "\n".join(f"- {h}" for h in headlines)
prompt = f"""You are a financial news analyst. Below are the latest headlines for {ticker.upper()}.
Provide a concise 3-5 sentence executive summary of the overall sentiment and key themes.
Headlines:
{headline_text}"""
response = await asyncio.to_thread(
_generate_with_retry, model, prompt, {"temperature": 0.2, "max_output_tokens": 512}
)
summary = (response.text or "").strip() if response else ""
return {
"ticker": ticker.upper(),
"summary": summary,
"headlines": headlines,
"items": all_items,
}
except Exception as exc:
logger.exception("AI news summary failed for %s", ticker)
raise HTTPException(status_code=500, detail=f"AI news summary failed: {exc}") from exc