1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
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,
)
|