From 3e1324eff69e4b121f85223758b872c7fb5dc027 Mon Sep 17 00:00:00 2001 From: Tyler Date: Sun, 29 Mar 2026 15:41:42 -0700 Subject: Migrate insiders and filings from FMP to yfinance FMP v3 insider-trading and sec_filings endpoints are legacy-gated. Switch to yfinance (t.insider_transactions, t.sec_filings) which provides the same data for free with no API key required. Co-Authored-By: Claude Sonnet 4.6 --- components/filings.py | 88 +++++++++++++++++++-------------------------------- 1 file changed, 32 insertions(+), 56 deletions(-) (limited to 'components/filings.py') diff --git a/components/filings.py b/components/filings.py index d366a2b..a1e4417 100644 --- a/components/filings.py +++ b/components/filings.py @@ -1,8 +1,7 @@ """SEC filings — recent 10-K, 10-Q, 8-K and other forms with direct links.""" -import pandas as pd import streamlit as st -from services.fmp_service import get_sec_filings +from services.data_service import get_sec_filings _FORM_DESCRIPTIONS = { @@ -24,22 +23,19 @@ _FORM_COLORS = { } -def _describe_form(form_type: str) -> str: - return _FORM_DESCRIPTIONS.get(form_type.strip().upper(), "") - - def render_filings(ticker: str): with st.spinner("Loading SEC filings…"): - raw = get_sec_filings(ticker) + filings = get_sec_filings(ticker) - if not raw: - st.info("No SEC filing data available. Requires FMP API key.") + if not filings: + st.info("No SEC filing data available for this ticker.") return - # Collect unique form types for filter - form_types = sorted({str(r.get("type") or r.get("form") or "").strip() for r in raw if r.get("type") or r.get("form")}) - filter_options = ["All"] + [t for t in ["10-K", "10-Q", "8-K"] if t in form_types] + \ - [t for t in form_types if t not in ("10-K", "10-Q", "8-K")] + # yfinance returns: date (datetime.date), type, title, edgarUrl, exhibits (dict) + form_types = sorted({str(f.get("type", "")).strip() for f in filings if f.get("type")}) + priority = [t for t in ["10-K", "10-Q", "8-K"] if t in form_types] + other = [t for t in form_types if t not in ("10-K", "10-Q", "8-K")] + filter_options = ["All"] + priority + other filter_col, _ = st.columns([1, 3]) with filter_col: @@ -47,69 +43,49 @@ def render_filings(ticker: str): # Summary counts counts = {} - for r in raw: - ft = str(r.get("type") or r.get("form") or "Other").strip() + for f in filings: + ft = str(f.get("type", "Other")).strip() counts[ft] = counts.get(ft, 0) + 1 - priority_forms = ["10-K", "10-Q", "8-K"] - summary_forms = [f for f in priority_forms if f in counts] - if summary_forms: - cols = st.columns(len(summary_forms)) - for col, ft in zip(cols, summary_forms): - col.metric(ft, counts[ft]) + if priority: + cols = st.columns(len(priority)) + for col, ft in zip(cols, priority): + col.metric(ft, counts.get(ft, 0)) st.write("") - # Filter - if selected_type == "All": - filtered = raw - else: - filtered = [ - r for r in raw - if str(r.get("type") or r.get("form") or "").strip() == selected_type - ] + filtered = filings if selected_type == "All" else [ + f for f in filings if str(f.get("type", "")).strip() == selected_type + ] if not filtered: st.info("No filings match the current filter.") return - # Build display table - rows = [] - for item in filtered: - form_type = str(item.get("type") or item.get("form") or "—").strip() - date = str(item.get("filingDate") or item.get("date") or "")[:10] - description = item.get("description") or _describe_form(form_type) or "—" - url = item.get("link") or item.get("finalLink") or item.get("url") or "" - rows.append({ - "Date": date, - "Form": form_type, - "Description": description, - "Link": url, - }) - - df = pd.DataFrame(rows) - - # Render as clickable table — Streamlit doesn't natively support link columns, - # so we render each row as a compact card for the most recent 30 entries. - for _, row in df.head(30).iterrows(): - form = row["Form"] + for item in filtered[:40]: + form = str(item.get("type", "—")).strip() + date = str(item.get("date", ""))[:10] + title = item.get("title") or _FORM_DESCRIPTIONS.get(form, "") + edgar_url = item.get("edgarUrl", "") + exhibits = item.get("exhibits") or {} + color = _FORM_COLORS.get(form, "rgba(255,255,255,0.05)") - date = row["Date"] - desc = row["Description"] - link = row["Link"] with st.container(): left, right = st.columns([5, 1]) with left: st.markdown( f"
" - f"{form}  ·  {date}
" - f"{desc}" + f"{form}  ·  " + f"{date}
" + f"{title}" f"
", unsafe_allow_html=True, ) with right: - if link: + # Prefer the actual filing doc over the Yahoo index page + doc_url = exhibits.get(form) or edgar_url + if doc_url: st.markdown( - f"
🔗 View
", + f"
🔗 View
", unsafe_allow_html=True, ) -- cgit v1.3-2-g0d8e