Spaces:
Sleeping
Sleeping
| # Copyright (2025) | |
| # Licensed under the Apache License, Version 2.0 (the "License"); | |
| # you may not use this file except in compliance with the License. | |
| # You may obtain a copy of the License at | |
| # http://www.apache.org/licenses/LICENSE-2.0 | |
| # Unless required by applicable law or agreed to in writing, software | |
| # distributed under the License is distributed on an "AS IS" BASIS, | |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| # See the License for the specific language governing permissions and | |
| # limitations under the License. | |
| import spaces | |
| import streamlit as st | |
| import requests | |
| import pandas as pd | |
| import plotly.express as px | |
| import plotly.graph_objects as go | |
| from datetime import datetime | |
| import os | |
| # Set page configuration | |
| st.set_page_config(page_title="Institutional Investor Portfolios", layout="wide") | |
| # Global API key | |
| API_KEY = os.getenv("FMP_API_KEY") | |
| # Initialize Session State for Portfolio Allocation | |
| if 'portfolio_allocation_data' not in st.session_state: | |
| st.session_state.portfolio_allocation_data = None | |
| if 'portfolio_allocation_params' not in st.session_state: | |
| st.session_state.portfolio_allocation_params = {} | |
| # Initialize Session State for Investor Performance | |
| if 'investor_performance_data' not in st.session_state: | |
| st.session_state.investor_performance_data = None | |
| if 'investor_performance_params' not in st.session_state: | |
| st.session_state.investor_performance_params = {} | |
| # Initialize Session State for Symbol Ownership | |
| if 'symbol_ownership_data' not in st.session_state: | |
| st.session_state.symbol_ownership_data = None | |
| if 'symbol_ownership_params' not in st.session_state: | |
| st.session_state.symbol_ownership_params = {} | |
| # Function for Page 1: CIK List | |
| def page1(): | |
| st.title("CIK and Name Combinations") | |
| st.markdown(""" | |
| Fetch and display the list of Central Index Key (CIK). CIK is a unique identifier assigned by the SEC to entities that file disclosures. | |
| """) | |
| with st.spinner("Fetching CIK and Name combinations..."): | |
| cik_name_df = fetch_cik_name_combinations() | |
| if not cik_name_df.empty: | |
| st.success("Data retrieved successfully!") | |
| st.dataframe(cik_name_df, use_container_width=True, height=800) | |
| # Provide download option | |
| csv = cik_name_df.to_csv(index=False).encode('utf-8') | |
| st.download_button( | |
| label="Download Data as CSV", | |
| data=csv, | |
| file_name='cik_name_combinations.csv', | |
| mime='text/csv', | |
| ) | |
| else: | |
| st.error("No data retrieved.") | |
| # Function to fetch CIK and name combinations | |
| def fetch_cik_name_combinations(): | |
| """ | |
| Fetches a list of CIK and name combinations from the API. | |
| Returns: | |
| pd.DataFrame: A DataFrame containing CIK and name pairs. | |
| """ | |
| url = f"https://financialmodelingprep.com/api/v4/institutional-ownership/list?apikey={API_KEY}" | |
| try: | |
| response = requests.get(url) | |
| if response.status_code == 200: | |
| data = response.json() | |
| df = pd.DataFrame(data) | |
| if not df.empty and 'cik' in df.columns and 'name' in df.columns: | |
| df = df[['cik', 'name']] | |
| return df | |
| else: | |
| return pd.DataFrame() | |
| else: | |
| st.error(f"Error fetching data: {response.status_code}, {response.text}") | |
| return pd.DataFrame() | |
| except Exception as e: | |
| st.error(f"An exception occurred: {e}") | |
| return pd.DataFrame() | |
| # Function for Page 2: Portfolio Allocation | |
| def page2(): | |
| st.title("Institutional Portfolio Allocation") | |
| st.markdown(""" | |
| 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. | |
| """) | |
| # Sidebar Inputs within an Expander | |
| with st.sidebar.expander("Inputs", expanded=True): | |
| cik_input = st.text_input("Enter CIK", value="0001067983", help="Enter the investor's CIK, which is a unique SEC identifier (e.g., 0001067983).") | |
| start_date = st.date_input("Select Start Date", value=datetime(2021, 9, 30), help="Choose the earliest quarter-end date for which to retrieve portfolio allocation data. Data will be fetched from this date onward.") | |
| top_n = st.number_input("Top N Groups", min_value=1, max_value=100, value=10, step=1, help="Set the number of top groups (by cumulative weight) to display in the trend charts.") | |
| top_n_changes = st.number_input("Top N Changes", min_value=1, max_value=100, value=10, step=1, help="Set the number of top positive and negative changes to display in the changes chart (i.e. to determine the biggest allocation shifts).") | |
| run_button = st.sidebar.button("Run") | |
| if run_button or (st.session_state.portfolio_allocation_params.get('cik') == cik_input and | |
| st.session_state.portfolio_allocation_params.get('start_date') == start_date.strftime("%Y-%m-%d") and | |
| st.session_state.portfolio_allocation_params.get('top_n') == top_n): | |
| # Update Session State with current parameters | |
| st.session_state.portfolio_allocation_params = { | |
| 'cik': cik_input, | |
| 'start_date': start_date.strftime("%Y-%m-%d"), | |
| 'top_n': top_n | |
| } | |
| with st.spinner("Fetching and processing portfolio allocation data..."): | |
| allocation_data = fetch_data_over_time(cik_input, start_date.strftime("%Y-%m-%d")) | |
| if not allocation_data.empty: | |
| st.session_state.portfolio_allocation_data = allocation_data | |
| st.success("Data retrieved successfully!") | |
| # Latest date data for bar charts | |
| latest_date = allocation_data["date"].max() | |
| latest_data = allocation_data[allocation_data["date"] == latest_date] | |
| # Plot bar charts | |
| st.subheader(f"Portfolio Allocation by Ticker on {latest_date}") | |
| fig1 = plot_bar_chart(latest_data, "symbol", "Weight (%)", "Market Value", height=500) | |
| st.plotly_chart(fig1, use_container_width=True) | |
| st.subheader(f"Portfolio Allocation by Industry on {latest_date}") | |
| fig2 = plot_bar_chart(latest_data, "industryTitle", "Weight (%)", "Market Value", height=700) | |
| st.plotly_chart(fig2, use_container_width=True) | |
| # Time-series plots for trends | |
| st.subheader("Portfolio Allocation Trends by Symbol") | |
| st.markdown(""" | |
| **Explanation:** | |
| 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. | |
| """) | |
| fig3 = plot_allocation_trends(allocation_data, "symbol", st.session_state.portfolio_allocation_params.get('top_n')) | |
| st.plotly_chart(fig3, use_container_width=True, height=600) | |
| st.subheader("Portfolio Allocation Trends by Industry") | |
| st.markdown(""" | |
| **Explanation:** | |
| 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. | |
| """) | |
| fig4 = plot_allocation_trends(allocation_data, "industryTitle", st.session_state.portfolio_allocation_params.get('top_n')) | |
| st.plotly_chart(fig4, use_container_width=True, height=600) | |
| st.subheader("Top Changes in Portfolio Allocation") | |
| fig_top_changes = plot_top_changes_plotly(allocation_data, n=top_n_changes) | |
| st.plotly_chart(fig_top_changes, use_container_width=True) | |
| st.subheader("Total Portfolio Value by Date") | |
| grouped_df = allocation_data.groupby("date").agg( | |
| total_market_value=("marketValue", "sum"), | |
| total_weight=("weight", "sum") | |
| ).reset_index() | |
| # Round values to the nearest whole number | |
| grouped_df["total_market_value"] = grouped_df["total_market_value"].round(0).astype(int) | |
| grouped_df["total_weight"] = grouped_df["total_weight"].round(0).astype(int) | |
| # Create a formatted market value column using your formatting function | |
| grouped_df["market_value"] = grouped_df["total_market_value"].apply(format_market_value) | |
| # Select and rename columns for clarity | |
| grouped_df = grouped_df[["date", "market_value", "total_weight"]] | |
| grouped_df.columns = ["Date", "Total Market Value", "Total Weight"] | |
| st.dataframe(grouped_df, use_container_width=True) | |
| # Transpose data at the bottom | |
| st.markdown("---") | |
| st.subheader("Industry Allocation Overview by Date") | |
| st.markdown(""" | |
| **Explanation:** | |
| 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. | |
| """) | |
| transposed_ticker_data = transpose_data(allocation_data, "symbol") | |
| st.dataframe(transposed_ticker_data, use_container_width=True) | |
| st.subheader("Transposed Industry Data") | |
| st.markdown(""" | |
| **Explanation:** | |
| 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. | |
| """) | |
| transposed_industry_data = transpose_data(allocation_data, "industryTitle") | |
| st.dataframe(transposed_industry_data, use_container_width=True) | |
| else: | |
| st.error("No data found for the specified CIK and date range.") | |
| # If data exists in session state and parameters match, display it without rerunning | |
| elif st.session_state.portfolio_allocation_data and st.session_state.portfolio_allocation_params.get('cik') == cik_input and \ | |
| st.session_state.portfolio_allocation_params.get('start_date') == start_date.strftime("%Y-%m-%d") and \ | |
| st.session_state.portfolio_allocation_params.get('top_n') == top_n: | |
| allocation_data = st.session_state.portfolio_allocation_data | |
| st.success("Displaying previously retrieved data.") | |
| # Latest date data for bar charts | |
| latest_date = allocation_data["date"].max() | |
| latest_data = allocation_data[allocation_data["date"] == latest_date] | |
| # Plot bar charts | |
| st.subheader(f"Portfolio Allocation by Ticker on {latest_date}") | |
| fig1 = plot_bar_chart(latest_data, "symbol", "Weight (%)", "Market Value", height=500) | |
| st.plotly_chart(fig1, use_container_width=True) | |
| st.subheader(f"Portfolio Allocation by Industry on {latest_date}") | |
| fig2 = plot_bar_chart(latest_data, "industryTitle", "Weight (%)", "Market Value", height=700) | |
| st.plotly_chart(fig2, use_container_width=True) | |
| # Time-series plots for trends | |
| st.subheader("Portfolio Allocation Trends by Symbol") | |
| st.markdown(""" | |
| 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. | |
| """) | |
| fig3 = plot_allocation_trends(allocation_data, "symbol", top_n) | |
| st.plotly_chart(fig3, use_container_width=True, height=600) | |
| st.subheader("Portfolio Allocation Trends by Industry") | |
| st.markdown(""" | |
| 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. | |
| """) | |
| fig4 = plot_allocation_trends(allocation_data, "industryTitle", top_n) | |
| st.plotly_chart(fig4, use_container_width=True, height=600) | |
| # Transpose data at the bottom | |
| st.markdown("---") | |
| st.subheader("Transposed Ticker Data") | |
| st.markdown(""" | |
| 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. | |
| """) | |
| transposed_ticker_data = transpose_data(allocation_data, "symbol") | |
| st.dataframe(transposed_ticker_data, use_container_width=True) | |
| st.subheader("Transposed Industry Data") | |
| st.markdown(""" | |
| 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. | |
| """) | |
| transposed_industry_data = transpose_data(allocation_data, "industryTitle") | |
| st.dataframe(transposed_industry_data, use_container_width=True) | |
| def plot_top_changes_plotly(df, n=10): | |
| # Get unique dates in descending order | |
| unique_dates = sorted(df["date"].unique(), reverse=True) | |
| if len(unique_dates) < 2: | |
| return go.Figure() # Not enough data | |
| latest_date = unique_dates[0] | |
| prev_date = unique_dates[1] | |
| # Filter data for the two most recent dates | |
| df_latest = df[df["date"] == latest_date] | |
| df_prev = df[df["date"] == prev_date] | |
| # Aggregate data by symbol for each date | |
| df_latest_agg = df_latest.groupby("symbol", as_index=False).agg({ | |
| "weight": "sum", | |
| "marketValue": "sum" | |
| }) | |
| df_prev_agg = df_prev.groupby("symbol", as_index=False).agg({ | |
| "weight": "sum", | |
| "marketValue": "sum" | |
| }) | |
| # Merge data on symbol to include new additions or removals | |
| merged = pd.merge(df_prev_agg, df_latest_agg, on="symbol", | |
| suffixes=("_prev", "_latest"), how="outer") | |
| merged.fillna(0, inplace=True) | |
| # Compute changes in weight and market value | |
| merged['delta_weight'] = merged['weight_latest'] - merged['weight_prev'] | |
| merged['delta_marketValue'] = merged['marketValue_latest'] - merged['marketValue_prev'] | |
| # Select top n positive changes and top n negative changes | |
| top_up = merged[merged['delta_weight'] > 0].sort_values( | |
| by='delta_weight', ascending=False).head(n) | |
| top_down = merged[merged['delta_weight'] < 0].sort_values( | |
| by='delta_weight', ascending=True).head(n) | |
| combined = pd.concat([top_up, top_down]) | |
| combined['color'] = combined['delta_weight'].apply(lambda x: 'green' if x >= 0 else 'red') | |
| combined['text'] = combined.apply( | |
| lambda row: f"{row['delta_weight']:.1f}% ({format_market_value(row['delta_marketValue'])})", axis=1) | |
| fig = go.Figure(data=[go.Bar( | |
| x=combined['symbol'], | |
| y=combined['delta_weight'], | |
| text=combined['text'], | |
| textposition='auto', | |
| marker_color=combined['color'] | |
| )]) | |
| fig.update_layout( | |
| title=f"Top Up and Down Changes: {prev_date} to {latest_date}", | |
| xaxis_title="Symbol", | |
| yaxis_title="Change in Weight (%)", | |
| template="plotly_white", | |
| height=500 | |
| ) | |
| return fig | |
| # Functions used in Page 2 | |
| def fetch_dates(cik): | |
| """ | |
| Fetches all available quarter-end dates for a specific CIK. | |
| Args: | |
| cik (str): Central Index Key of the institutional investor. | |
| Returns: | |
| list: A list of available quarter-end dates in descending order. | |
| """ | |
| endpoint = f"https://financialmodelingprep.com/api/v4/institutional-ownership/portfolio-date" | |
| params = {"cik": cik, "apikey": API_KEY} | |
| try: | |
| response = requests.get(endpoint, params=params) | |
| if response.status_code == 200: | |
| dates = sorted([item["date"] for item in response.json()], reverse=True) | |
| return dates | |
| else: | |
| st.error(f"Error fetching dates: {response.status_code}, {response.text}") | |
| return [] | |
| except Exception as e: | |
| st.error(f"An exception occurred: {e}") | |
| return [] | |
| def fetch_portfolio_allocation(cik, date, page=0): | |
| """ | |
| Fetches portfolio allocation for a specific CIK and date from the API. | |
| Args: | |
| cik (str): Central Index Key of the institutional investor. | |
| date (str): Quarter-end date in YYYY-MM-DD format. | |
| page (int): Page number for large datasets (default is 0). | |
| Returns: | |
| pd.DataFrame: Processed DataFrame containing portfolio allocation. | |
| """ | |
| endpoint = f"https://financialmodelingprep.com/api/v4/institutional-ownership/portfolio-holdings" | |
| params = { | |
| "cik": cik, | |
| "date": date, | |
| "page": page, | |
| "apikey": API_KEY | |
| } | |
| try: | |
| response = requests.get(endpoint, params=params) | |
| if response.status_code == 200: | |
| data = response.json() | |
| df = pd.DataFrame(data) | |
| if not df.empty and all(col in df.columns for col in ["symbol", "industryTitle", "weight", "marketValue"]): | |
| df = df[["symbol", "industryTitle", "weight", "marketValue"]] | |
| df["weight"] = df["weight"].astype(float) | |
| df["marketValue"] = df["marketValue"].astype(float) | |
| df["date"] = date | |
| return df | |
| else: | |
| return pd.DataFrame() | |
| else: | |
| st.error(f"Error fetching allocation: {response.status_code}, {response.text}") | |
| return pd.DataFrame() | |
| except Exception as e: | |
| st.error(f"An exception occurred: {e}") | |
| return pd.DataFrame() | |
| def fetch_data_over_time(cik, start_date): | |
| available_dates = fetch_dates(cik) | |
| selected_dates = [date for date in available_dates if date >= start_date] | |
| all_data = [] | |
| for date in selected_dates: | |
| page = 0 | |
| while True: | |
| df = fetch_portfolio_allocation(cik, date, page=page) | |
| if df.empty: | |
| break | |
| all_data.append(df) | |
| page += 1 | |
| if all_data: | |
| return pd.concat(all_data, ignore_index=True) | |
| else: | |
| st.error("No data found for the specified time range.") | |
| return pd.DataFrame() | |
| def transpose_data(df, group_by_column): | |
| """ | |
| Transposes the DataFrame so that dates become columns for weight and market value. | |
| Args: | |
| df (pd.DataFrame): Original DataFrame containing portfolio allocation over time. | |
| group_by_column (str): Column to group data by (e.g., "symbol" or "industryTitle"). | |
| Returns: | |
| pd.DataFrame: Transposed DataFrame. | |
| """ | |
| pivoted_weight = df.pivot_table(values="weight", index=group_by_column, columns="date", aggfunc="sum", fill_value=0) | |
| pivoted_market_value = df.pivot_table(values="marketValue", index=group_by_column, columns="date", aggfunc="sum", fill_value=0) | |
| # Reorder columns: weight -> market value -> next weight -> next market value | |
| combined = pd.concat([pivoted_weight, pivoted_market_value], axis=1, keys=["Weight", "Market Value"]) | |
| combined = combined.swaplevel(axis=1).sort_index(axis=1) | |
| return combined | |
| def plot_bar_chart(df, group_by_column, weight_title, market_value_title, height=500): | |
| """ | |
| Plots a bar chart for portfolio allocation using Plotly. | |
| Args: | |
| df (pd.DataFrame): DataFrame containing portfolio allocation data. | |
| group_by_column (str): Column to group data by (e.g., "symbol" or "industryTitle"). | |
| weight_title (str): Title for the weight axis. | |
| market_value_title (str): Title for the market value. | |
| height (int): Height of the plot in pixels. | |
| Returns: | |
| plotly.graph_objects.Figure: Plotly figure object. | |
| """ | |
| # Ensure data only corresponds to the latest date and aggregate by the group column | |
| latest_date = df["date"].max() | |
| filtered_df = df[df["date"] == latest_date].groupby(group_by_column, as_index=False).sum() | |
| # Sort the data by weight in descending order | |
| filtered_df = filtered_df.sort_values(by="weight", ascending=False) | |
| # Format market values for display | |
| filtered_df["marketValueFormatted"] = filtered_df["marketValue"].apply(format_market_value) | |
| # Create bar chart | |
| fig = go.Figure() | |
| fig.add_trace(go.Bar( | |
| x=filtered_df[group_by_column], | |
| y=filtered_df["weight"], | |
| text=[f"{w:.1f}%<br>{mv}" for w, mv in zip(filtered_df["weight"], filtered_df["marketValueFormatted"])], | |
| textposition="outside", | |
| texttemplate="%{text}", | |
| marker=dict(color="teal"), | |
| textfont=dict(size=14), # Makes the labels larger | |
| cliponaxis=False # Ensures labels aren't clipped | |
| )) | |
| fig.update_layout( | |
| title=f"Portfolio Allocation by {group_by_column.capitalize()} on {latest_date}", | |
| xaxis_title=group_by_column.capitalize(), | |
| yaxis_title=weight_title, | |
| xaxis_tickangle=45, # Make labels vertical | |
| showlegend=False, | |
| height=height | |
| ) | |
| fig.update_traces( | |
| textfont_size=12, # Increase font size | |
| cliponaxis=False # Ensure text doesn't get clipped | |
| ) | |
| return fig | |
| def plot_allocation_trends(df, group_by_column, top_n=10): | |
| """ | |
| Plots trends for portfolio allocation over time using Plotly. | |
| Args: | |
| df (pd.DataFrame): DataFrame containing portfolio allocation data over time. | |
| group_by_column (str): Column to group data by (e.g., "symbol" or "industryTitle"). | |
| top_n (int): Number of top contributors to show in the chart. | |
| Returns: | |
| plotly.express.Figure: Plotly figure object. | |
| """ | |
| # Group and aggregate data by group_by_column and date | |
| aggregated_df = df.groupby([group_by_column, "date"], as_index=False).sum() | |
| # Summarize total weight for sorting | |
| total_weight = aggregated_df.groupby(group_by_column)["weight"].sum().sort_values(ascending=False) | |
| top_groups = total_weight.head(top_n).index | |
| # Filter data to include only the top_n groups | |
| filtered_df = aggregated_df[aggregated_df[group_by_column].isin(top_groups)] | |
| filtered_df["marketValueFormatted"] = filtered_df["marketValue"].apply(format_market_value) | |
| # Create the Plotly line chart | |
| fig = px.line( | |
| filtered_df, | |
| x="date", | |
| y="weight", | |
| color=group_by_column, | |
| title=f"Portfolio Allocation Trends by {group_by_column.capitalize()}", | |
| labels={"weight": "Weight (%)", "date": "Date"}, | |
| markers=True, | |
| hover_data={ | |
| group_by_column: True, | |
| "weight": ":.2f", # Weight with 2 decimals | |
| "marketValueFormatted": True # Show market value in hover | |
| } | |
| ) | |
| fig.update_layout( | |
| legend_title=group_by_column.capitalize(), | |
| xaxis_title="Date", | |
| yaxis_title="Weight (%)", | |
| hovermode="closest", | |
| height=600 | |
| ) | |
| return fig | |
| def format_market_value(value): | |
| sign = "-" if value < 0 else "" | |
| abs_value = abs(value) | |
| if abs_value >= 1e9: | |
| return f"{sign}{abs_value/1e9:.1f}B" | |
| elif abs_value >= 1e6: | |
| return f"{sign}{abs_value/1e6:.1f}M" | |
| elif abs_value >= 1e3: | |
| return f"{sign}{abs_value/1e3:.1f}K" | |
| else: | |
| return f"{sign}{abs_value:.1f}" | |
| # Function for Page 3: Investor Performance | |
| def page3(): | |
| st.title("Investor Performance") | |
| st.markdown(""" | |
| 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. | |
| """) | |
| # Sidebar Inputs within an Expander | |
| with st.sidebar.expander("Investor Performance Inputs", expanded=True): | |
| cik_input = st.text_input("Enter CIK", value="0001067983", help="Enter the investor's CIK (e.g., 0001067983) to retrieve performance metrics.") | |
| run_button = st.sidebar.button("Run") | |
| if run_button or (st.session_state.investor_performance_params.get('cik') == cik_input): | |
| # Update Session State with current parameters | |
| st.session_state.investor_performance_params = { | |
| 'cik': cik_input | |
| } | |
| with st.spinner("Fetching and processing investor performance data..."): | |
| data = fetch_investor_performance(cik_input) | |
| if not data.empty: | |
| st.session_state.investor_performance_data = data | |
| st.success("Data retrieved successfully!") | |
| st.dataframe(data, use_container_width=True) | |
| # Ensure the data is sorted by date | |
| data["date"] = pd.to_datetime(data["date"]) | |
| data = data.sort_values(by="date") | |
| # Plotting | |
| st.subheader("Portfolio Value and Change in Market Value (%)") | |
| st.markdown(""" | |
| **Explanation:** | |
| 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. | |
| """) | |
| fig1 = plot_portfolio_value_and_change(data) | |
| st.plotly_chart(fig1, use_container_width=True) | |
| st.subheader("Performance and Relative Performance to S&P 500") | |
| st.markdown(""" | |
| **Explanation:** | |
| 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. | |
| """) | |
| fig2 = plot_performance_and_relative(data) | |
| st.plotly_chart(fig2, use_container_width=True) | |
| st.subheader("Portfolio Turnover Metrics") | |
| st.markdown(""" | |
| **Explanation:** | |
| This chart illustrates the portfolio's turnover rate, including buy and sell activities. Turnover metrics provide insight into the trading frequency and strategy. | |
| """) | |
| fig3 = plot_turnover_metrics(data) | |
| st.plotly_chart(fig3, use_container_width=True) | |
| st.subheader("Cumulative Performance Over Time") | |
| st.markdown(""" | |
| **Explanation:** | |
| 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. | |
| """) | |
| fig4 = plot_cumulative_performance(data) | |
| st.plotly_chart(fig4, use_container_width=True) | |
| st.subheader("Holding Periods") | |
| st.markdown(""" | |
| **Explanation:** | |
| This chart depicts the average holding periods for the portfolio and its top holdings. Holding periods indicate the investment duration and strategy stability. | |
| """) | |
| fig5 = plot_holding_periods(data) | |
| st.plotly_chart(fig5, use_container_width=True) | |
| st.subheader("Portfolio Activity") | |
| st.markdown(""" | |
| **Explanation:** | |
| This chart displays portfolio size changes and the number of securities added or removed. It provides an overview of portfolio expansion or contraction. | |
| """) | |
| fig6 = plot_portfolio_activity(data) | |
| st.plotly_chart(fig6, use_container_width=True) | |
| st.subheader("Market Value and Securities Count") | |
| st.markdown(""" | |
| **Explanation:** | |
| 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. | |
| """) | |
| fig7 = plot_market_value_and_securities(data) | |
| st.plotly_chart(fig7, use_container_width=True) | |
| else: | |
| st.error("No data found for the specified CIK.") | |
| # If data exists in session state and parameters match, display it without rerunning | |
| elif st.session_state.investor_performance_data and st.session_state.investor_performance_params.get('cik') == cik_input: | |
| data = st.session_state.investor_performance_data | |
| st.success("Displaying previously retrieved data.") | |
| st.dataframe(data, use_container_width=True) | |
| # Ensure the data is sorted by date | |
| data["date"] = pd.to_datetime(data["date"]) | |
| data = data.sort_values(by="date") | |
| # Plotting | |
| st.subheader("Portfolio Value and Change in Market Value (%)") | |
| st.markdown(""" | |
| 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. | |
| """) | |
| fig1 = plot_portfolio_value_and_change(data) | |
| st.plotly_chart(fig1, use_container_width=True) | |
| st.subheader("Performance and Relative Performance to S&P 500") | |
| st.markdown(""" | |
| 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. | |
| """) | |
| fig2 = plot_performance_and_relative(data) | |
| st.plotly_chart(fig2, use_container_width=True) | |
| st.subheader("Portfolio Turnover Metrics") | |
| st.markdown(""" | |
| This chart illustrates the portfolio's turnover rate, including buy and sell activities. Turnover metrics provide insight into the trading frequency and strategy. | |
| """) | |
| fig3 = plot_turnover_metrics(data) | |
| st.plotly_chart(fig3, use_container_width=True) | |
| st.subheader("Cumulative Performance Over Time") | |
| st.markdown(""" | |
| 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. | |
| """) | |
| fig4 = plot_cumulative_performance(data) | |
| st.plotly_chart(fig4, use_container_width=True) | |
| st.subheader("Holding Periods") | |
| st.markdown(""" | |
| This chart depicts the average holding periods for the portfolio and its top holdings. Holding periods indicate the investment duration and strategy stability. | |
| """) | |
| fig5 = plot_holding_periods(data) | |
| st.plotly_chart(fig5, use_container_width=True) | |
| st.subheader("Portfolio Activity") | |
| st.markdown(""" | |
| This chart displays portfolio size changes and the number of securities added or removed. It provides an overview of portfolio expansion or contraction. | |
| """) | |
| fig6 = plot_portfolio_activity(data) | |
| st.plotly_chart(fig6, use_container_width=True) | |
| st.subheader("Market Value and Securities Count") | |
| st.markdown(""" | |
| 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. | |
| """) | |
| fig7 = plot_market_value_and_securities(data) | |
| st.plotly_chart(fig7, use_container_width=True) | |
| # Functions used in Page 3 | |
| def fetch_investor_performance(cik): | |
| """ | |
| Fetches investor performance data from FMP API. | |
| Args: | |
| cik (str): Central Index Key of the institutional investor. | |
| Returns: | |
| pd.DataFrame: DataFrame containing investor performance data. | |
| """ | |
| url = f"https://financialmodelingprep.com/api/v4/institutional-ownership/portfolio-holdings-summary" | |
| params = { | |
| "cik": cik, | |
| "page": 0, | |
| "apikey": API_KEY | |
| } | |
| try: | |
| response = requests.get(url, params=params) | |
| if response.status_code == 200: | |
| data = response.json() | |
| df = pd.DataFrame(data) | |
| return df | |
| else: | |
| st.error(f"Failed to fetch data: {response.status_code}") | |
| return pd.DataFrame() | |
| except Exception as e: | |
| st.error(f"An exception occurred: {e}") | |
| return pd.DataFrame() | |
| def plot_portfolio_value_and_change(df): | |
| """ | |
| Plots portfolio value and change in market value percentage. | |
| Args: | |
| df (pd.DataFrame): DataFrame containing investor performance data. | |
| Returns: | |
| plotly.graph_objects.Figure: Plotly figure object. | |
| """ | |
| fig = go.Figure() | |
| # Portfolio Value Line | |
| fig.add_trace(go.Scatter( | |
| x=df["date"], | |
| y=df["marketValue"], | |
| mode='lines+markers', | |
| name="Portfolio Value", | |
| yaxis="y1" | |
| )) | |
| # Change in Market Value Percentage Line | |
| fig.add_trace(go.Scatter( | |
| x=df["date"], | |
| y=df["changeInMarketValuePercentage"], | |
| mode='lines+markers', | |
| name="Change in Market Value (%)", | |
| yaxis="y2" | |
| )) | |
| fig.update_layout( | |
| title="Portfolio Value and Change in Market Value (%)", | |
| xaxis_title="Date", | |
| yaxis=dict(title="Market Value ($)", tickformat="$,.0f"), | |
| yaxis2=dict(title="Change in Market Value (%)", overlaying="y", side="right", tickformat=".2f"), | |
| legend=dict(title="Metrics"), | |
| height=500 | |
| ) | |
| return fig | |
| def plot_performance_and_relative(df): | |
| """ | |
| Plots performance and relative performance to S&P 500. | |
| Args: | |
| df (pd.DataFrame): DataFrame containing investor performance data. | |
| Returns: | |
| plotly.graph_objects.Figure: Plotly figure object. | |
| """ | |
| fig = go.Figure() | |
| # Performance Percentage Line | |
| fig.add_trace(go.Scatter( | |
| x=df["date"], | |
| y=df["performancePercentage"], | |
| mode='lines+markers', | |
| name="Performance (%)", | |
| yaxis="y1" | |
| )) | |
| # Relative Performance Percentage Line | |
| fig.add_trace(go.Scatter( | |
| x=df["date"], | |
| y=df["performanceRelativeToSP500Percentage"], | |
| mode='lines+markers', | |
| name="Relative Performance to S&P 500 (%)", | |
| yaxis="y2" | |
| )) | |
| fig.update_layout( | |
| title="Performance and Relative Performance to S&P 500", | |
| xaxis_title="Date", | |
| yaxis=dict(title="Performance (%)", tickformat=".2f"), | |
| yaxis2=dict(title="Relative Performance (%)", overlaying="y", side="right", tickformat=".2f"), | |
| legend=dict(title="Metrics"), | |
| height=500 | |
| ) | |
| return fig | |
| def plot_turnover_metrics(df): | |
| """ | |
| Plots portfolio turnover metrics. | |
| Args: | |
| df (pd.DataFrame): DataFrame containing investor performance data. | |
| Returns: | |
| plotly.graph_objects.Figure: Plotly figure object. | |
| """ | |
| fig = go.Figure() | |
| # Turnover Line | |
| fig.add_trace(go.Scatter( | |
| x=df["date"], | |
| y=df["turnover"], | |
| mode='lines+markers', | |
| name="Turnover (%)", | |
| yaxis="y1" | |
| )) | |
| # Alternate Turnover Metrics | |
| fig.add_trace(go.Scatter( | |
| x=df["date"], | |
| y=df["turnoverAlternateSell"], | |
| mode='lines+markers', | |
| name="Turnover (Sell)", | |
| yaxis="y2" | |
| )) | |
| fig.add_trace(go.Scatter( | |
| x=df["date"], | |
| y=df["turnoverAlternateBuy"], | |
| mode='lines+markers', | |
| name="Turnover (Buy)", | |
| yaxis="y2" | |
| )) | |
| fig.update_layout( | |
| title="Portfolio Turnover Metrics", | |
| xaxis_title="Date", | |
| yaxis=dict(title="Turnover (%)", tickformat=".2f"), | |
| yaxis2=dict(title="Turnover (Buy/Sell)", overlaying="y", side="right", tickformat=".2f"), | |
| legend=dict(title="Metrics"), | |
| height=500 | |
| ) | |
| return fig | |
| def plot_cumulative_performance(df): | |
| """ | |
| Plots cumulative performance over time. | |
| Args: | |
| df (pd.DataFrame): DataFrame containing investor performance data. | |
| Returns: | |
| plotly.graph_objects.Figure: Plotly figure object. | |
| """ | |
| fig = go.Figure() | |
| # Cumulative Performance Percentages | |
| fig.add_trace(go.Scatter( | |
| x=df["date"], | |
| y=df["performancePercentage1year"], | |
| mode='lines+markers', | |
| name="1-Year Performance (%)", | |
| yaxis="y1" | |
| )) | |
| fig.add_trace(go.Scatter( | |
| x=df["date"], | |
| y=df["performancePercentage3year"], | |
| mode='lines+markers', | |
| name="3-Year Performance (%)", | |
| yaxis="y1" | |
| )) | |
| fig.add_trace(go.Scatter( | |
| x=df["date"], | |
| y=df["performancePercentage5year"], | |
| mode='lines+markers', | |
| name="5-Year Performance (%)", | |
| yaxis="y1" | |
| )) | |
| fig.add_trace(go.Scatter( | |
| x=df["date"], | |
| y=df["performanceSinceInceptionPercentage"], | |
| mode='lines+markers', | |
| name="Since Inception (%)", | |
| yaxis="y1" | |
| )) | |
| fig.update_layout( | |
| title="Cumulative Performance Over Time", | |
| xaxis_title="Date", | |
| yaxis=dict(title="Performance (%)", tickformat=".2f"), | |
| legend=dict(title="Metrics"), | |
| height=500 | |
| ) | |
| return fig | |
| def plot_holding_periods(df): | |
| """ | |
| Plots holding periods. | |
| Args: | |
| df (pd.DataFrame): DataFrame containing investor performance data. | |
| Returns: | |
| plotly.graph_objects.Figure: Plotly figure object. | |
| """ | |
| fig = go.Figure() | |
| # Overall Holding Period | |
| fig.add_trace(go.Scatter( | |
| x=df["date"], | |
| y=df["averageHoldingPeriod"], | |
| mode='lines+markers', | |
| name="Average Holding Period", | |
| yaxis="y1" | |
| )) | |
| # Top Holdings Periods | |
| fig.add_trace(go.Scatter( | |
| x=df["date"], | |
| y=df["averageHoldingPeriodTop10"], | |
| mode='lines+markers', | |
| name="Top 10 Holding Period", | |
| yaxis="y1" | |
| )) | |
| fig.add_trace(go.Scatter( | |
| x=df["date"], | |
| y=df["averageHoldingPeriodTop20"], | |
| mode='lines+markers', | |
| name="Top 20 Holding Period", | |
| yaxis="y1" | |
| )) | |
| fig.update_layout( | |
| title="Holding Periods", | |
| xaxis_title="Date", | |
| yaxis=dict(title="Holding Period (Quarters)"), | |
| legend=dict(title="Metrics"), | |
| height=500 | |
| ) | |
| return fig | |
| def plot_portfolio_activity(df): | |
| """ | |
| Plots portfolio activity. | |
| Args: | |
| df (pd.DataFrame): DataFrame containing investor performance data. | |
| Returns: | |
| plotly.graph_objects.Figure: Plotly figure object. | |
| """ | |
| fig = go.Figure() | |
| # Portfolio Size | |
| fig.add_trace(go.Scatter( | |
| x=df["date"], | |
| y=df["portfolioSize"], | |
| mode='lines+markers', | |
| name="Portfolio Size", | |
| yaxis="y1" | |
| )) | |
| # Securities Added and Removed | |
| fig.add_trace(go.Scatter( | |
| x=df["date"], | |
| y=df["securitiesAdded"], | |
| mode='lines+markers', | |
| name="Securities Added", | |
| yaxis="y2" | |
| )) | |
| fig.add_trace(go.Scatter( | |
| x=df["date"], | |
| y=df["securitiesRemoved"], | |
| mode='lines+markers', | |
| name="Securities Removed", | |
| yaxis="y2" | |
| )) | |
| fig.update_layout( | |
| title="Portfolio Activity", | |
| xaxis_title="Date", | |
| yaxis=dict(title="Portfolio Size"), | |
| yaxis2=dict(title="Activity (Count)", overlaying="y", side="right"), | |
| legend=dict(title="Metrics"), | |
| height=500 | |
| ) | |
| return fig | |
| def plot_market_value_and_securities(df): | |
| """ | |
| Plots market value and securities count. | |
| Args: | |
| df (pd.DataFrame): DataFrame containing investor performance data. | |
| Returns: | |
| plotly.graph_objects.Figure: Plotly figure object. | |
| """ | |
| fig = go.Figure() | |
| # Portfolio Value Line | |
| fig.add_trace(go.Scatter( | |
| x=df["date"], | |
| y=df["marketValue"], | |
| mode='lines+markers', | |
| name="Portfolio Value", | |
| yaxis="y1" | |
| )) | |
| # Portfolio Size Line | |
| fig.add_trace(go.Scatter( | |
| x=df["date"], | |
| y=df["portfolioSize"], | |
| mode='lines+markers', | |
| name="Portfolio Size", | |
| yaxis="y2" | |
| )) | |
| fig.update_layout( | |
| title="Market Value and Securities Count", | |
| xaxis_title="Date", | |
| yaxis=dict(title="Market Value ($)", tickformat="$,.0f"), | |
| yaxis2=dict(title="Portfolio Size", overlaying="y", side="right"), | |
| legend=dict(title="Metrics"), | |
| height=500 | |
| ) | |
| return fig | |
| # Function to fetch symbol ownership (Page 4) | |
| def fetch_symbol_ownership(symbol): | |
| """ | |
| Fetches symbol ownership data from FMP API. | |
| Args: | |
| symbol (str): Stock symbol. | |
| Returns: | |
| pd.DataFrame: DataFrame containing symbol ownership data. | |
| """ | |
| url = f"https://financialmodelingprep.com/api/v4/institutional-ownership/symbol-ownership" | |
| params = { | |
| "symbol": symbol, | |
| "includeCurrentQuarter": "true", | |
| "apikey": API_KEY | |
| } | |
| try: | |
| response = requests.get(url, params=params) | |
| if response.status_code == 200: | |
| data = response.json() | |
| df = pd.DataFrame(data) | |
| return df | |
| else: | |
| st.error(f"Failed to fetch data: {response.status_code}") | |
| return pd.DataFrame() | |
| except Exception as e: | |
| st.error(f"An exception occurred: {e}") | |
| return pd.DataFrame() | |
| # Function for Page 4: Symbol Ownership | |
| def page4(): | |
| st.title("Symbol Ownership") | |
| st.markdown(""" | |
| Fetch and visualizes ownership data for a specific stock symbol. Analyze investor counts, ownership percentages, portfolio value changes, positions activity, derivative activity. | |
| """) | |
| # Sidebar Inputs within an Expander | |
| with st.sidebar.expander("Symbol Ownership Inputs", expanded=True): | |
| symbol = st.text_input("Enter Stock Symbol", value="AAPL", help="Enter a valid stock symbol (e.g., AAPL for Apple Inc.) to analyze its ownership data.") | |
| run_button = st.sidebar.button("Run") | |
| if run_button or (st.session_state.symbol_ownership_params.get('symbol') == symbol): | |
| # Update Session State with current parameters | |
| st.session_state.symbol_ownership_params = { | |
| 'symbol': symbol | |
| } | |
| with st.spinner("Fetching and processing symbol ownership data..."): | |
| data = fetch_symbol_ownership(symbol) | |
| if not data.empty: | |
| st.session_state.symbol_ownership_data = data | |
| st.success("Data retrieved successfully!") | |
| st.dataframe(data, use_container_width=True) | |
| # Ensure the data is sorted by date | |
| data["date"] = pd.to_datetime(data["date"]) | |
| data = data.sort_values(by="date") | |
| # Plotting | |
| st.subheader("Investor Count and Ownership Percentage") | |
| st.markdown(""" | |
| **Explanation:** | |
| 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. | |
| """) | |
| fig1 = plot_investor_count_and_ownership(data) | |
| st.plotly_chart(fig1, use_container_width=True) | |
| st.subheader("Portfolio Value and Ownership Percentage Change") | |
| st.markdown(""" | |
| **Explanation:** | |
| 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. | |
| """) | |
| fig2 = plot_portfolio_value_and_change_symbol(data) | |
| st.plotly_chart(fig2, use_container_width=True) | |
| st.subheader("Positions Activity") | |
| st.markdown(""" | |
| **Explanation:** | |
| 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. | |
| """) | |
| fig3 = plot_positions_activity_symbol(data) | |
| st.plotly_chart(fig3, use_container_width=True) | |
| st.subheader("Derivative Activity") | |
| st.markdown(""" | |
| **Explanation:** | |
| 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. | |
| """) | |
| fig4 = plot_derivative_activity_symbol(data) | |
| st.plotly_chart(fig4, use_container_width=True) | |
| st.subheader("Changes in Metrics") | |
| st.markdown(""" | |
| **Explanation:** | |
| This chart shows the changes in key metrics like the number of 13F shares and total investment. It highlights significant shifts in investment positions. | |
| """) | |
| fig5 = plot_changes_in_metrics_symbol(data) | |
| st.plotly_chart(fig5, use_container_width=True) | |
| st.subheader("Ownership Percent and Total Invested") | |
| st.markdown(""" | |
| **Explanation:** | |
| 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. | |
| """) | |
| fig6 = plot_ownership_and_investment_symbol(data) | |
| st.plotly_chart(fig6, use_container_width=True) | |
| else: | |
| st.error("No data found for the specified symbol.") | |
| # If data exists in session state and parameters match, display it without rerunning | |
| elif st.session_state.symbol_ownership_data and st.session_state.symbol_ownership_params.get('symbol') == symbol: | |
| data = st.session_state.symbol_ownership_data | |
| st.success("Displaying previously retrieved data.") | |
| st.dataframe(data, use_container_width=True) | |
| # Ensure the data is sorted by date | |
| data["date"] = pd.to_datetime(data["date"]) | |
| data = data.sort_values(by="date") | |
| # Plotting | |
| st.subheader("Investor Count and Ownership Percentage") | |
| st.markdown(""" | |
| **Explanation:** | |
| 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. | |
| """) | |
| fig1 = plot_investor_count_and_ownership(data) | |
| st.plotly_chart(fig1, use_container_width=True) | |
| st.subheader("Portfolio Value and Ownership Percentage Change") | |
| st.markdown(""" | |
| **Explanation:** | |
| 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. | |
| """) | |
| fig2 = plot_portfolio_value_and_change_symbol(data) | |
| st.plotly_chart(fig2, use_container_width=True) | |
| st.subheader("Positions Activity") | |
| st.markdown(""" | |
| **Explanation:** | |
| 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. | |
| """) | |
| fig3 = plot_positions_activity_symbol(data) | |
| st.plotly_chart(fig3, use_container_width=True) | |
| st.subheader("Derivative Activity") | |
| st.markdown(""" | |
| **Explanation:** | |
| 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. | |
| """) | |
| fig4 = plot_derivative_activity_symbol(data) | |
| st.plotly_chart(fig4, use_container_width=True) | |
| st.subheader("Changes in Metrics") | |
| st.markdown(""" | |
| **Explanation:** | |
| This chart shows the changes in key metrics like the number of 13F shares and total investment. It highlights significant shifts in investment positions. | |
| """) | |
| fig5 = plot_changes_in_metrics_symbol(data) | |
| st.plotly_chart(fig5, use_container_width=True) | |
| st.subheader("Ownership Percent and Total Invested") | |
| st.markdown(""" | |
| **Explanation:** | |
| 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. | |
| """) | |
| fig6 = plot_ownership_and_investment_symbol(data) | |
| st.plotly_chart(fig6, use_container_width=True) | |
| # Functions used in Page 4 | |
| def plot_investor_count_and_ownership(df): | |
| """ | |
| Plots investor count and ownership percentage. | |
| Args: | |
| df (pd.DataFrame): DataFrame containing symbol ownership data. | |
| Returns: | |
| plotly.graph_objects.Figure: Plotly figure object. | |
| """ | |
| fig = go.Figure() | |
| # Investors Holding Line | |
| fig.add_trace(go.Scatter( | |
| x=df["date"], | |
| y=df["investorsHolding"], | |
| mode='lines+markers', | |
| name="Investors Holding", | |
| yaxis="y1" | |
| )) | |
| # Ownership Percentage Line | |
| fig.add_trace(go.Scatter( | |
| x=df["date"], | |
| y=df["ownershipPercent"], | |
| mode='lines+markers', | |
| name="Ownership (%)", | |
| yaxis="y2" | |
| )) | |
| fig.update_layout( | |
| title="Investor Count and Ownership Percentage", | |
| xaxis_title="Date", | |
| yaxis=dict(title="Investors Holding"), | |
| yaxis2=dict(title="Ownership (%)", overlaying="y", side="right", tickformat=".2f"), | |
| legend=dict(title="Metrics"), | |
| height=500 | |
| ) | |
| return fig | |
| def plot_portfolio_value_and_change_symbol(df): | |
| """ | |
| Plots portfolio value and ownership percentage change. | |
| Args: | |
| df (pd.DataFrame): DataFrame containing symbol ownership data. | |
| Returns: | |
| plotly.graph_objects.Figure: Plotly figure object. | |
| """ | |
| fig = go.Figure() | |
| # Total Invested Line | |
| fig.add_trace(go.Scatter( | |
| x=df["date"], | |
| y=df["totalInvested"], | |
| mode='lines+markers', | |
| name="Total Invested", | |
| yaxis="y1" | |
| )) | |
| # Ownership Percentage Change Line | |
| fig.add_trace(go.Scatter( | |
| x=df["date"], | |
| y=df["ownershipPercentChange"], | |
| mode='lines+markers', | |
| name="Ownership Percent Change", | |
| yaxis="y2" | |
| )) | |
| fig.update_layout( | |
| title="Portfolio Value and Ownership Percentage Change", | |
| xaxis_title="Date", | |
| yaxis=dict(title="Total Invested ($)", tickformat="$,.0f"), | |
| yaxis2=dict(title="Ownership Percent Change (%)", overlaying="y", side="right", tickformat=".2f"), | |
| legend=dict(title="Metrics"), | |
| height=500 | |
| ) | |
| return fig | |
| def plot_positions_activity_symbol(df): | |
| """ | |
| Plots positions activity. | |
| Args: | |
| df (pd.DataFrame): DataFrame containing symbol ownership data. | |
| Returns: | |
| plotly.graph_objects.Figure: Plotly figure object. | |
| """ | |
| fig = go.Figure() | |
| # New and Closed Positions Lines | |
| fig.add_trace(go.Scatter( | |
| x=df["date"], | |
| y=df["newPositions"], | |
| mode='lines+markers', | |
| name="New Positions", | |
| yaxis="y1" | |
| )) | |
| fig.add_trace(go.Scatter( | |
| x=df["date"], | |
| y=df["closedPositions"], | |
| mode='lines+markers', | |
| name="Closed Positions", | |
| yaxis="y1" | |
| )) | |
| # Increased and Reduced Positions Lines | |
| fig.add_trace(go.Scatter( | |
| x=df["date"], | |
| y=df["increasedPositions"], | |
| mode='lines+markers', | |
| name="Increased Positions", | |
| yaxis="y2" | |
| )) | |
| fig.add_trace(go.Scatter( | |
| x=df["date"], | |
| y=df["reducedPositions"], | |
| mode='lines+markers', | |
| name="Reduced Positions", | |
| yaxis="y2" | |
| )) | |
| fig.update_layout( | |
| title="Positions Activity", | |
| xaxis_title="Date", | |
| yaxis=dict(title="New/Closed Positions"), | |
| yaxis2=dict(title="Increased/Reduced Positions", overlaying="y", side="right"), | |
| legend=dict(title="Metrics"), | |
| height=500 | |
| ) | |
| return fig | |
| def plot_derivative_activity_symbol(df): | |
| """ | |
| Plots derivative activity. | |
| Args: | |
| df (pd.DataFrame): DataFrame containing symbol ownership data. | |
| Returns: | |
| plotly.graph_objects.Figure: Plotly figure object. | |
| """ | |
| fig = go.Figure() | |
| # Total Calls and Puts Lines | |
| fig.add_trace(go.Scatter( | |
| x=df["date"], | |
| y=df["totalCalls"], | |
| mode='lines+markers', | |
| name="Total Calls", | |
| yaxis="y1" | |
| )) | |
| fig.add_trace(go.Scatter( | |
| x=df["date"], | |
| y=df["totalPuts"], | |
| mode='lines+markers', | |
| name="Total Puts", | |
| yaxis="y1" | |
| )) | |
| # Put/Call Ratio Line | |
| fig.add_trace(go.Scatter( | |
| x=df["date"], | |
| y=df["putCallRatio"], | |
| mode='lines+markers', | |
| name="Put/Call Ratio", | |
| yaxis="y2" | |
| )) | |
| fig.update_layout( | |
| title="Derivative Activity", | |
| xaxis_title="Date", | |
| yaxis=dict(title="Total Calls/Puts"), | |
| yaxis2=dict(title="Put/Call Ratio", overlaying="y", side="right", tickformat=".2f"), | |
| legend=dict(title="Metrics"), | |
| height=500 | |
| ) | |
| return fig | |
| def plot_changes_in_metrics_symbol(df): | |
| """ | |
| Plots changes in metrics. | |
| Args: | |
| df (pd.DataFrame): DataFrame containing symbol ownership data. | |
| Returns: | |
| plotly.graph_objects.Figure: Plotly figure object. | |
| """ | |
| fig = go.Figure() | |
| # Change in Number of 13F Shares | |
| fig.add_trace(go.Scatter( | |
| x=df["date"], | |
| y=df["numberOf13FsharesChange"], | |
| mode='lines+markers', | |
| name="Change in 13F Shares", | |
| yaxis="y1" | |
| )) | |
| # Change in Total Investment | |
| fig.add_trace(go.Scatter( | |
| x=df["date"], | |
| y=df["totalInvestedChange"], | |
| mode='lines+markers', | |
| name="Change in Total Investment", | |
| yaxis="y2" | |
| )) | |
| fig.update_layout( | |
| title="Changes in Metrics", | |
| xaxis_title="Date", | |
| yaxis=dict(title="Change in 13F Shares"), | |
| yaxis2=dict(title="Change in Total Investment ($)", overlaying="y", side="right", tickformat="$,.0f"), | |
| legend=dict(title="Metrics"), | |
| height=500 | |
| ) | |
| return fig | |
| def plot_ownership_and_investment_symbol(df): | |
| """ | |
| Plots ownership percent and total invested. | |
| Args: | |
| df (pd.DataFrame): DataFrame containing symbol ownership data. | |
| Returns: | |
| plotly.graph_objects.Figure: Plotly figure object. | |
| """ | |
| fig = go.Figure() | |
| # Ownership Percent | |
| fig.add_trace(go.Scatter( | |
| x=df["date"], | |
| y=df["ownershipPercent"], | |
| mode='lines+markers', | |
| name="Ownership Percent", | |
| yaxis="y1" | |
| )) | |
| # Total Invested | |
| fig.add_trace(go.Scatter( | |
| x=df["date"], | |
| y=df["totalInvested"], | |
| mode='lines+markers', | |
| name="Total Invested", | |
| yaxis="y2" | |
| )) | |
| fig.update_layout( | |
| title="Ownership Percent and Total Invested", | |
| xaxis_title="Date", | |
| yaxis=dict(title="Ownership Percent (%)"), | |
| yaxis2=dict(title="Total Invested ($)", overlaying="y", side="right", tickformat="$,.0f"), | |
| legend=dict(title="Metrics"), | |
| height=500 | |
| ) | |
| return fig | |
| # Function for Main Navigation | |
| def main(): | |
| st.sidebar.title("Input Parameters") | |
| with st.sidebar.expander("Navigation", expanded=True): | |
| page = st.radio("Go to", ["Portfolio Allocation", "Investor Performance", "Symbol Ownership","CIK List"]) | |
| if page == "CIK List": | |
| page1() | |
| elif page == "Portfolio Allocation": | |
| page2() | |
| elif page == "Investor Performance": | |
| page3() | |
| elif page == "Symbol Ownership": | |
| page4() | |
| if __name__ == "__main__": | |
| main() | |
| hide_streamlit_style = """ | |
| <style> | |
| #MainMenu {visibility: hidden;} | |
| footer {visibility: hidden;} | |
| </style> | |
| """ | |
| st.markdown(hide_streamlit_style, unsafe_allow_html=True) |