QuantumLearner commited on
Commit
2ac3641
·
verified ·
1 Parent(s): b2e9cdc

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +1410 -0
app.py ADDED
@@ -0,0 +1,1410 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import requests
3
+ import pandas as pd
4
+ import plotly.express as px
5
+ import plotly.graph_objects as go
6
+ from datetime import datetime
7
+
8
+ # Set page configuration
9
+ st.set_page_config(page_title="Institutional Investor Portfolios", layout="wide")
10
+
11
+ # Global API key
12
+ API_KEY = "b431ec171262073909ebf8c0c4afba71"
13
+
14
+ # Initialize Session State for Portfolio Allocation
15
+ if 'portfolio_allocation_data' not in st.session_state:
16
+ st.session_state.portfolio_allocation_data = None
17
+ if 'portfolio_allocation_params' not in st.session_state:
18
+ st.session_state.portfolio_allocation_params = {}
19
+
20
+ # Initialize Session State for Investor Performance
21
+ if 'investor_performance_data' not in st.session_state:
22
+ st.session_state.investor_performance_data = None
23
+ if 'investor_performance_params' not in st.session_state:
24
+ st.session_state.investor_performance_params = {}
25
+
26
+ # Initialize Session State for Symbol Ownership
27
+ if 'symbol_ownership_data' not in st.session_state:
28
+ st.session_state.symbol_ownership_data = None
29
+ if 'symbol_ownership_params' not in st.session_state:
30
+ st.session_state.symbol_ownership_params = {}
31
+
32
+ # Function for Page 1: CIK List
33
+ def page1():
34
+ st.title("CIK and Name Combinations")
35
+ st.markdown("""
36
+ **Description:**
37
+ Fetch and display the list of Central Index Key (CIK). CIK is a unique identifier assigned by the SEC to entities that file disclosures.
38
+ """)
39
+
40
+ with st.spinner("Fetching CIK and Name combinations..."):
41
+ cik_name_df = fetch_cik_name_combinations()
42
+ if not cik_name_df.empty:
43
+ st.success("Data retrieved successfully!")
44
+ st.dataframe(cik_name_df, use_container_width=True, height=800)
45
+
46
+ # Provide download option
47
+ csv = cik_name_df.to_csv(index=False).encode('utf-8')
48
+ st.download_button(
49
+ label="Download Data as CSV",
50
+ data=csv,
51
+ file_name='cik_name_combinations.csv',
52
+ mime='text/csv',
53
+ )
54
+ else:
55
+ st.error("No data retrieved.")
56
+
57
+ # Function to fetch CIK and name combinations
58
+ @st.cache_data
59
+ def fetch_cik_name_combinations():
60
+ """
61
+ Fetches a list of CIK and name combinations from the API.
62
+
63
+ Returns:
64
+ pd.DataFrame: A DataFrame containing CIK and name pairs.
65
+ """
66
+ url = f"https://financialmodelingprep.com/api/v4/institutional-ownership/list?apikey={API_KEY}"
67
+ try:
68
+ response = requests.get(url)
69
+ if response.status_code == 200:
70
+ data = response.json()
71
+ df = pd.DataFrame(data)
72
+ if not df.empty and 'cik' in df.columns and 'name' in df.columns:
73
+ df = df[['cik', 'name']]
74
+ return df
75
+ else:
76
+ return pd.DataFrame()
77
+ else:
78
+ st.error(f"Error fetching data: {response.status_code}, {response.text}")
79
+ return pd.DataFrame()
80
+ except Exception as e:
81
+ st.error(f"An exception occurred: {e}")
82
+ return pd.DataFrame()
83
+
84
+ # Function for Page 2: Portfolio Allocation
85
+ def page2():
86
+ st.title("Portfolio Allocation Over Time")
87
+ st.markdown("""
88
+ **Description:**
89
+ Fetch and visualize portfolio allocation data over time for a specific CIK. Enter the CIK and specify a start date to analyze the portfolio's composition.
90
+ """)
91
+
92
+ # Sidebar Inputs within an Expander
93
+ with st.sidebar.expander("Portfolio Allocation Inputs", expanded=True):
94
+ cik_input = st.text_input("Enter CIK", value="0001067983", help="Enter the Central Index Key (CIK) of the institutional investor.")
95
+ start_date = st.date_input("Select Start Date", value=datetime(2021, 9, 30), help="Choose the start date for fetching portfolio allocation data.")
96
+ top_n = st.number_input("Top N Groups", min_value=1, max_value=100, value=10, step=1, help="Select the number of top groups to display in trend charts.")
97
+
98
+ run_button = st.sidebar.button("Run")
99
+
100
+ if run_button or (st.session_state.portfolio_allocation_params.get('cik') == cik_input and
101
+ st.session_state.portfolio_allocation_params.get('start_date') == start_date.strftime("%Y-%m-%d") and
102
+ st.session_state.portfolio_allocation_params.get('top_n') == top_n):
103
+ # Update Session State with current parameters
104
+ st.session_state.portfolio_allocation_params = {
105
+ 'cik': cik_input,
106
+ 'start_date': start_date.strftime("%Y-%m-%d"),
107
+ 'top_n': top_n
108
+ }
109
+
110
+ with st.spinner("Fetching and processing portfolio allocation data..."):
111
+ allocation_data = fetch_data_over_time(cik_input, start_date.strftime("%Y-%m-%d"))
112
+ if not allocation_data.empty:
113
+ st.session_state.portfolio_allocation_data = allocation_data
114
+ st.success("Data retrieved successfully!")
115
+
116
+ # Latest date data for bar charts
117
+ latest_date = allocation_data["date"].max()
118
+ latest_data = allocation_data[allocation_data["date"] == latest_date]
119
+
120
+ # Plot bar charts
121
+ st.subheader(f"Portfolio Allocation by Ticker on {latest_date}")
122
+ fig1 = plot_bar_chart(latest_data, "symbol", "Weight (%)", "Market Value", height=500)
123
+ st.plotly_chart(fig1, use_container_width=True)
124
+
125
+ st.subheader(f"Portfolio Allocation by Industry on {latest_date}")
126
+ fig2 = plot_bar_chart(latest_data, "industryTitle", "Weight (%)", "Market Value", height=700)
127
+ st.plotly_chart(fig2, use_container_width=True)
128
+
129
+ # Time-series plots for trends
130
+ st.subheader("Portfolio Allocation Trends by Symbol")
131
+ st.markdown("""
132
+ **Explanation:**
133
+ This chart shows the trends in portfolio allocation by individual symbols over time. The top N symbols by weight are displayed to highlight the most significant contributors to the portfolio.
134
+ """)
135
+ fig3 = plot_allocation_trends(allocation_data, "symbol", st.session_state.portfolio_allocation_params.get('top_n'))
136
+ st.plotly_chart(fig3, use_container_width=True, height=600)
137
+
138
+ st.subheader("Portfolio Allocation Trends by Industry")
139
+ st.markdown("""
140
+ **Explanation:**
141
+ This chart illustrates the trends in portfolio allocation across different industries over time. The top N industries by weight are displayed to emphasize the major sectors in the portfolio.
142
+ """)
143
+ fig4 = plot_allocation_trends(allocation_data, "industryTitle", st.session_state.portfolio_allocation_params.get('top_n'))
144
+ st.plotly_chart(fig4, use_container_width=True, height=600)
145
+
146
+ # Transpose data at the bottom
147
+ st.markdown("---")
148
+ st.subheader("Transposed Ticker Data")
149
+ st.markdown("""
150
+ **Explanation:**
151
+ This table presents the portfolio allocation weights and market values for each ticker across different dates. It provides a detailed view of how each stock's weight and market value have evolved over time.
152
+ """)
153
+ transposed_ticker_data = transpose_data(allocation_data, "symbol")
154
+ st.dataframe(transposed_ticker_data, use_container_width=True)
155
+
156
+ st.subheader("Transposed Industry Data")
157
+ st.markdown("""
158
+ **Explanation:**
159
+ This table displays the portfolio allocation weights and market values for each industry across different dates. It offers insights into the sector-wise distribution and changes in the portfolio.
160
+ """)
161
+ transposed_industry_data = transpose_data(allocation_data, "industryTitle")
162
+ st.dataframe(transposed_industry_data, use_container_width=True)
163
+ else:
164
+ st.error("No data found for the specified CIK and date range.")
165
+
166
+ # If data exists in session state and parameters match, display it without rerunning
167
+ elif st.session_state.portfolio_allocation_data and st.session_state.portfolio_allocation_params.get('cik') == cik_input and \
168
+ st.session_state.portfolio_allocation_params.get('start_date') == start_date.strftime("%Y-%m-%d") and \
169
+ st.session_state.portfolio_allocation_params.get('top_n') == top_n:
170
+
171
+ allocation_data = st.session_state.portfolio_allocation_data
172
+
173
+ st.success("Displaying previously retrieved data.")
174
+
175
+ # Latest date data for bar charts
176
+ latest_date = allocation_data["date"].max()
177
+ latest_data = allocation_data[allocation_data["date"] == latest_date]
178
+
179
+ # Plot bar charts
180
+ st.subheader(f"Portfolio Allocation by Ticker on {latest_date}")
181
+ fig1 = plot_bar_chart(latest_data, "symbol", "Weight (%)", "Market Value", height=500)
182
+ st.plotly_chart(fig1, use_container_width=True)
183
+
184
+ st.subheader(f"Portfolio Allocation by Industry on {latest_date}")
185
+ fig2 = plot_bar_chart(latest_data, "industryTitle", "Weight (%)", "Market Value", height=700)
186
+ st.plotly_chart(fig2, use_container_width=True)
187
+
188
+ # Time-series plots for trends
189
+ st.subheader("Portfolio Allocation Trends by Symbol")
190
+ st.markdown("""
191
+ **Explanation:**
192
+ This chart shows the trends in portfolio allocation by individual symbols over time. The top N symbols by weight are displayed to highlight the most significant contributors to the portfolio.
193
+ """)
194
+ fig3 = plot_allocation_trends(allocation_data, "symbol", top_n)
195
+ st.plotly_chart(fig3, use_container_width=True, height=600)
196
+
197
+ st.subheader("Portfolio Allocation Trends by Industry")
198
+ st.markdown("""
199
+ **Explanation:**
200
+ This chart illustrates the trends in portfolio allocation across different industries over time. The top N industries by weight are displayed to emphasize the major sectors in the portfolio.
201
+ """)
202
+ fig4 = plot_allocation_trends(allocation_data, "industryTitle", top_n)
203
+ st.plotly_chart(fig4, use_container_width=True, height=600)
204
+
205
+ # Transpose data at the bottom
206
+ st.markdown("---")
207
+ st.subheader("Transposed Ticker Data")
208
+ st.markdown("""
209
+ **Explanation:**
210
+ This table presents the portfolio allocation weights and market values for each ticker across different dates. It provides a detailed view of how each stock's weight and market value have evolved over time.
211
+ """)
212
+ transposed_ticker_data = transpose_data(allocation_data, "symbol")
213
+ st.dataframe(transposed_ticker_data, use_container_width=True)
214
+
215
+ st.subheader("Transposed Industry Data")
216
+ st.markdown("""
217
+ **Explanation:**
218
+ This table displays the portfolio allocation weights and market values for each industry across different dates. It offers insights into the sector-wise distribution and changes in the portfolio.
219
+ """)
220
+ transposed_industry_data = transpose_data(allocation_data, "industryTitle")
221
+ st.dataframe(transposed_industry_data, use_container_width=True)
222
+
223
+ # Functions used in Page 2
224
+ @st.cache_data
225
+ def fetch_dates(cik):
226
+ """
227
+ Fetches all available quarter-end dates for a specific CIK.
228
+
229
+ Args:
230
+ cik (str): Central Index Key of the institutional investor.
231
+
232
+ Returns:
233
+ list: A list of available quarter-end dates in descending order.
234
+ """
235
+ endpoint = f"https://financialmodelingprep.com/api/v4/institutional-ownership/portfolio-date"
236
+ params = {"cik": cik, "apikey": API_KEY}
237
+ try:
238
+ response = requests.get(endpoint, params=params)
239
+ if response.status_code == 200:
240
+ dates = sorted([item["date"] for item in response.json()], reverse=True)
241
+ return dates
242
+ else:
243
+ st.error(f"Error fetching dates: {response.status_code}, {response.text}")
244
+ return []
245
+ except Exception as e:
246
+ st.error(f"An exception occurred: {e}")
247
+ return []
248
+
249
+ @st.cache_data
250
+ def fetch_portfolio_allocation(cik, date, page=0):
251
+ """
252
+ Fetches portfolio allocation for a specific CIK and date from the API.
253
+
254
+ Args:
255
+ cik (str): Central Index Key of the institutional investor.
256
+ date (str): Quarter-end date in YYYY-MM-DD format.
257
+ page (int): Page number for large datasets (default is 0).
258
+
259
+ Returns:
260
+ pd.DataFrame: Processed DataFrame containing portfolio allocation.
261
+ """
262
+ endpoint = f"https://financialmodelingprep.com/api/v4/institutional-ownership/portfolio-holdings"
263
+ params = {
264
+ "cik": cik,
265
+ "date": date,
266
+ "page": page,
267
+ "apikey": API_KEY
268
+ }
269
+ try:
270
+ response = requests.get(endpoint, params=params)
271
+ if response.status_code == 200:
272
+ data = response.json()
273
+ df = pd.DataFrame(data)
274
+ if not df.empty and all(col in df.columns for col in ["symbol", "industryTitle", "weight", "marketValue"]):
275
+ df = df[["symbol", "industryTitle", "weight", "marketValue"]]
276
+ df["weight"] = df["weight"].astype(float)
277
+ df["marketValue"] = df["marketValue"].astype(float)
278
+ df["date"] = date
279
+ return df
280
+ else:
281
+ return pd.DataFrame()
282
+ else:
283
+ st.error(f"Error fetching allocation: {response.status_code}, {response.text}")
284
+ return pd.DataFrame()
285
+ except Exception as e:
286
+ st.error(f"An exception occurred: {e}")
287
+ return pd.DataFrame()
288
+
289
+ def fetch_data_over_time(cik, start_date):
290
+ """
291
+ Fetches portfolio allocation data over time starting from the specified date.
292
+
293
+ Args:
294
+ cik (str): Central Index Key of the institutional investor.
295
+ start_date (str): Start date in YYYY-MM-DD format.
296
+
297
+ Returns:
298
+ pd.DataFrame: Consolidated DataFrame containing portfolio allocation over time.
299
+ """
300
+ available_dates = fetch_dates(cik)
301
+ selected_dates = [date for date in available_dates if date >= start_date]
302
+ all_data = []
303
+
304
+ for date in selected_dates:
305
+ # Removed individual fetching messages
306
+ df = fetch_portfolio_allocation(cik, date)
307
+ if not df.empty:
308
+ all_data.append(df)
309
+
310
+ if all_data:
311
+ return pd.concat(all_data, ignore_index=True)
312
+ else:
313
+ st.error("No data found for the specified time range.")
314
+ return pd.DataFrame()
315
+
316
+ def transpose_data(df, group_by_column):
317
+ """
318
+ Transposes the DataFrame so that dates become columns for weight and market value.
319
+
320
+ Args:
321
+ df (pd.DataFrame): Original DataFrame containing portfolio allocation over time.
322
+ group_by_column (str): Column to group data by (e.g., "symbol" or "industryTitle").
323
+
324
+ Returns:
325
+ pd.DataFrame: Transposed DataFrame.
326
+ """
327
+ pivoted_weight = df.pivot_table(values="weight", index=group_by_column, columns="date", aggfunc="sum", fill_value=0)
328
+ pivoted_market_value = df.pivot_table(values="marketValue", index=group_by_column, columns="date", aggfunc="sum", fill_value=0)
329
+
330
+ # Reorder columns: weight -> market value -> next weight -> next market value
331
+ combined = pd.concat([pivoted_weight, pivoted_market_value], axis=1, keys=["Weight", "Market Value"])
332
+ combined = combined.swaplevel(axis=1).sort_index(axis=1)
333
+ return combined
334
+
335
+ def plot_bar_chart(df, group_by_column, weight_title, market_value_title, height=500):
336
+ """
337
+ Plots a bar chart for portfolio allocation using Plotly.
338
+
339
+ Args:
340
+ df (pd.DataFrame): DataFrame containing portfolio allocation data.
341
+ group_by_column (str): Column to group data by (e.g., "symbol" or "industryTitle").
342
+ weight_title (str): Title for the weight axis.
343
+ market_value_title (str): Title for the market value.
344
+ height (int): Height of the plot in pixels.
345
+
346
+ Returns:
347
+ plotly.graph_objects.Figure: Plotly figure object.
348
+ """
349
+ # Ensure data only corresponds to the latest date and aggregate by the group column
350
+ latest_date = df["date"].max()
351
+ filtered_df = df[df["date"] == latest_date].groupby(group_by_column, as_index=False).sum()
352
+
353
+ # Sort the data by weight in descending order
354
+ filtered_df = filtered_df.sort_values(by="weight", ascending=False)
355
+
356
+ # Format market values for display
357
+ filtered_df["marketValueFormatted"] = filtered_df["marketValue"].apply(format_market_value)
358
+
359
+ # Create bar chart
360
+ fig = go.Figure()
361
+ fig.add_trace(go.Bar(
362
+ x=filtered_df[group_by_column],
363
+ y=filtered_df["weight"],
364
+ text=[f"{w:.1f}%<br>{mv}" for w, mv in zip(filtered_df["weight"], filtered_df["marketValueFormatted"])],
365
+ textposition="outside",
366
+ texttemplate="%{text}",
367
+ marker=dict(color="teal"),
368
+ textfont=dict(size=14), # Makes the labels larger
369
+ cliponaxis=False # Ensures labels aren't clipped
370
+ ))
371
+ fig.update_layout(
372
+ title=f"Portfolio Allocation by {group_by_column.capitalize()} on {latest_date}",
373
+ xaxis_title=group_by_column.capitalize(),
374
+ yaxis_title=weight_title,
375
+ xaxis_tickangle=45, # Make labels vertical
376
+ showlegend=False,
377
+ height=height
378
+ )
379
+ fig.update_traces(
380
+ textfont_size=12, # Increase font size
381
+ cliponaxis=False # Ensure text doesn't get clipped
382
+ )
383
+ return fig
384
+
385
+ def plot_allocation_trends(df, group_by_column, top_n=10):
386
+ """
387
+ Plots trends for portfolio allocation over time using Plotly.
388
+
389
+ Args:
390
+ df (pd.DataFrame): DataFrame containing portfolio allocation data over time.
391
+ group_by_column (str): Column to group data by (e.g., "symbol" or "industryTitle").
392
+ top_n (int): Number of top contributors to show in the chart.
393
+
394
+ Returns:
395
+ plotly.express.Figure: Plotly figure object.
396
+ """
397
+ # Group and aggregate data by group_by_column and date
398
+ aggregated_df = df.groupby([group_by_column, "date"], as_index=False).sum()
399
+
400
+ # Summarize total weight for sorting
401
+ total_weight = aggregated_df.groupby(group_by_column)["weight"].sum().sort_values(ascending=False)
402
+ top_groups = total_weight.head(top_n).index
403
+
404
+ # Filter data to include only the top_n groups
405
+ filtered_df = aggregated_df[aggregated_df[group_by_column].isin(top_groups)]
406
+ filtered_df["marketValueFormatted"] = filtered_df["marketValue"].apply(format_market_value)
407
+
408
+ # Create the Plotly line chart
409
+ fig = px.line(
410
+ filtered_df,
411
+ x="date",
412
+ y="weight",
413
+ color=group_by_column,
414
+ title=f"Portfolio Allocation Trends by {group_by_column.capitalize()}",
415
+ labels={"weight": "Weight (%)", "date": "Date"},
416
+ markers=True,
417
+ hover_data={
418
+ group_by_column: True,
419
+ "weight": ":.2f", # Weight with 2 decimals
420
+ "marketValueFormatted": True # Show market value in hover
421
+ }
422
+ )
423
+ fig.update_layout(
424
+ legend_title=group_by_column.capitalize(),
425
+ xaxis_title="Date",
426
+ yaxis_title="Weight (%)",
427
+ hovermode="closest",
428
+ height=600
429
+ )
430
+ return fig
431
+
432
+ def format_market_value(value):
433
+ """
434
+ Formats a market value into billions, millions, or thousands with appropriate units.
435
+
436
+ Args:
437
+ value (float): Market value in absolute terms.
438
+
439
+ Returns:
440
+ str: Formatted market value as a string with units.
441
+ """
442
+ if value >= 1e9:
443
+ return f"{value / 1e9:.1f}B"
444
+ elif value >= 1e6:
445
+ return f"{value / 1e6:.1f}M"
446
+ elif value >= 1e3:
447
+ return f"{value / 1e3:.1f}K"
448
+ else:
449
+ return f"{value:.1f}"
450
+
451
+ # Function for Page 3: Investor Performance
452
+ def page3():
453
+ st.title("Investor Performance")
454
+ st.markdown("""
455
+ **Description:**
456
+ Fetch and visualize various performance metrics for a specific institutional investor identified by their CIK. Analyze portfolio value, market value changes, performance relative to benchmarks, turnover metrics, and more.
457
+ """)
458
+
459
+ # Sidebar Inputs within an Expander
460
+ with st.sidebar.expander("Investor Performance Inputs", expanded=True):
461
+ cik_input = st.text_input("Enter CIK", value="0001067983", help="Enter the Central Index Key (CIK) of the institutional investor.")
462
+
463
+ run_button = st.sidebar.button("Run")
464
+
465
+ if run_button or (st.session_state.investor_performance_params.get('cik') == cik_input):
466
+ # Update Session State with current parameters
467
+ st.session_state.investor_performance_params = {
468
+ 'cik': cik_input
469
+ }
470
+
471
+ with st.spinner("Fetching and processing investor performance data..."):
472
+ data = fetch_investor_performance(cik_input)
473
+ if not data.empty:
474
+ st.session_state.investor_performance_data = data
475
+ st.success("Data retrieved successfully!")
476
+ st.dataframe(data, use_container_width=True)
477
+
478
+ # Ensure the data is sorted by date
479
+ data["date"] = pd.to_datetime(data["date"])
480
+ data = data.sort_values(by="date")
481
+
482
+ # Plotting
483
+ st.subheader("Portfolio Value and Change in Market Value (%)")
484
+ st.markdown("""
485
+ **Explanation:**
486
+ This chart displays the portfolio's total market value over time alongside the percentage change in market value. It helps in understanding the growth or decline of the portfolio's value.
487
+ """)
488
+ fig1 = plot_portfolio_value_and_change(data)
489
+ st.plotly_chart(fig1, use_container_width=True)
490
+
491
+ st.subheader("Performance and Relative Performance to S&P 500")
492
+ st.markdown("""
493
+ **Explanation:**
494
+ This chart compares the portfolio's performance against the S&P 500 benchmark. It shows how well the portfolio is performing relative to the broader market.
495
+ """)
496
+ fig2 = plot_performance_and_relative(data)
497
+ st.plotly_chart(fig2, use_container_width=True)
498
+
499
+ st.subheader("Portfolio Turnover Metrics")
500
+ st.markdown("""
501
+ **Explanation:**
502
+ This chart illustrates the portfolio's turnover rate, including buy and sell activities. Turnover metrics provide insight into the trading frequency and strategy.
503
+ """)
504
+ fig3 = plot_turnover_metrics(data)
505
+ st.plotly_chart(fig3, use_container_width=True)
506
+
507
+ st.subheader("Cumulative Performance Over Time")
508
+ st.markdown("""
509
+ **Explanation:**
510
+ This chart shows the cumulative performance of the portfolio over different time horizons (1-year, 3-year, 5-year, and since inception). It highlights long-term growth trends.
511
+ """)
512
+ fig4 = plot_cumulative_performance(data)
513
+ st.plotly_chart(fig4, use_container_width=True)
514
+
515
+ st.subheader("Holding Periods")
516
+ st.markdown("""
517
+ **Explanation:**
518
+ This chart depicts the average holding periods for the portfolio and its top holdings. Holding periods indicate the investment duration and strategy stability.
519
+ """)
520
+ fig5 = plot_holding_periods(data)
521
+ st.plotly_chart(fig5, use_container_width=True)
522
+
523
+ st.subheader("Portfolio Activity")
524
+ st.markdown("""
525
+ **Explanation:**
526
+ This chart displays portfolio size changes and the number of securities added or removed. It provides an overview of portfolio expansion or contraction.
527
+ """)
528
+ fig6 = plot_portfolio_activity(data)
529
+ st.plotly_chart(fig6, use_container_width=True)
530
+
531
+ st.subheader("Market Value and Securities Count")
532
+ st.markdown("""
533
+ **Explanation:**
534
+ This chart compares the portfolio's total market value against the number of securities held. It helps in understanding the diversification and valuation aspects of the portfolio.
535
+ """)
536
+ fig7 = plot_market_value_and_securities(data)
537
+ st.plotly_chart(fig7, use_container_width=True)
538
+ else:
539
+ st.error("No data found for the specified CIK.")
540
+
541
+ # If data exists in session state and parameters match, display it without rerunning
542
+ elif st.session_state.investor_performance_data and st.session_state.investor_performance_params.get('cik') == cik_input:
543
+ data = st.session_state.investor_performance_data
544
+
545
+ st.success("Displaying previously retrieved data.")
546
+ st.dataframe(data, use_container_width=True)
547
+
548
+ # Ensure the data is sorted by date
549
+ data["date"] = pd.to_datetime(data["date"])
550
+ data = data.sort_values(by="date")
551
+
552
+ # Plotting
553
+ st.subheader("Portfolio Value and Change in Market Value (%)")
554
+ st.markdown("""
555
+ **Explanation:**
556
+ This chart displays the portfolio's total market value over time alongside the percentage change in market value. It helps in understanding the growth or decline of the portfolio's value.
557
+ """)
558
+ fig1 = plot_portfolio_value_and_change(data)
559
+ st.plotly_chart(fig1, use_container_width=True)
560
+
561
+ st.subheader("Performance and Relative Performance to S&P 500")
562
+ st.markdown("""
563
+ **Explanation:**
564
+ This chart compares the portfolio's performance against the S&P 500 benchmark. It shows how well the portfolio is performing relative to the broader market.
565
+ """)
566
+ fig2 = plot_performance_and_relative(data)
567
+ st.plotly_chart(fig2, use_container_width=True)
568
+
569
+ st.subheader("Portfolio Turnover Metrics")
570
+ st.markdown("""
571
+ **Explanation:**
572
+ This chart illustrates the portfolio's turnover rate, including buy and sell activities. Turnover metrics provide insight into the trading frequency and strategy.
573
+ """)
574
+ fig3 = plot_turnover_metrics(data)
575
+ st.plotly_chart(fig3, use_container_width=True)
576
+
577
+ st.subheader("Cumulative Performance Over Time")
578
+ st.markdown("""
579
+ **Explanation:**
580
+ This chart shows the cumulative performance of the portfolio over different time horizons (1-year, 3-year, 5-year, and since inception). It highlights long-term growth trends.
581
+ """)
582
+ fig4 = plot_cumulative_performance(data)
583
+ st.plotly_chart(fig4, use_container_width=True)
584
+
585
+ st.subheader("Holding Periods")
586
+ st.markdown("""
587
+ **Explanation:**
588
+ This chart depicts the average holding periods for the portfolio and its top holdings. Holding periods indicate the investment duration and strategy stability.
589
+ """)
590
+ fig5 = plot_holding_periods(data)
591
+ st.plotly_chart(fig5, use_container_width=True)
592
+
593
+ st.subheader("Portfolio Activity")
594
+ st.markdown("""
595
+ **Explanation:**
596
+ This chart displays portfolio size changes and the number of securities added or removed. It provides an overview of portfolio expansion or contraction.
597
+ """)
598
+ fig6 = plot_portfolio_activity(data)
599
+ st.plotly_chart(fig6, use_container_width=True)
600
+
601
+ st.subheader("Market Value and Securities Count")
602
+ st.markdown("""
603
+ **Explanation:**
604
+ This chart compares the portfolio's total market value against the number of securities held. It helps in understanding the diversification and valuation aspects of the portfolio.
605
+ """)
606
+ fig7 = plot_market_value_and_securities(data)
607
+ st.plotly_chart(fig7, use_container_width=True)
608
+
609
+ # Functions used in Page 3
610
+ @st.cache_data
611
+ def fetch_investor_performance(cik):
612
+ """
613
+ Fetches investor performance data from FMP API.
614
+
615
+ Args:
616
+ cik (str): Central Index Key of the institutional investor.
617
+
618
+ Returns:
619
+ pd.DataFrame: DataFrame containing investor performance data.
620
+ """
621
+ url = f"https://financialmodelingprep.com/api/v4/institutional-ownership/portfolio-holdings-summary"
622
+ params = {
623
+ "cik": cik,
624
+ "page": 0,
625
+ "apikey": API_KEY
626
+ }
627
+ try:
628
+ response = requests.get(url, params=params)
629
+ if response.status_code == 200:
630
+ data = response.json()
631
+ df = pd.DataFrame(data)
632
+ return df
633
+ else:
634
+ st.error(f"Failed to fetch data: {response.status_code}")
635
+ return pd.DataFrame()
636
+ except Exception as e:
637
+ st.error(f"An exception occurred: {e}")
638
+ return pd.DataFrame()
639
+
640
+ def plot_portfolio_value_and_change(df):
641
+ """
642
+ Plots portfolio value and change in market value percentage.
643
+
644
+ Args:
645
+ df (pd.DataFrame): DataFrame containing investor performance data.
646
+
647
+ Returns:
648
+ plotly.graph_objects.Figure: Plotly figure object.
649
+ """
650
+ fig = go.Figure()
651
+
652
+ # Portfolio Value Line
653
+ fig.add_trace(go.Scatter(
654
+ x=df["date"],
655
+ y=df["marketValue"],
656
+ mode='lines+markers',
657
+ name="Portfolio Value",
658
+ yaxis="y1"
659
+ ))
660
+
661
+ # Change in Market Value Percentage Line
662
+ fig.add_trace(go.Scatter(
663
+ x=df["date"],
664
+ y=df["changeInMarketValuePercentage"],
665
+ mode='lines+markers',
666
+ name="Change in Market Value (%)",
667
+ yaxis="y2"
668
+ ))
669
+
670
+ fig.update_layout(
671
+ title="Portfolio Value and Change in Market Value (%)",
672
+ xaxis_title="Date",
673
+ yaxis=dict(title="Market Value ($)", tickformat="$,.0f"),
674
+ yaxis2=dict(title="Change in Market Value (%)", overlaying="y", side="right", tickformat=".2f"),
675
+ legend=dict(title="Metrics"),
676
+ height=500
677
+ )
678
+ return fig
679
+
680
+ def plot_performance_and_relative(df):
681
+ """
682
+ Plots performance and relative performance to S&P 500.
683
+
684
+ Args:
685
+ df (pd.DataFrame): DataFrame containing investor performance data.
686
+
687
+ Returns:
688
+ plotly.graph_objects.Figure: Plotly figure object.
689
+ """
690
+ fig = go.Figure()
691
+
692
+ # Performance Percentage Line
693
+ fig.add_trace(go.Scatter(
694
+ x=df["date"],
695
+ y=df["performancePercentage"],
696
+ mode='lines+markers',
697
+ name="Performance (%)",
698
+ yaxis="y1"
699
+ ))
700
+
701
+ # Relative Performance Percentage Line
702
+ fig.add_trace(go.Scatter(
703
+ x=df["date"],
704
+ y=df["performanceRelativeToSP500Percentage"],
705
+ mode='lines+markers',
706
+ name="Relative Performance to S&P 500 (%)",
707
+ yaxis="y2"
708
+ ))
709
+
710
+ fig.update_layout(
711
+ title="Performance and Relative Performance to S&P 500",
712
+ xaxis_title="Date",
713
+ yaxis=dict(title="Performance (%)", tickformat=".2f"),
714
+ yaxis2=dict(title="Relative Performance (%)", overlaying="y", side="right", tickformat=".2f"),
715
+ legend=dict(title="Metrics"),
716
+ height=500
717
+ )
718
+ return fig
719
+
720
+ def plot_turnover_metrics(df):
721
+ """
722
+ Plots portfolio turnover metrics.
723
+
724
+ Args:
725
+ df (pd.DataFrame): DataFrame containing investor performance data.
726
+
727
+ Returns:
728
+ plotly.graph_objects.Figure: Plotly figure object.
729
+ """
730
+ fig = go.Figure()
731
+
732
+ # Turnover Line
733
+ fig.add_trace(go.Scatter(
734
+ x=df["date"],
735
+ y=df["turnover"],
736
+ mode='lines+markers',
737
+ name="Turnover (%)",
738
+ yaxis="y1"
739
+ ))
740
+
741
+ # Alternate Turnover Metrics
742
+ fig.add_trace(go.Scatter(
743
+ x=df["date"],
744
+ y=df["turnoverAlternateSell"],
745
+ mode='lines+markers',
746
+ name="Turnover (Sell)",
747
+ yaxis="y2"
748
+ ))
749
+ fig.add_trace(go.Scatter(
750
+ x=df["date"],
751
+ y=df["turnoverAlternateBuy"],
752
+ mode='lines+markers',
753
+ name="Turnover (Buy)",
754
+ yaxis="y2"
755
+ ))
756
+
757
+ fig.update_layout(
758
+ title="Portfolio Turnover Metrics",
759
+ xaxis_title="Date",
760
+ yaxis=dict(title="Turnover (%)", tickformat=".2f"),
761
+ yaxis2=dict(title="Turnover (Buy/Sell)", overlaying="y", side="right", tickformat=".2f"),
762
+ legend=dict(title="Metrics"),
763
+ height=500
764
+ )
765
+ return fig
766
+
767
+ def plot_cumulative_performance(df):
768
+ """
769
+ Plots cumulative performance over time.
770
+
771
+ Args:
772
+ df (pd.DataFrame): DataFrame containing investor performance data.
773
+
774
+ Returns:
775
+ plotly.graph_objects.Figure: Plotly figure object.
776
+ """
777
+ fig = go.Figure()
778
+
779
+ # Cumulative Performance Percentages
780
+ fig.add_trace(go.Scatter(
781
+ x=df["date"],
782
+ y=df["performancePercentage1year"],
783
+ mode='lines+markers',
784
+ name="1-Year Performance (%)",
785
+ yaxis="y1"
786
+ ))
787
+ fig.add_trace(go.Scatter(
788
+ x=df["date"],
789
+ y=df["performancePercentage3year"],
790
+ mode='lines+markers',
791
+ name="3-Year Performance (%)",
792
+ yaxis="y1"
793
+ ))
794
+ fig.add_trace(go.Scatter(
795
+ x=df["date"],
796
+ y=df["performancePercentage5year"],
797
+ mode='lines+markers',
798
+ name="5-Year Performance (%)",
799
+ yaxis="y1"
800
+ ))
801
+ fig.add_trace(go.Scatter(
802
+ x=df["date"],
803
+ y=df["performanceSinceInceptionPercentage"],
804
+ mode='lines+markers',
805
+ name="Since Inception (%)",
806
+ yaxis="y1"
807
+ ))
808
+
809
+ fig.update_layout(
810
+ title="Cumulative Performance Over Time",
811
+ xaxis_title="Date",
812
+ yaxis=dict(title="Performance (%)", tickformat=".2f"),
813
+ legend=dict(title="Metrics"),
814
+ height=500
815
+ )
816
+ return fig
817
+
818
+ def plot_holding_periods(df):
819
+ """
820
+ Plots holding periods.
821
+
822
+ Args:
823
+ df (pd.DataFrame): DataFrame containing investor performance data.
824
+
825
+ Returns:
826
+ plotly.graph_objects.Figure: Plotly figure object.
827
+ """
828
+ fig = go.Figure()
829
+
830
+ # Overall Holding Period
831
+ fig.add_trace(go.Scatter(
832
+ x=df["date"],
833
+ y=df["averageHoldingPeriod"],
834
+ mode='lines+markers',
835
+ name="Average Holding Period",
836
+ yaxis="y1"
837
+ ))
838
+
839
+ # Top Holdings Periods
840
+ fig.add_trace(go.Scatter(
841
+ x=df["date"],
842
+ y=df["averageHoldingPeriodTop10"],
843
+ mode='lines+markers',
844
+ name="Top 10 Holding Period",
845
+ yaxis="y1"
846
+ ))
847
+ fig.add_trace(go.Scatter(
848
+ x=df["date"],
849
+ y=df["averageHoldingPeriodTop20"],
850
+ mode='lines+markers',
851
+ name="Top 20 Holding Period",
852
+ yaxis="y1"
853
+ ))
854
+
855
+ fig.update_layout(
856
+ title="Holding Periods",
857
+ xaxis_title="Date",
858
+ yaxis=dict(title="Holding Period (Quarters)"),
859
+ legend=dict(title="Metrics"),
860
+ height=500
861
+ )
862
+ return fig
863
+
864
+ def plot_portfolio_activity(df):
865
+ """
866
+ Plots portfolio activity.
867
+
868
+ Args:
869
+ df (pd.DataFrame): DataFrame containing investor performance data.
870
+
871
+ Returns:
872
+ plotly.graph_objects.Figure: Plotly figure object.
873
+ """
874
+ fig = go.Figure()
875
+
876
+ # Portfolio Size
877
+ fig.add_trace(go.Scatter(
878
+ x=df["date"],
879
+ y=df["portfolioSize"],
880
+ mode='lines+markers',
881
+ name="Portfolio Size",
882
+ yaxis="y1"
883
+ ))
884
+
885
+ # Securities Added and Removed
886
+ fig.add_trace(go.Scatter(
887
+ x=df["date"],
888
+ y=df["securitiesAdded"],
889
+ mode='lines+markers',
890
+ name="Securities Added",
891
+ yaxis="y2"
892
+ ))
893
+ fig.add_trace(go.Scatter(
894
+ x=df["date"],
895
+ y=df["securitiesRemoved"],
896
+ mode='lines+markers',
897
+ name="Securities Removed",
898
+ yaxis="y2"
899
+ ))
900
+
901
+ fig.update_layout(
902
+ title="Portfolio Activity",
903
+ xaxis_title="Date",
904
+ yaxis=dict(title="Portfolio Size"),
905
+ yaxis2=dict(title="Activity (Count)", overlaying="y", side="right"),
906
+ legend=dict(title="Metrics"),
907
+ height=500
908
+ )
909
+ return fig
910
+
911
+ def plot_market_value_and_securities(df):
912
+ """
913
+ Plots market value and securities count.
914
+
915
+ Args:
916
+ df (pd.DataFrame): DataFrame containing investor performance data.
917
+
918
+ Returns:
919
+ plotly.graph_objects.Figure: Plotly figure object.
920
+ """
921
+ fig = go.Figure()
922
+
923
+ # Portfolio Value Line
924
+ fig.add_trace(go.Scatter(
925
+ x=df["date"],
926
+ y=df["marketValue"],
927
+ mode='lines+markers',
928
+ name="Portfolio Value",
929
+ yaxis="y1"
930
+ ))
931
+
932
+ # Portfolio Size Line
933
+ fig.add_trace(go.Scatter(
934
+ x=df["date"],
935
+ y=df["portfolioSize"],
936
+ mode='lines+markers',
937
+ name="Portfolio Size",
938
+ yaxis="y2"
939
+ ))
940
+
941
+ fig.update_layout(
942
+ title="Market Value and Securities Count",
943
+ xaxis_title="Date",
944
+ yaxis=dict(title="Market Value ($)", tickformat="$,.0f"),
945
+ yaxis2=dict(title="Portfolio Size", overlaying="y", side="right"),
946
+ legend=dict(title="Metrics"),
947
+ height=500
948
+ )
949
+ return fig
950
+
951
+ # Function to fetch symbol ownership (Page 4)
952
+ @st.cache_data
953
+ def fetch_symbol_ownership(symbol):
954
+ """
955
+ Fetches symbol ownership data from FMP API.
956
+
957
+ Args:
958
+ symbol (str): Stock symbol.
959
+
960
+ Returns:
961
+ pd.DataFrame: DataFrame containing symbol ownership data.
962
+ """
963
+ url = f"https://financialmodelingprep.com/api/v4/institutional-ownership/symbol-ownership"
964
+ params = {
965
+ "symbol": symbol,
966
+ "includeCurrentQuarter": "true",
967
+ "apikey": API_KEY
968
+ }
969
+ try:
970
+ response = requests.get(url, params=params)
971
+ if response.status_code == 200:
972
+ data = response.json()
973
+ df = pd.DataFrame(data)
974
+ return df
975
+ else:
976
+ st.error(f"Failed to fetch data: {response.status_code}")
977
+ return pd.DataFrame()
978
+ except Exception as e:
979
+ st.error(f"An exception occurred: {e}")
980
+ return pd.DataFrame()
981
+
982
+ # Function for Page 4: Symbol Ownership
983
+ def page4():
984
+ st.title("Symbol Ownership")
985
+ st.markdown("""
986
+ **Description:**
987
+ Fetch and visualizes ownership data for a specific stock symbol. Analyze investor counts, ownership percentages, portfolio value changes, positions activity, derivative activity.
988
+ """)
989
+
990
+ # Sidebar Inputs within an Expander
991
+ with st.sidebar.expander("Symbol Ownership Inputs", expanded=True):
992
+ symbol = st.text_input("Enter Stock Symbol", value="AAPL", help="Enter the stock symbol you want to analyze, e.g., AAPL for Apple Inc.")
993
+
994
+ run_button = st.sidebar.button("Run")
995
+
996
+ if run_button or (st.session_state.symbol_ownership_params.get('symbol') == symbol):
997
+ # Update Session State with current parameters
998
+ st.session_state.symbol_ownership_params = {
999
+ 'symbol': symbol
1000
+ }
1001
+
1002
+ with st.spinner("Fetching and processing symbol ownership data..."):
1003
+ data = fetch_symbol_ownership(symbol)
1004
+ if not data.empty:
1005
+ st.session_state.symbol_ownership_data = data
1006
+ st.success("Data retrieved successfully!")
1007
+ st.dataframe(data, use_container_width=True)
1008
+
1009
+ # Ensure the data is sorted by date
1010
+ data["date"] = pd.to_datetime(data["date"])
1011
+ data = data.sort_values(by="date")
1012
+
1013
+ # Plotting
1014
+ st.subheader("Investor Count and Ownership Percentage")
1015
+ st.markdown("""
1016
+ **Explanation:**
1017
+ This chart displays the number of investors holding the stock and the percentage of ownership over time. It provides insights into investor interest and ownership trends.
1018
+ """)
1019
+ fig1 = plot_investor_count_and_ownership(data)
1020
+ st.plotly_chart(fig1, use_container_width=True)
1021
+
1022
+ st.subheader("Portfolio Value and Ownership Percentage Change")
1023
+ st.markdown("""
1024
+ **Explanation:**
1025
+ This chart shows the total invested amount and how the ownership percentage has changed over time. It helps in understanding investment growth and shifts in ownership stakes.
1026
+ """)
1027
+ fig2 = plot_portfolio_value_and_change_symbol(data)
1028
+ st.plotly_chart(fig2, use_container_width=True)
1029
+
1030
+ st.subheader("Positions Activity")
1031
+ st.markdown("""
1032
+ **Explanation:**
1033
+ This chart illustrates the activity related to positions, including new and closed positions as well as increases and reductions. It reflects the trading dynamics of the stock.
1034
+ """)
1035
+ fig3 = plot_positions_activity_symbol(data)
1036
+ st.plotly_chart(fig3, use_container_width=True)
1037
+
1038
+ st.subheader("Derivative Activity")
1039
+ st.markdown("""
1040
+ **Explanation:**
1041
+ This chart displays derivative activities such as total calls and puts, along with the put/call ratio. It provides insights into options trading related to the stock.
1042
+ """)
1043
+ fig4 = plot_derivative_activity_symbol(data)
1044
+ st.plotly_chart(fig4, use_container_width=True)
1045
+
1046
+ st.subheader("Changes in Metrics")
1047
+ st.markdown("""
1048
+ **Explanation:**
1049
+ This chart shows the changes in key metrics like the number of 13F shares and total investment. It highlights significant shifts in investment positions.
1050
+ """)
1051
+ fig5 = plot_changes_in_metrics_symbol(data)
1052
+ st.plotly_chart(fig5, use_container_width=True)
1053
+
1054
+ st.subheader("Ownership Percent and Total Invested")
1055
+ st.markdown("""
1056
+ **Explanation:**
1057
+ This chart compares the ownership percentage with the total amount invested in the stock. It helps in assessing the investment intensity relative to ownership stake.
1058
+ """)
1059
+ fig6 = plot_ownership_and_investment_symbol(data)
1060
+ st.plotly_chart(fig6, use_container_width=True)
1061
+ else:
1062
+ st.error("No data found for the specified symbol.")
1063
+
1064
+ # If data exists in session state and parameters match, display it without rerunning
1065
+ elif st.session_state.symbol_ownership_data and st.session_state.symbol_ownership_params.get('symbol') == symbol:
1066
+ data = st.session_state.symbol_ownership_data
1067
+
1068
+ st.success("Displaying previously retrieved data.")
1069
+ st.dataframe(data, use_container_width=True)
1070
+
1071
+ # Ensure the data is sorted by date
1072
+ data["date"] = pd.to_datetime(data["date"])
1073
+ data = data.sort_values(by="date")
1074
+
1075
+ # Plotting
1076
+ st.subheader("Investor Count and Ownership Percentage")
1077
+ st.markdown("""
1078
+ **Explanation:**
1079
+ This chart displays the number of investors holding the stock and the percentage of ownership over time. It provides insights into investor interest and ownership trends.
1080
+ """)
1081
+ fig1 = plot_investor_count_and_ownership(data)
1082
+ st.plotly_chart(fig1, use_container_width=True)
1083
+
1084
+ st.subheader("Portfolio Value and Ownership Percentage Change")
1085
+ st.markdown("""
1086
+ **Explanation:**
1087
+ This chart shows the total invested amount and how the ownership percentage has changed over time. It helps in understanding investment growth and shifts in ownership stakes.
1088
+ """)
1089
+ fig2 = plot_portfolio_value_and_change_symbol(data)
1090
+ st.plotly_chart(fig2, use_container_width=True)
1091
+
1092
+ st.subheader("Positions Activity")
1093
+ st.markdown("""
1094
+ **Explanation:**
1095
+ This chart illustrates the activity related to positions, including new and closed positions as well as increases and reductions. It reflects the trading dynamics of the stock.
1096
+ """)
1097
+ fig3 = plot_positions_activity_symbol(data)
1098
+ st.plotly_chart(fig3, use_container_width=True)
1099
+
1100
+ st.subheader("Derivative Activity")
1101
+ st.markdown("""
1102
+ **Explanation:**
1103
+ This chart displays derivative activities such as total calls and puts, along with the put/call ratio. It provides insights into options trading related to the stock.
1104
+ """)
1105
+ fig4 = plot_derivative_activity_symbol(data)
1106
+ st.plotly_chart(fig4, use_container_width=True)
1107
+
1108
+ st.subheader("Changes in Metrics")
1109
+ st.markdown("""
1110
+ **Explanation:**
1111
+ This chart shows the changes in key metrics like the number of 13F shares and total investment. It highlights significant shifts in investment positions.
1112
+ """)
1113
+ fig5 = plot_changes_in_metrics_symbol(data)
1114
+ st.plotly_chart(fig5, use_container_width=True)
1115
+
1116
+ st.subheader("Ownership Percent and Total Invested")
1117
+ st.markdown("""
1118
+ **Explanation:**
1119
+ This chart compares the ownership percentage with the total amount invested in the stock. It helps in assessing the investment intensity relative to ownership stake.
1120
+ """)
1121
+ fig6 = plot_ownership_and_investment_symbol(data)
1122
+ st.plotly_chart(fig6, use_container_width=True)
1123
+
1124
+ # Functions used in Page 4
1125
+ def plot_investor_count_and_ownership(df):
1126
+ """
1127
+ Plots investor count and ownership percentage.
1128
+
1129
+ Args:
1130
+ df (pd.DataFrame): DataFrame containing symbol ownership data.
1131
+
1132
+ Returns:
1133
+ plotly.graph_objects.Figure: Plotly figure object.
1134
+ """
1135
+ fig = go.Figure()
1136
+
1137
+ # Investors Holding Line
1138
+ fig.add_trace(go.Scatter(
1139
+ x=df["date"],
1140
+ y=df["investorsHolding"],
1141
+ mode='lines+markers',
1142
+ name="Investors Holding",
1143
+ yaxis="y1"
1144
+ ))
1145
+
1146
+ # Ownership Percentage Line
1147
+ fig.add_trace(go.Scatter(
1148
+ x=df["date"],
1149
+ y=df["ownershipPercent"],
1150
+ mode='lines+markers',
1151
+ name="Ownership (%)",
1152
+ yaxis="y2"
1153
+ ))
1154
+
1155
+ fig.update_layout(
1156
+ title="Investor Count and Ownership Percentage",
1157
+ xaxis_title="Date",
1158
+ yaxis=dict(title="Investors Holding"),
1159
+ yaxis2=dict(title="Ownership (%)", overlaying="y", side="right", tickformat=".2f"),
1160
+ legend=dict(title="Metrics"),
1161
+ height=500
1162
+ )
1163
+ return fig
1164
+
1165
+ def plot_portfolio_value_and_change_symbol(df):
1166
+ """
1167
+ Plots portfolio value and ownership percentage change.
1168
+
1169
+ Args:
1170
+ df (pd.DataFrame): DataFrame containing symbol ownership data.
1171
+
1172
+ Returns:
1173
+ plotly.graph_objects.Figure: Plotly figure object.
1174
+ """
1175
+ fig = go.Figure()
1176
+
1177
+ # Total Invested Line
1178
+ fig.add_trace(go.Scatter(
1179
+ x=df["date"],
1180
+ y=df["totalInvested"],
1181
+ mode='lines+markers',
1182
+ name="Total Invested",
1183
+ yaxis="y1"
1184
+ ))
1185
+
1186
+ # Ownership Percentage Change Line
1187
+ fig.add_trace(go.Scatter(
1188
+ x=df["date"],
1189
+ y=df["ownershipPercentChange"],
1190
+ mode='lines+markers',
1191
+ name="Ownership Percent Change",
1192
+ yaxis="y2"
1193
+ ))
1194
+
1195
+ fig.update_layout(
1196
+ title="Portfolio Value and Ownership Percentage Change",
1197
+ xaxis_title="Date",
1198
+ yaxis=dict(title="Total Invested ($)", tickformat="$,.0f"),
1199
+ yaxis2=dict(title="Ownership Percent Change (%)", overlaying="y", side="right", tickformat=".2f"),
1200
+ legend=dict(title="Metrics"),
1201
+ height=500
1202
+ )
1203
+ return fig
1204
+
1205
+ def plot_positions_activity_symbol(df):
1206
+ """
1207
+ Plots positions activity.
1208
+
1209
+ Args:
1210
+ df (pd.DataFrame): DataFrame containing symbol ownership data.
1211
+
1212
+ Returns:
1213
+ plotly.graph_objects.Figure: Plotly figure object.
1214
+ """
1215
+ fig = go.Figure()
1216
+
1217
+ # New and Closed Positions Lines
1218
+ fig.add_trace(go.Scatter(
1219
+ x=df["date"],
1220
+ y=df["newPositions"],
1221
+ mode='lines+markers',
1222
+ name="New Positions",
1223
+ yaxis="y1"
1224
+ ))
1225
+ fig.add_trace(go.Scatter(
1226
+ x=df["date"],
1227
+ y=df["closedPositions"],
1228
+ mode='lines+markers',
1229
+ name="Closed Positions",
1230
+ yaxis="y1"
1231
+ ))
1232
+
1233
+ # Increased and Reduced Positions Lines
1234
+ fig.add_trace(go.Scatter(
1235
+ x=df["date"],
1236
+ y=df["increasedPositions"],
1237
+ mode='lines+markers',
1238
+ name="Increased Positions",
1239
+ yaxis="y2"
1240
+ ))
1241
+ fig.add_trace(go.Scatter(
1242
+ x=df["date"],
1243
+ y=df["reducedPositions"],
1244
+ mode='lines+markers',
1245
+ name="Reduced Positions",
1246
+ yaxis="y2"
1247
+ ))
1248
+
1249
+ fig.update_layout(
1250
+ title="Positions Activity",
1251
+ xaxis_title="Date",
1252
+ yaxis=dict(title="New/Closed Positions"),
1253
+ yaxis2=dict(title="Increased/Reduced Positions", overlaying="y", side="right"),
1254
+ legend=dict(title="Metrics"),
1255
+ height=500
1256
+ )
1257
+ return fig
1258
+
1259
+ def plot_derivative_activity_symbol(df):
1260
+ """
1261
+ Plots derivative activity.
1262
+
1263
+ Args:
1264
+ df (pd.DataFrame): DataFrame containing symbol ownership data.
1265
+
1266
+ Returns:
1267
+ plotly.graph_objects.Figure: Plotly figure object.
1268
+ """
1269
+ fig = go.Figure()
1270
+
1271
+ # Total Calls and Puts Lines
1272
+ fig.add_trace(go.Scatter(
1273
+ x=df["date"],
1274
+ y=df["totalCalls"],
1275
+ mode='lines+markers',
1276
+ name="Total Calls",
1277
+ yaxis="y1"
1278
+ ))
1279
+ fig.add_trace(go.Scatter(
1280
+ x=df["date"],
1281
+ y=df["totalPuts"],
1282
+ mode='lines+markers',
1283
+ name="Total Puts",
1284
+ yaxis="y1"
1285
+ ))
1286
+
1287
+ # Put/Call Ratio Line
1288
+ fig.add_trace(go.Scatter(
1289
+ x=df["date"],
1290
+ y=df["putCallRatio"],
1291
+ mode='lines+markers',
1292
+ name="Put/Call Ratio",
1293
+ yaxis="y2"
1294
+ ))
1295
+
1296
+ fig.update_layout(
1297
+ title="Derivative Activity",
1298
+ xaxis_title="Date",
1299
+ yaxis=dict(title="Total Calls/Puts"),
1300
+ yaxis2=dict(title="Put/Call Ratio", overlaying="y", side="right", tickformat=".2f"),
1301
+ legend=dict(title="Metrics"),
1302
+ height=500
1303
+ )
1304
+ return fig
1305
+
1306
+ def plot_changes_in_metrics_symbol(df):
1307
+ """
1308
+ Plots changes in metrics.
1309
+
1310
+ Args:
1311
+ df (pd.DataFrame): DataFrame containing symbol ownership data.
1312
+
1313
+ Returns:
1314
+ plotly.graph_objects.Figure: Plotly figure object.
1315
+ """
1316
+ fig = go.Figure()
1317
+
1318
+ # Change in Number of 13F Shares
1319
+ fig.add_trace(go.Scatter(
1320
+ x=df["date"],
1321
+ y=df["numberOf13FsharesChange"],
1322
+ mode='lines+markers',
1323
+ name="Change in 13F Shares",
1324
+ yaxis="y1"
1325
+ ))
1326
+
1327
+ # Change in Total Investment
1328
+ fig.add_trace(go.Scatter(
1329
+ x=df["date"],
1330
+ y=df["totalInvestedChange"],
1331
+ mode='lines+markers',
1332
+ name="Change in Total Investment",
1333
+ yaxis="y2"
1334
+ ))
1335
+
1336
+ fig.update_layout(
1337
+ title="Changes in Metrics",
1338
+ xaxis_title="Date",
1339
+ yaxis=dict(title="Change in 13F Shares"),
1340
+ yaxis2=dict(title="Change in Total Investment ($)", overlaying="y", side="right", tickformat="$,.0f"),
1341
+ legend=dict(title="Metrics"),
1342
+ height=500
1343
+ )
1344
+ return fig
1345
+
1346
+ def plot_ownership_and_investment_symbol(df):
1347
+ """
1348
+ Plots ownership percent and total invested.
1349
+
1350
+ Args:
1351
+ df (pd.DataFrame): DataFrame containing symbol ownership data.
1352
+
1353
+ Returns:
1354
+ plotly.graph_objects.Figure: Plotly figure object.
1355
+ """
1356
+ fig = go.Figure()
1357
+
1358
+ # Ownership Percent
1359
+ fig.add_trace(go.Scatter(
1360
+ x=df["date"],
1361
+ y=df["ownershipPercent"],
1362
+ mode='lines+markers',
1363
+ name="Ownership Percent",
1364
+ yaxis="y1"
1365
+ ))
1366
+
1367
+ # Total Invested
1368
+ fig.add_trace(go.Scatter(
1369
+ x=df["date"],
1370
+ y=df["totalInvested"],
1371
+ mode='lines+markers',
1372
+ name="Total Invested",
1373
+ yaxis="y2"
1374
+ ))
1375
+
1376
+ fig.update_layout(
1377
+ title="Ownership Percent and Total Invested",
1378
+ xaxis_title="Date",
1379
+ yaxis=dict(title="Ownership Percent (%)"),
1380
+ yaxis2=dict(title="Total Invested ($)", overlaying="y", side="right", tickformat="$,.0f"),
1381
+ legend=dict(title="Metrics"),
1382
+ height=500
1383
+ )
1384
+ return fig
1385
+
1386
+ # Function for Main Navigation
1387
+ def main():
1388
+ with st.sidebar.expander("Navigation", expanded=True):
1389
+ page = st.radio("Go to", ["CIK List", "Portfolio Allocation", "Investor Performance", "Symbol Ownership"])
1390
+
1391
+ if page == "CIK List":
1392
+ page1()
1393
+ elif page == "Portfolio Allocation":
1394
+ page2()
1395
+ elif page == "Investor Performance":
1396
+ page3()
1397
+ elif page == "Symbol Ownership":
1398
+ page4()
1399
+
1400
+ if __name__ == "__main__":
1401
+ main()
1402
+
1403
+
1404
+ hide_streamlit_style = """
1405
+ <style>
1406
+ #MainMenu {visibility: hidden;}
1407
+ footer {visibility: hidden;}
1408
+ </style>
1409
+ """
1410
+ st.markdown(hide_streamlit_style, unsafe_allow_html=True)