mirror of
https://github.com/shawnkim1997/All-in-one-Financial-Analysis.git
synced 2026-08-12 18:18:03 +00:00
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>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
56a9561f71
commit
b2acda81ee
@@ -0,0 +1,88 @@
|
||||
"""
|
||||
Analyst estimates — earnings & revenue forecasts, consensus targets.
|
||||
"""
|
||||
import streamlit as st
|
||||
import pandas as pd
|
||||
|
||||
try:
|
||||
import yfinance as yf
|
||||
except ImportError:
|
||||
yf = None
|
||||
|
||||
|
||||
@st.cache_data(ttl=600)
|
||||
def get_analyst_estimates(ticker: str) -> dict:
|
||||
"""Fetch analyst earnings and revenue estimates from yfinance."""
|
||||
if not yf or not ticker:
|
||||
return {}
|
||||
try:
|
||||
t = yf.Ticker(ticker)
|
||||
result = {}
|
||||
|
||||
# Earnings estimates
|
||||
ee = getattr(t, "earnings_estimate", None)
|
||||
if ee is not None and not ee.empty:
|
||||
result["earnings_estimate"] = ee
|
||||
|
||||
# Revenue estimates
|
||||
re = getattr(t, "revenue_estimate", None)
|
||||
if re is not None and not re.empty:
|
||||
result["revenue_estimate"] = re
|
||||
|
||||
# EPS trend
|
||||
et = getattr(t, "eps_trend", None)
|
||||
if et is not None and not et.empty:
|
||||
result["eps_trend"] = et
|
||||
|
||||
# Earnings history
|
||||
eh = getattr(t, "earnings_history", None)
|
||||
if eh is not None and not eh.empty:
|
||||
result["earnings_history"] = eh
|
||||
|
||||
# Growth estimates
|
||||
ge = getattr(t, "growth_estimates", None)
|
||||
if ge is not None and not ge.empty:
|
||||
result["growth_estimates"] = ge
|
||||
|
||||
# Price targets
|
||||
info = t.info or {}
|
||||
result["targets"] = {
|
||||
"current": info.get("currentPrice") or info.get("regularMarketPrice"),
|
||||
"mean": info.get("targetMeanPrice"),
|
||||
"high": info.get("targetHighPrice"),
|
||||
"low": info.get("targetLowPrice"),
|
||||
"median": info.get("targetMedianPrice"),
|
||||
"recommendation": info.get("recommendationKey", "N/A"),
|
||||
"num_analysts": info.get("numberOfAnalystOpinions"),
|
||||
}
|
||||
|
||||
return result
|
||||
except Exception:
|
||||
return {}
|
||||
|
||||
|
||||
@st.cache_data(ttl=600)
|
||||
def get_earnings_dates(ticker: str) -> pd.DataFrame:
|
||||
"""Fetch historical and upcoming earnings dates with surprise data."""
|
||||
if not yf or not ticker:
|
||||
return pd.DataFrame()
|
||||
try:
|
||||
t = yf.Ticker(ticker)
|
||||
dates = t.earnings_dates
|
||||
if dates is not None and not dates.empty:
|
||||
return dates.head(12)
|
||||
except Exception:
|
||||
pass
|
||||
return pd.DataFrame()
|
||||
|
||||
|
||||
def format_estimate_table(df: pd.DataFrame) -> pd.DataFrame:
|
||||
"""Format estimate DataFrame for display with proper number formatting."""
|
||||
if df is None or df.empty:
|
||||
return pd.DataFrame()
|
||||
display = df.copy()
|
||||
for col in display.columns:
|
||||
display[col] = display[col].apply(
|
||||
lambda v: f"{v:,.2f}" if isinstance(v, (int, float)) and v == v else "N/A"
|
||||
)
|
||||
return display
|
||||
Reference in New Issue
Block a user