mirror of
https://github.com/shawnkim1997/All-in-one-Financial-Analysis.git
synced 2026-08-20 14:18:05 +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>
70 lines
2.5 KiB
Python
70 lines
2.5 KiB
Python
"""Insider trading router -- recent insider transactions from yfinance."""
|
|
|
|
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}", summary="Recent insider transactions")
|
|
async def insider_transactions(ticker: str) -> Dict[str, Any]:
|
|
try:
|
|
import yfinance as yf
|
|
t = yf.Ticker(ticker.upper())
|
|
insiders = t.insider_transactions
|
|
if insiders is None or (hasattr(insiders, 'empty') and insiders.empty):
|
|
return {"ticker": ticker.upper(), "transactions": []}
|
|
|
|
transactions: List[Dict[str, Any]] = []
|
|
if hasattr(insiders, 'iterrows'):
|
|
for _, row in insiders.iterrows():
|
|
transactions.append({
|
|
"date": str(row.get("Start Date", ""))[:10] if "Start Date" in row.index else "",
|
|
"insider": str(row.get("Insider", "")),
|
|
"relation": str(row.get("Position", row.get("Relationship", ""))),
|
|
"transaction": str(row.get("Transaction", "")),
|
|
"shares": _safe_float(row.get("Shares")),
|
|
"value": _safe_float(row.get("Value")),
|
|
})
|
|
|
|
return {"ticker": ticker.upper(), "transactions": transactions[:30]}
|
|
except Exception as exc:
|
|
raise HTTPException(status_code=500, detail=f"Insider transactions failed: {exc}") from exc
|
|
|
|
|
|
@router.get("/{ticker}/holders", summary="Institutional and mutual fund holders")
|
|
async def holders(ticker: str) -> Dict[str, Any]:
|
|
try:
|
|
import yfinance as yf
|
|
t = yf.Ticker(ticker.upper())
|
|
|
|
institutional = []
|
|
inst = t.institutional_holders
|
|
if inst is not None and hasattr(inst, 'iterrows'):
|
|
for _, row in inst.iterrows():
|
|
institutional.append({
|
|
"holder": str(row.get("Holder", "")),
|
|
"shares": _safe_float(row.get("Shares")),
|
|
"value": _safe_float(row.get("Value")),
|
|
"pct_held": _safe_float(row.get("% Out")),
|
|
})
|
|
|
|
return {
|
|
"ticker": ticker.upper(),
|
|
"institutional": institutional[:15],
|
|
}
|
|
except Exception as exc:
|
|
raise HTTPException(status_code=500, detail=f"Holders failed: {exc}") from exc
|