mirror of
https://github.com/shawnkim1997/All-in-one-Financial-Analysis.git
synced 2026-07-27 18:47:44 +00:00
b2acda81ee
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>
129 lines
4.2 KiB
Python
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)
|