diff options
| author | Openclaw <openclaw@mail.tylerhoang.xyz> | 2026-03-29 13:21:39 -0700 |
|---|---|---|
| committer | Openclaw <openclaw@mail.tylerhoang.xyz> | 2026-03-29 13:21:39 -0700 |
| commit | 4fdcb4ce0f00bc8f62d50ba5d352dd2fe01cd7e7 (patch) | |
| tree | 35dc4234751521d5a89a124f53eeaa827813be07 /components/filings.py | |
| parent | fc55820f5128f97e231de5388e59912e4a675782 (diff) | |
Add historical ratios, forward estimates, insider transactions, SEC filings
- services/fmp_service.py: add get_historical_ratios, get_historical_key_metrics,
get_analyst_estimates, get_insider_transactions, get_sec_filings
- components/valuation.py: add Historical Ratios and Forward Estimates subtabs
- components/insiders.py: new — insider buy/sell summary, monthly chart, detail table
- components/filings.py: new — SEC filings with type filter and direct links
- app.py: wire in Insiders and Filings top-level tabs
Diffstat (limited to 'components/filings.py')
| -rw-r--r-- | components/filings.py | 115 |
1 files changed, 115 insertions, 0 deletions
diff --git a/components/filings.py b/components/filings.py new file mode 100644 index 0000000..d366a2b --- /dev/null +++ b/components/filings.py @@ -0,0 +1,115 @@ +"""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 + + +_FORM_DESCRIPTIONS = { + "10-K": "Annual report", + "10-Q": "Quarterly report", + "8-K": "Material event disclosure", + "DEF 14A": "Proxy statement", + "S-1": "IPO registration", + "S-3": "Securities registration", + "4": "Insider ownership change", + "SC 13G": "Beneficial ownership (passive)", + "SC 13D": "Beneficial ownership (active)", +} + +_FORM_COLORS = { + "10-K": "rgba(79,142,247,0.15)", + "10-Q": "rgba(130,224,170,0.15)", + "8-K": "rgba(247,162,79,0.15)", +} + + +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) + + if not raw: + st.info("No SEC filing data available. Requires FMP API key.") + 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")] + + filter_col, _ = st.columns([1, 3]) + with filter_col: + selected_type = st.selectbox("Form type", options=filter_options, index=0, key="filings_filter") + + # Summary counts + counts = {} + for r in raw: + ft = str(r.get("type") or r.get("form") or "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]) + 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 + ] + + 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"] + 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"<div style='background:{color};padding:6px 10px;border-radius:4px;margin-bottom:2px'>" + f"<strong>{form}</strong> · <span style='color:#9aa0b0;font-size:0.82rem'>{date}</span><br>" + f"<span style='font-size:0.85rem'>{desc}</span>" + f"</div>", + unsafe_allow_html=True, + ) + with right: + if link: + st.markdown( + f"<div style='padding-top:8px'><a href='{link}' target='_blank'>🔗 View</a></div>", + unsafe_allow_html=True, + ) |
