File size: 12,211 Bytes
f6764ad
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c6925d9
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
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
@st.cache_data(show_spinner=False)
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
@st.cache_data(show_spinner=False)
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
)