mirror of
https://github.com/shawnkim1997/All-in-one-Financial-Analysis.git
synced 2026-08-15 19:38:07 +00:00
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>
143 lines
3.9 KiB
Python
143 lines
3.9 KiB
Python
#!/usr/bin/env python3
|
|
"""Generate README.md for ATLAS Terminal from project metadata.
|
|
|
|
Scans the project structure and produces a fresh README with:
|
|
- Feature list derived from router files
|
|
- Tech stack from requirements.txt and package.json
|
|
- Changelog from recent git commits
|
|
|
|
Run manually or via the GitHub Actions workflow (.github/workflows/update-readme.yml).
|
|
"""
|
|
|
|
import json
|
|
import subprocess
|
|
import textwrap
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
|
|
PROJECT_ROOT = Path(__file__).resolve().parent.parent
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Helpers
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def _read_requirements() -> list[str]:
|
|
"""Return top-level package names from requirements.txt."""
|
|
req_path = PROJECT_ROOT / "requirements.txt"
|
|
if not req_path.exists():
|
|
return []
|
|
lines = req_path.read_text().splitlines()
|
|
packages = []
|
|
for line in lines:
|
|
line = line.strip()
|
|
if line and not line.startswith("#"):
|
|
name = line.split(">=")[0].split("==")[0].split("[")[0].strip()
|
|
packages.append(name)
|
|
return packages
|
|
|
|
|
|
def _read_package_json_deps() -> list[str]:
|
|
"""Return dependency names from apps/web/package.json."""
|
|
pkg_path = PROJECT_ROOT / "apps" / "web" / "package.json"
|
|
if not pkg_path.exists():
|
|
return []
|
|
try:
|
|
data = json.loads(pkg_path.read_text())
|
|
deps = list(data.get("dependencies", {}).keys())
|
|
deps += list(data.get("devDependencies", {}).keys())
|
|
return deps
|
|
except (json.JSONDecodeError, OSError):
|
|
return []
|
|
|
|
|
|
def _detect_routers() -> list[str]:
|
|
"""Return router module names from server/routers/."""
|
|
router_dir = PROJECT_ROOT / "server" / "routers"
|
|
if not router_dir.exists():
|
|
return []
|
|
return sorted(
|
|
f.stem
|
|
for f in router_dir.glob("*.py")
|
|
if f.stem != "__init__"
|
|
)
|
|
|
|
|
|
def _recent_commits(n: int = 10) -> list[str]:
|
|
"""Return the last *n* one-line commit messages."""
|
|
try:
|
|
result = subprocess.run(
|
|
["git", "log", f"-{n}", "--pretty=format:%s"],
|
|
capture_output=True, text=True, cwd=str(PROJECT_ROOT),
|
|
)
|
|
if result.returncode == 0:
|
|
return [line for line in result.stdout.splitlines() if line.strip()]
|
|
except FileNotFoundError:
|
|
pass
|
|
return []
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Template
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def generate() -> str:
|
|
routers = _detect_routers()
|
|
py_deps = _read_requirements()
|
|
js_deps = _read_package_json_deps()
|
|
commits = _recent_commits(10)
|
|
|
|
feature_bullets = "\n".join(f"- **{r.replace('_', ' ').title()}**" for r in routers)
|
|
py_stack = ", ".join(py_deps[:8]) + (" ..." if len(py_deps) > 8 else "")
|
|
js_stack = ", ".join(js_deps[:8]) + (" ..." if len(js_deps) > 8 else "")
|
|
changelog = "\n".join(f"- {c}" for c in commits) if commits else "- (no commits yet)"
|
|
today = datetime.now().strftime("%Y-%m-%d")
|
|
|
|
readme = textwrap.dedent(f"""\
|
|
# ATLAS Terminal
|
|
|
|
> Personal Bloomberg-style financial terminal -- real-time market data,
|
|
> AI-powered analysis, DCF valuation, and portfolio management.
|
|
|
|
## Features
|
|
|
|
{feature_bullets}
|
|
|
|
## Tech Stack
|
|
|
|
**Backend (Python):** {py_stack}
|
|
|
|
**Frontend (Next.js):** {js_stack}
|
|
|
|
## Quick Start
|
|
|
|
```bash
|
|
# Backend
|
|
cd atlas-terminal
|
|
pip install -r requirements.txt
|
|
uvicorn server.main:app --reload --port 8000
|
|
|
|
# Frontend
|
|
cd apps/web
|
|
npm install && npm run dev
|
|
```
|
|
|
|
## Recent Changes
|
|
|
|
{changelog}
|
|
|
|
---
|
|
*Auto-generated on {today}*
|
|
""")
|
|
return readme
|
|
|
|
|
|
def main():
|
|
readme_path = PROJECT_ROOT / "README.md"
|
|
content = generate()
|
|
readme_path.write_text(content)
|
|
print(f"README.md written ({len(content)} bytes)")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|