Files
All-in-one-Financial-Analysis/atlas-terminal/server/routers/earnings.py
T
shawnkim1997andClaude Opus 4.6 b2acda81ee feat: add Atlas Terminal — Next.js 14 + FastAPI full-stack migration
Complete migration from Streamlit to Next.js 14 App Router + FastAPI backend.

Frontend (Next.js 14):
- 10 pages: Overview, Research, Valuation, Technical, Markets, Earnings, News, Portfolio, Filings, Settings
- Terminal Noir dark theme with custom Tailwind config
- TradingView Lightweight Charts for candlestick/volume
- Valuation: DCF, Sensitivity Matrix, Monte Carlo, Tornado, Reverse DCF
- Financial Statements table with YoY growth badges and margin rows
- SEC EDGAR inline filing viewer with section tabs
- News split-view with iframe article embedding
- Technical Analysis with RSI, MACD, Bollinger, Fibonacci, Moving Averages
- Earnings beat/miss visualization
- AI Copilot chat panel with Gemini integration

Backend (FastAPI):
- 13 routers: market_data, financials, valuation, technical, earnings, insider, edgar, news, portfolio, analysis, chat, estimates, fx
- Services: DCF engine, Monte Carlo simulation, sensitivity analysis, risk metrics, SEC parser, technical indicators
- yfinance + yahooquery data sources with fallback pattern
- SQLite caching layer

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-21 02:10:10 +00:00

89 lines
3.4 KiB
Python

"""Earnings router -- earnings history, upcoming dates, and transcripts."""
from typing import Any, Dict, List
from fastapi import APIRouter, HTTPException
router = APIRouter()
def _safe_float(val, default=None):
if val is None:
return default
try:
import math
f = float(val)
return default if math.isnan(f) or math.isinf(f) else f
except (TypeError, ValueError):
return default
@router.get("/{ticker}/history", summary="Earnings history (EPS actual vs estimate)")
async def earnings_history(ticker: str) -> Dict[str, Any]:
try:
import yfinance as yf
t = yf.Ticker(ticker.upper())
earnings = t.earnings_history
if earnings is None or (hasattr(earnings, 'empty') and earnings.empty):
return {"ticker": ticker.upper(), "history": []}
history: List[Dict[str, Any]] = []
if hasattr(earnings, 'iterrows'):
for idx, row in earnings.iterrows():
history.append({
"date": str(idx)[:10],
"eps_actual": _safe_float(row.get("epsActual", row.get("Reported EPS"))),
"eps_estimate": _safe_float(row.get("epsEstimate", row.get("EPS Estimate"))),
"surprise": round(_safe_float(row.get("surprisePercent", row.get("Surprise(%)")), 0) * 100, 2),
})
return {"ticker": ticker.upper(), "history": history[-12:]}
except Exception as exc:
raise HTTPException(status_code=500, detail=f"Earnings history failed: {exc}") from exc
@router.get("/{ticker}/calendar", summary="Upcoming earnings date")
async def earnings_calendar(ticker: str) -> Dict[str, Any]:
try:
import yfinance as yf
t = yf.Ticker(ticker.upper())
cal = t.calendar
if cal is None:
return {"ticker": ticker.upper(), "next_earnings": None}
if isinstance(cal, dict):
earnings_date = cal.get("Earnings Date")
if isinstance(earnings_date, list) and earnings_date:
earnings_date = str(earnings_date[0])[:10]
elif earnings_date:
earnings_date = str(earnings_date)[:10]
return {
"ticker": ticker.upper(),
"next_earnings": earnings_date,
"revenue_estimate": _safe_float(cal.get("Revenue Average")),
"eps_estimate": _safe_float(cal.get("Earnings Average")),
}
return {"ticker": ticker.upper(), "next_earnings": None}
except Exception as exc:
raise HTTPException(status_code=500, detail=f"Earnings calendar failed: {exc}") from exc
@router.get("/{ticker}/quarterly", summary="Quarterly earnings data")
async def quarterly_earnings(ticker: str) -> Dict[str, Any]:
try:
import yfinance as yf
t = yf.Ticker(ticker.upper())
quarterly = t.quarterly_earnings
data: List[Dict[str, Any]] = []
if quarterly is not None and hasattr(quarterly, 'iterrows'):
for idx, row in quarterly.iterrows():
data.append({
"period": str(idx),
"revenue": _safe_float(row.get("Revenue")),
"earnings": _safe_float(row.get("Earnings")),
})
return {"ticker": ticker.upper(), "quarterly": data}
except Exception as exc:
raise HTTPException(status_code=500, detail=f"Quarterly earnings failed: {exc}") from exc