Files
shawnkim1997 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

129 lines
4.2 KiB
Python

"""
ATLAS Terminal — Thin Orchestrator
All-in-One Financial Analysis Dashboard — Hybrid Architecture
"""
import os
os.environ["OBJC_DISABLE_INITIALIZE_FORK_SAFETY"] = "YES"
import streamlit as st
from config.constants import MARKET_OPTIONS
from config.theme import SOFT_NAVY_CSS, HEADER_HTML
from utils.ticker import get_global_ticker
from data.market import _get_ticker_bar_data
from data.fundamentals import get_sector_industry
from views.sidebar import render_sidebar
from views.tab1_quant import render_tab1_quantitative
from views.tab1_ai import render_tab1_ai_analysis
from views.tab1_filings import render_tab1_filings
from views.tab2_dcf import render_tab2
from views.tab3_comps import render_tab3
from views.tab4_news import render_tab4
from views.tab5_markets import render_tab5
from views.tab6_crypto import render_tab6
from views.tab7_technical import render_tab7
from views.tab8_financial_statement import render_tab8
from views.tab9_portfolio import render_tab9
from views.tab10_valuation import render_tab10
from views.tab11_estimates import render_tab11
try:
import yfinance as yf
except ImportError:
yf = None
try:
from dotenv import load_dotenv
load_dotenv()
except ImportError:
pass
# ---------- Page config & theme ----------
st.set_page_config(page_title="ATLAS Terminal", layout="wide", initial_sidebar_state="expanded")
st.markdown(SOFT_NAVY_CSS, unsafe_allow_html=True)
st.markdown(HEADER_HTML, unsafe_allow_html=True)
# ---------- Ticker bar — major indices & crypto ----------
ticker_data = _get_ticker_bar_data() if yf else []
if ticker_data:
_cells = ""
for item in ticker_data:
_c = "#34D399" if item["change"] >= 0 else "#F87171"
_a = "\u25b2" if item["change"] >= 0 else "\u25bc"
_cells += (
f'<div style="flex:1;text-align:center;padding:10px 6px;background:rgba(255,255,255,0.03);'
f'border-radius:6px;border:1px solid rgba(255,255,255,0.06);min-width:100px;">'
f'<div style="color:#6B7280;font-size:0.65rem;font-weight:600;letter-spacing:1px;">{item["label"]}</div>'
f'<div style="color:#F3F4F6;font-size:1.05rem;font-family:\'Inter\',JetBrains Mono,monospace;font-weight:700;margin:2px 0;">{item["price"]:,.2f}</div>'
f'<div style="color:{_c};font-size:0.75rem;font-family:\'Inter\',JetBrains Mono,monospace;">{_a} {item["change"]:+.2f}%</div>'
f'</div>'
)
st.markdown(f'<div style="display:flex;gap:8px;margin-bottom:16px;overflow-x:auto;">{_cells}</div>', unsafe_allow_html=True)
# ---------- Sidebar ----------
ticker = render_sidebar()
# ---------- Tabs ----------
tab1, tab2, tab3, tab4, tab5, tab6, tab7, tab8, tab9, tab10, tab11 = st.tabs([
"\U0001f4ca 10-K & MD&A",
"\U0001f4b0 DCF",
"\U0001f3ed Comps",
"\U0001f4f0 News",
"\U0001f30d Markets",
"\u20bf Crypto",
"\U0001f6e1 Technical",
"\U0001f4c4 Financials",
"\U0001f4bc Portfolio",
"\U0001f4b9 Valuation",
"\U0001f4c8 Estimates",
])
# ----- Tab 1: 10-K & MD&A Insights -----
with tab1:
market = st.session_state.get("market") or MARKET_OPTIONS[0]
quant_ticker = get_global_ticker(ticker, market) if ticker else ""
st.subheader("10-K & MD&A Insights — Qualitative and Quantitative")
if ticker:
si = get_sector_industry(quant_ticker)
sector, industry = si.get("sector", "N/A"), si.get("industry", "N/A")
st.caption(
f"Sector: **{sector}** · Industry: **{industry}**"
+ (f" · Ticker: **{quant_ticker}**" if quant_ticker != ticker else "")
)
if ticker:
google_api_key = (st.session_state.get("google_api_key") or "").strip()
sec_email = (st.session_state.get("sec_email") or "").strip()
render_tab1_quantitative(ticker, quant_ticker, market, sector, industry, google_api_key, sec_email)
render_tab1_ai_analysis(ticker, quant_ticker, market)
render_tab1_filings(ticker, market)
with tab2:
render_tab2(ticker)
with tab3:
render_tab3(ticker)
with tab4:
render_tab4(ticker)
with tab5:
render_tab5()
with tab6:
render_tab6()
with tab7:
render_tab7(ticker)
with tab8:
render_tab8(ticker)
with tab9:
render_tab9()
with tab10:
render_tab10(ticker)
with tab11:
render_tab11(ticker)