QuantumLearner commited on
Commit
ed18b18
·
verified ·
1 Parent(s): 62c1956

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +187 -0
app.py ADDED
@@ -0,0 +1,187 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import requests
3
+ import pandas as pd
4
+ import altair as alt
5
+ import datetime
6
+ import re
7
+
8
+ st.set_page_config(page_title="Congress Trades", layout="wide")
9
+
10
+ API_KEY = "b431ec171262073909ebf8c0c4afba71"
11
+ SENATE_BASE_URL = "https://financialmodelingprep.com/api/v4/senate-trading-rss-feed"
12
+ HOUSE_BASE_URL = "https://financialmodelingprep.com/api/v4/senate-disclosure-rss-feed"
13
+
14
+ def fetch_data(base_url, pages=5):
15
+ data = []
16
+ for page in range(pages):
17
+ url = f"{base_url}?page={page}&apikey={API_KEY}"
18
+ r = requests.get(url)
19
+ if r.status_code == 200:
20
+ data.extend(r.json())
21
+ return data
22
+
23
+ def parse_amount_range(amount_str):
24
+ if not isinstance(amount_str, str):
25
+ return None
26
+ clean_str = amount_str.replace("$", "").replace(",", "")
27
+ if " - " in clean_str:
28
+ low, high = clean_str.split(" - ")
29
+ try:
30
+ return (int(low) + int(high)) / 2
31
+ except ValueError:
32
+ return None
33
+ match = re.match(r"\d+", clean_str)
34
+ return float(match.group()) if match else None
35
+
36
+ def load_data(base_url):
37
+ raw_data = fetch_data(base_url, pages=5)
38
+ if not raw_data:
39
+ return pd.DataFrame()
40
+ df = pd.DataFrame(raw_data)
41
+ if "transactionDate" in df.columns:
42
+ df["transactionDate"] = pd.to_datetime(df["transactionDate"], errors="coerce")
43
+ df.sort_values(by="transactionDate", ascending=False, inplace=True)
44
+ return df
45
+
46
+ st.sidebar.title("Filters")
47
+ start_date = st.sidebar.date_input("Start transaction date", value=datetime.date(2025, 1, 1))
48
+ top_n = st.sidebar.slider("Top N stocks", min_value=1, max_value=20, value=5)
49
+ run_button = st.sidebar.button("Run")
50
+
51
+ st.title("Congress Trades Dashboard")
52
+ st.write("Analyze the latest trades reported by members of Congress.")
53
+
54
+ if run_button:
55
+ senate_data = load_data(SENATE_BASE_URL)
56
+ house_data = load_data(HOUSE_BASE_URL)
57
+
58
+ if not senate_data.empty:
59
+ senate_data = senate_data[senate_data["transactionDate"] >= pd.to_datetime(start_date)]
60
+ if not house_data.empty:
61
+ house_data = house_data[house_data["transactionDate"] >= pd.to_datetime(start_date)]
62
+
63
+ # Prepare Senate
64
+ senate_chart_data = pd.DataFrame()
65
+ if not senate_data.empty:
66
+ senate_chart_data = pd.DataFrame({
67
+ "ticker": senate_data["symbol"],
68
+ "rawType": senate_data["type"].str.lower(),
69
+ "amount": senate_data["amount"].apply(parse_amount_range),
70
+ "chamber": "Senate"
71
+ })
72
+
73
+ # Prepare House
74
+ house_chart_data = pd.DataFrame()
75
+ if not house_data.empty:
76
+ house_chart_data = pd.DataFrame({
77
+ "ticker": house_data["ticker"],
78
+ "rawType": house_data["type"].str.lower(),
79
+ "amount": house_data["amount"].apply(parse_amount_range),
80
+ "chamber": "House"
81
+ })
82
+
83
+ combined_data = pd.concat([senate_chart_data, house_chart_data], ignore_index=True)
84
+ combined_data.dropna(subset=["amount", "ticker"], inplace=True)
85
+ combined_data = combined_data[combined_data["amount"] > 0]
86
+
87
+ # Convert types to "purchase" or "sale"
88
+ def standardize_trade_type(t):
89
+ if "sale" in t or "sold" in t or "sell" in t:
90
+ return "sale"
91
+ return "purchase"
92
+
93
+ combined_data["tradeType"] = combined_data["rawType"].apply(standardize_trade_type)
94
+
95
+ combined_data["count"] = 1
96
+
97
+ # Get top N by sum
98
+ sum_per_ticker = (
99
+ combined_data
100
+ .groupby("ticker", as_index=False)["amount"]
101
+ .sum()
102
+ .sort_values("amount", ascending=False)
103
+ .head(top_n)
104
+ )
105
+ top_tickers = sum_per_ticker["ticker"].unique()
106
+ filtered_data = combined_data[combined_data["ticker"].isin(top_tickers)]
107
+
108
+ if filtered_data.empty:
109
+ st.write("No data available for the selected filters.")
110
+ else:
111
+ chart_data = (
112
+ filtered_data
113
+ .groupby(["ticker", "chamber", "tradeType"], as_index=False)
114
+ .agg({"amount": "sum", "count": "sum"})
115
+ )
116
+
117
+ base = alt.Chart(chart_data).encode(
118
+ x=alt.X("ticker:N", axis=alt.Axis(labelAngle=-45)),
119
+ xOffset="chamber:N",
120
+ y=alt.Y("amount:Q", title="Total Amount", stack="zero"),
121
+ color=alt.Color("tradeType:N", scale=alt.Scale(domain=["purchase", "sale"], range=["green", "red"]))
122
+ )
123
+ bars = base.mark_bar()
124
+ text = base.mark_text(dy=-5, color="black").encode(text=alt.Text("count:Q"))
125
+ chart = alt.layer(bars, text).properties(width=40 * len(top_tickers), height=400)
126
+
127
+ st.altair_chart(chart, use_container_width=True)
128
+
129
+ # Reorder columns for Senate
130
+ if not senate_data.empty:
131
+ # The order you specified:
132
+ # 1) name
133
+ # 2) disclosure/received date
134
+ # 3) symbol
135
+ # 4) purchase/sale
136
+ # 5) amount
137
+ # 6) assetDescription
138
+ # We'll map "office" -> name, "dateRecieved" -> date, "symbol" -> symbol,
139
+ # "type" -> purchase/sale, "amount" -> amount, "assetDescription" -> assetDescription
140
+ # Then we append remaining columns
141
+ desired_order_senate = [
142
+ "office", # name
143
+ "dateRecieved",
144
+ "symbol", # symbol
145
+ "type", # purchase/sale
146
+ "amount", # amount
147
+ "assetDescription"
148
+ ]
149
+ # Create an ordered list of columns that exist
150
+ existing_senate_cols = [c for c in desired_order_senate if c in senate_data.columns]
151
+ # Append the rest that we didn't list
152
+ remaining_senate_cols = [c for c in senate_data.columns if c not in existing_senate_cols]
153
+ reordered_senate_cols = existing_senate_cols + remaining_senate_cols
154
+ senate_data = senate_data[reordered_senate_cols]
155
+
156
+ # Reorder columns for House
157
+ if not house_data.empty:
158
+ desired_order_house = [
159
+ "representative", # name
160
+ "disclosureDate",
161
+ "ticker", # symbol
162
+ "type", # purchase/sale
163
+ "amount", # amount
164
+ "assetDescription"
165
+ ]
166
+ existing_house_cols = [c for c in desired_order_house if c in house_data.columns]
167
+ remaining_house_cols = [c for c in house_data.columns if c not in existing_house_cols]
168
+ reordered_house_cols = existing_house_cols + remaining_house_cols
169
+ house_data = house_data[reordered_house_cols]
170
+
171
+ st.subheader("Senate Data")
172
+ st.dataframe(senate_data, use_container_width=True)
173
+
174
+ st.subheader("House Data")
175
+ st.dataframe(house_data, use_container_width=True)
176
+
177
+ else:
178
+ st.write("Set filters and press Run to load data.")
179
+
180
+
181
+ hide_streamlit_style = """
182
+ <style>
183
+ #MainMenu {visibility: hidden;}
184
+ footer {visibility: hidden;}
185
+ </style>
186
+ """
187
+ st.markdown(hide_streamlit_style, unsafe_allow_html=True)