Spaces:
Running
Running
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
)
|