Spaces:
Sleeping
Sleeping
| import streamlit as st | |
| import pandas as pd | |
| import requests | |
| import plotly.express as px | |
| from datetime import datetime, timedelta | |
| import textwrap | |
| import os | |
| API_KEY = os.getenv("FMP_API_KEY") | |
| # Set wide page layout | |
| st.set_page_config(page_title="Analyst Recommendations", layout="wide") | |
| # Sidebar: Global ticker and page navigation | |
| st.sidebar.header("Inputs") | |
| with st.sidebar.expander("Ticker and Page Settings", expanded=True): | |
| ticker = st.text_input( | |
| "Enter Ticker Symbol", | |
| value="AAPL", | |
| help="Input the stock ticker symbol (e.g., AAPL, MSFT)." | |
| ) | |
| page = st.radio( | |
| "Select Page", | |
| ["Historical Ratings", "Recommendations"], | |
| help="Choose which analysis page to view." | |
| ) | |
| # Reset stored results if ticker changes | |
| if "ticker" not in st.session_state or st.session_state.ticker != ticker: | |
| st.session_state.ticker = ticker | |
| st.session_state.historical_data = None | |
| st.session_state.analyst_data = None | |
| st.session_state.run_pressed_hist = False | |
| st.session_state.run_pressed_analyst = False | |
| # Cached function for historical data | |
| def load_historical_data(ticker): | |
| try: | |
| url = f"https://financialmodelingprep.com/api/v3/historical-rating/{ticker}?apikey={API_KEY}" | |
| response = requests.get(url) | |
| if response.status_code != 200: | |
| st.error("Error retrieving historical data.") | |
| return None | |
| data = response.json() | |
| df = pd.DataFrame(data) | |
| # Define required columns. | |
| req_cols = [ | |
| 'date', 'rating', 'ratingScore', 'ratingRecommendation', 'ratingDetailsDCFScore', | |
| 'ratingDetailsROEScore', 'ratingDetailsROERecommendation', | |
| 'ratingDetailsROAScore', 'ratingDetailsROARecommendation', | |
| 'ratingDetailsDEScore', 'ratingDetailsDERecommendation', | |
| 'ratingDetailsPEScore', 'ratingDetailsPERecommendation', | |
| 'ratingDetailsPBScore', 'ratingDetailsPBRecommendation' | |
| ] | |
| # Use alternative column if available. | |
| if 'ratingDetailsROCFRecommendation' in df.columns: | |
| req_cols.insert(4, 'ratingDetailsROCFRecommendation') | |
| else: | |
| req_cols.insert(4, 'ratingDetailsDCFRecommendation') | |
| df = df[req_cols] | |
| df['date'] = pd.to_datetime(df['date']) | |
| df = df.sort_values('date') | |
| return df | |
| except Exception: | |
| st.error("An error occurred while loading historical data.") | |
| return None | |
| # Cached function for analyst recommendations data | |
| def load_analyst_data(ticker): | |
| try: | |
| url = f"https://financialmodelingprep.com/api/v3/analyst-stock-recommendations/{ticker}?apikey={API_KEY}" | |
| response = requests.get(url) | |
| if response.status_code != 200: | |
| st.error("Error retrieving analyst data.") | |
| return None | |
| data = response.json() | |
| if isinstance(data, dict): | |
| data = [data] | |
| df = pd.DataFrame(data) | |
| df['date'] = pd.to_datetime(df['date']) | |
| df = df.sort_values('date') | |
| return df | |
| except Exception: | |
| st.error("An error occurred while loading analyst data.") | |
| return None | |
| # Main area explanation | |
| #st.title("Analysts Recommendations") | |
| #st.write("This app displays historical rating scores and analyst recommendations for the specified ticker.") | |
| # PAGE: Historical Ratings | |
| if page == "Historical Ratings": | |
| st.header("Historical Ratings") | |
| # Sidebar inputs for this page | |
| st.write("Below are a series of bar charts that show historical trends in key financial metrics.") | |
| with st.expander("Category Description", expanded=False): | |
| description = textwrap.dedent(""" | |
| - **Overall Rating Score**: Reflects the general analyst rating, summarizing overall performance. | |
| - **DCF Score**: Represents the discounted cash flow valuation, which estimates a stock's intrinsic value. | |
| - **ROE Score**: Measures return on equity to assess how efficiently a company uses shareholder funds. | |
| - **ROA Score**: Indicates return on assets to gauge the effectiveness of asset use in generating profits. | |
| - **DE Score**: Shows the debt-to-equity ratio, highlighting the financial leverage of the company. | |
| - **PE Score**: Provides the price-to-earnings ratio, indicating market valuation relative to earnings. | |
| - **PB Score**: Measures the price-to-book ratio to assess if the stock is undervalued compared to its book value. | |
| Each chart displays the numerical score with a text recommendation. Colors denote recommendations like "Strong Buy", "Buy", "Neutral", "Sell", and "Strong Sell". | |
| """) | |
| st.markdown(description) | |
| default_date = datetime.today() - timedelta(days=365) | |
| with st.sidebar.expander("Date Settings", expanded=True): | |
| start_date = st.date_input( | |
| "Start Date", | |
| value=default_date, | |
| help="Select a start date for filtering historical data." | |
| ) | |
| # Place run button below the start date input | |
| if st.sidebar.button("Run Analysis", key="hist_run_button"): | |
| st.session_state.run_pressed_hist = True | |
| if st.session_state.run_pressed_hist: | |
| # Load data if not already loaded | |
| if st.session_state.historical_data is None: | |
| st.session_state.historical_data = load_historical_data(ticker) | |
| df_hist = st.session_state.historical_data | |
| if df_hist is not None: | |
| # Filter data based on start date. | |
| df_filtered = df_hist[df_hist['date'] >= pd.to_datetime(start_date)] | |
| #st.subheader(f"Historical Ratings for {ticker}") | |
| #st.write("Bar charts below show various score metrics over time.") | |
| recommendation_colors = { | |
| "Strong Buy": "green", | |
| "Buy": "lightgreen", | |
| "Neutral": "orange", | |
| "Sell": "lightcoral", | |
| "Strong Sell": "red" | |
| } | |
| categories = [ | |
| 'ratingScore', 'ratingDetailsDCFScore', 'ratingDetailsROEScore', | |
| 'ratingDetailsROAScore', 'ratingDetailsDEScore', 'ratingDetailsPEScore', | |
| 'ratingDetailsPBScore' | |
| ] | |
| titles = [ | |
| 'Overall Rating Score', 'DCF Score', 'ROE Score', | |
| 'ROA Score', 'DE Score', 'PE Score', 'PB Score' | |
| ] | |
| for category, title in zip(categories, titles): | |
| recommendation_col = category.replace("Score", "Recommendation") | |
| st.markdown(f"**{title} Chart**") | |
| #st.write("This chart shows the score over time with its associated recommendation.") | |
| try: | |
| fig = px.bar( | |
| df_filtered, | |
| x='date', | |
| y=category, | |
| text=category, | |
| labels={'date': 'Date', category: 'Score'}, | |
| title=title, | |
| color=recommendation_col, | |
| color_discrete_map=recommendation_colors, | |
| custom_data=['rating', recommendation_col] | |
| ) | |
| fig.update_traces( | |
| texttemplate="%{text}<br>%{customdata[0]} (%{customdata[1]})", | |
| textposition='outside', | |
| hovertemplate="<b>Date</b>: %{x}<br><b>Score</b>: %{y}<br><b>Rating</b>: %{customdata[0]}<br><b>Recommendation</b>: %{customdata[1]}<extra></extra>" | |
| ) | |
| st.plotly_chart(fig, use_container_width=True) | |
| except Exception: | |
| st.error("Error displaying the chart. Please check the data and inputs.") | |
| with st.expander("Show Detailed Historical Data"): | |
| st.dataframe(df_filtered) | |
| else: | |
| st.info("Press 'Run Analysis' in the sidebar to load historical ratings data.") | |
| # PAGE: Analyst Recommendations | |
| elif page == "Recommendations": | |
| st.header("Analyst Recommendations") | |
| st.write("This section presents the monthly analyst recommendations for the selected ticker. The stacked bar chart aggregates various recommendation types over time.") | |
| with st.expander("Category Description", expanded=False): | |
| description = textwrap.dedent(""" | |
| - **Strong Buy**: Indicates that analysts are very confident the stock will perform strongly. | |
| - **Buy**: Reflects a positive outlook from analysts regarding future performance. | |
| - **Hold**: Suggests that analysts expect the stock to maintain its current performance. | |
| - **Sell**: Signals a negative outlook, indicating the stock may underperform. | |
| - **Strong Sell**: Represents a very bearish sentiment, with analysts expecting significant underperformance. | |
| The chart lets you observe shifts in market sentiment over time and compare the prevalence of each recommendation type. | |
| """) | |
| st.markdown(description) | |
| # No additional page-specific inputs; thus, no expander is shown. | |
| if st.sidebar.button("Run Analysis", key="analyst_run_button"): | |
| st.session_state.run_pressed_analyst = True | |
| if st.session_state.run_pressed_analyst: | |
| if st.session_state.analyst_data is None: | |
| st.session_state.analyst_data = load_analyst_data(ticker) | |
| df_analyst = st.session_state.analyst_data | |
| if df_analyst is not None: | |
| st.subheader(f"Analyst Recommendations for {ticker}") | |
| st.write("The stacked bar chart below shows monthly analyst recommendations.") | |
| rating_cols = [ | |
| "analystRatingsStrongBuy", | |
| "analystRatingsbuy", | |
| "analystRatingsHold", | |
| "analystRatingsSell", | |
| "analystRatingsStrongSell" | |
| ] | |
| df_melted = pd.melt( | |
| df_analyst, | |
| id_vars=["date"], | |
| value_vars=rating_cols, | |
| var_name="RatingType", | |
| value_name="Count" | |
| ) | |
| color_map = { | |
| "analystRatingsStrongBuy": "green", | |
| "analystRatingsbuy": "lightgreen", | |
| "analystRatingsHold": "orange", | |
| "analystRatingsSell": "lightcoral", | |
| "analystRatingsStrongSell": "red" | |
| } | |
| rating_order = [ | |
| "analystRatingsStrongBuy", | |
| "analystRatingsbuy", | |
| "analystRatingsHold", | |
| "analystRatingsSell", | |
| "analystRatingsStrongSell" | |
| ] | |
| try: | |
| fig2 = px.bar( | |
| df_melted, | |
| x="date", | |
| y="Count", | |
| color="RatingType", | |
| text="Count", | |
| title=f"Monthly Analyst Recommendations for {ticker.upper()}", | |
| color_discrete_map=color_map, | |
| category_orders={"RatingType": rating_order}, | |
| custom_data=["RatingType"], | |
| labels={"date": "Date", "Count": "Number of Recommendations", "RatingType": "Recommendation Type"} | |
| ) | |
| fig2.update_layout(barmode="stack") | |
| fig2.update_traces( | |
| texttemplate="%{text}", | |
| textposition="inside", | |
| hovertemplate="<b>Date</b>: %{x}<br><b>Count</b>: %{y}<br><b>Rating Type</b>: %{customdata[0]}<extra></extra>" | |
| ) | |
| st.plotly_chart(fig2, use_container_width=True) | |
| except Exception: | |
| st.error("Error displaying the chart. Please check the data and inputs.") | |
| with st.expander("Show Detailed Analyst Data"): | |
| st.dataframe(df_analyst) | |
| else: | |
| st.info("Press 'Run Analysis' in the sidebar to load analyst recommendations data.") | |
| # Hide default Streamlit style | |
| st.markdown( | |
| """ | |
| <style> | |
| #MainMenu {visibility: hidden;} | |
| footer {visibility: hidden;} | |
| </style> | |
| """, | |
| unsafe_allow_html=True | |
| ) | |