QuantumLearner commited on
Commit
adfd04e
·
verified ·
1 Parent(s): 5e6be38

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +79 -53
app.py CHANGED
@@ -5,11 +5,15 @@ import plotly.graph_objects as go
5
  from plotly.subplots import make_subplots
6
  import os
7
 
8
- API_KEY = os.getenv("FMP_API_KEY")
 
 
9
 
 
10
  MAX_PAGES = 5
11
  DEFAULT_TOP_N = 10
12
 
 
13
  if "historical_run" not in st.session_state:
14
  st.session_state.historical_run = False
15
  if "trending_run" not in st.session_state:
@@ -29,24 +33,32 @@ if "change_top_n" not in st.session_state:
29
 
30
  @st.cache_data(show_spinner=False)
31
  def get_historical_sentiment(ticker, pages=MAX_PAGES):
 
32
  frames = []
33
  for page in range(pages):
34
  url = (
35
- f"https://financialmodelingprep.com/api/v4/historical/social-sentiment?"
36
- f"symbol={ticker}&page={page}&apikey={API_KEY}"
37
  )
38
- r = requests.get(url)
39
- r.raise_for_status()
40
- data = r.json()
 
 
 
 
 
41
  if not data:
42
  break
43
  df = pd.DataFrame(data)
44
  frames.append(df)
 
45
  if frames:
46
  full_df = pd.concat(frames, ignore_index=True)
47
  full_df["date"] = pd.to_datetime(full_df["date"])
48
  full_df.sort_values("date", inplace=True)
49
  return full_df
 
50
  return pd.DataFrame()
51
 
52
  def plot_dual_axes(
@@ -55,6 +67,7 @@ def plot_dual_axes(
55
  left_color="red", right_color="blue",
56
  date_range=None
57
  ):
 
58
  fig = make_subplots(specs=[[{"secondary_y": True}]])
59
  fig.add_trace(
60
  go.Scatter(
@@ -106,17 +119,17 @@ def plot_dual_axes(
106
  return fig
107
 
108
  def run_historical_sentiment(ticker):
109
- st.write(
110
- "This section shows how Stocktwits and Twitter sentiment changed over time."
111
- )
112
  df = get_historical_sentiment(ticker)
113
  if df.empty:
114
- st.error("No data found for " + ticker)
115
  return
 
116
  common_range = [df["date"].min(), df["date"].max()]
117
 
118
  st.subheader(f"{ticker} Sentiment Scores")
119
- st.write("This plot compares the sentiment scores from Stocktwits and Twitter.")
120
  fig1 = plot_dual_axes(
121
  df, "date", "stocktwitsSentiment", "twitterSentiment",
122
  f"{ticker} Sentiment Scores",
@@ -126,7 +139,7 @@ def run_historical_sentiment(ticker):
126
  st.plotly_chart(fig1, use_container_width=True)
127
 
128
  st.subheader(f"{ticker} Posts")
129
- st.write("This plot compares how many posts appear on each platform.")
130
  fig2 = plot_dual_axes(
131
  df, "date", "stocktwitsPosts", "twitterPosts",
132
  f"{ticker} Posts",
@@ -136,7 +149,7 @@ def run_historical_sentiment(ticker):
136
  st.plotly_chart(fig2, use_container_width=True)
137
 
138
  st.subheader(f"{ticker} Comments")
139
- st.write("This plot compares the number of comments on Stocktwits and Twitter.")
140
  fig3 = plot_dual_axes(
141
  df, "date", "stocktwitsComments", "twitterComments",
142
  f"{ticker} Comments",
@@ -146,7 +159,7 @@ def run_historical_sentiment(ticker):
146
  st.plotly_chart(fig3, use_container_width=True)
147
 
148
  st.subheader(f"{ticker} Likes")
149
- st.write("This plot compares how many likes each platform received.")
150
  fig4 = plot_dual_axes(
151
  df, "date", "stocktwitsLikes", "twitterLikes",
152
  f"{ticker} Likes",
@@ -156,7 +169,7 @@ def run_historical_sentiment(ticker):
156
  st.plotly_chart(fig4, use_container_width=True)
157
 
158
  st.subheader(f"{ticker} Impressions")
159
- st.write("This plot compares the total impressions from Stocktwits and Twitter.")
160
  fig5 = plot_dual_axes(
161
  df, "date", "stocktwitsImpressions", "twitterImpressions",
162
  f"{ticker} Impressions",
@@ -171,23 +184,31 @@ def run_historical_sentiment(ticker):
171
 
172
  @st.cache_data(show_spinner=False)
173
  def fetch_trending_sentiment(sentiment_type="bullish", source="stocktwits"):
 
174
  url = (
175
- f"https://financialmodelingprep.com/api/v4/social-sentiments/trending?"
176
- f"type={sentiment_type}&source={source}&apikey={API_KEY}"
177
  )
178
- response = requests.get(url)
179
- response.raise_for_status()
180
- data = response.json()
 
 
 
 
181
  if not data:
182
  return pd.DataFrame()
 
183
  df = pd.DataFrame(data)
184
  df = df.sort_values("rank").reset_index(drop=True)
185
  return df
186
 
187
  def plot_trending(df, title, top_n):
 
188
  if df.empty:
189
  st.error("No data available to plot.")
190
  return
 
191
  df = df.head(top_n)
192
  fig = go.Figure()
193
  fig.add_trace(
@@ -250,19 +271,17 @@ def plot_trending(df, title, top_n):
250
  st.plotly_chart(fig, use_container_width=True)
251
 
252
  def run_trending_sentiment(top_n):
253
- st.write(
254
- "This section ranks stocks by bullish and bearish sentiment. "
255
- "Two charts show the sentiment versus last known sentiment."
256
- )
257
- st.write("## Bullish Trending Sentiment")
258
- st.write("The first chart shows the top bullish stocks and how sentiment changed from the last update.")
259
  bullish_df = fetch_trending_sentiment(sentiment_type="bullish", source="stocktwits")
260
- plot_trending(bullish_df, title="Bullish Trending Sentiment", top_n=top_n)
261
 
262
- st.write("## Bearish Trending Sentiment")
263
- st.write("The second chart shows the top bearish stocks and how sentiment changed from the last update.")
264
  bearish_df = fetch_trending_sentiment(sentiment_type="bearish", source="stocktwits")
265
- plot_trending(bearish_df, title="Bearish Trending Sentiment", top_n=top_n)
266
 
267
  #############################
268
  # PAGE 3: Sentiment Change
@@ -270,23 +289,31 @@ def run_trending_sentiment(top_n):
270
 
271
  @st.cache_data(show_spinner=False)
272
  def fetch_change_sentiment(sentiment_type="bullish", source="stocktwits"):
 
273
  url = (
274
- f"https://financialmodelingprep.com/api/v4/social-sentiments/change?"
275
- f"type={sentiment_type}&source={source}&apikey={API_KEY}"
276
  )
277
- response = requests.get(url)
278
- response.raise_for_status()
279
- data = response.json()
 
 
 
 
280
  if not data:
281
  return pd.DataFrame()
 
282
  df = pd.DataFrame(data)
283
  df = df.sort_values("rank").reset_index(drop=True)
284
  return df
285
 
286
  def plot_change_sentiment(df, title, top_n):
 
287
  if df.empty:
288
  st.error("No data available to plot.")
289
  return
 
290
  df = df.head(top_n)
291
  fig = go.Figure()
292
  fig.add_trace(
@@ -349,17 +376,15 @@ def plot_change_sentiment(df, title, top_n):
349
  st.plotly_chart(fig, use_container_width=True)
350
 
351
  def run_change_sentiment(top_n):
352
- st.write(
353
- "This section shows how sentiment scores have changed. "
354
- "We compare current sentiment to the change in sentiment."
355
- )
356
  st.write("## Bullish Sentiment Change")
357
- st.write("This chart displays current bullish sentiment and how much it has changed recently.")
358
  bullish_df = fetch_change_sentiment(sentiment_type="bullish", source="stocktwits")
359
  plot_change_sentiment(bullish_df, title="Bullish Sentiment Change", top_n=top_n)
360
 
361
  st.write("## Bearish Sentiment Change")
362
- st.write("This chart displays current bearish sentiment and how much it has changed recently.")
363
  bearish_df = fetch_change_sentiment(sentiment_type="bearish", source="stocktwits")
364
  plot_change_sentiment(bearish_df, title="Bearish Sentiment Change", top_n=top_n)
365
 
@@ -369,26 +394,26 @@ def run_change_sentiment(top_n):
369
 
370
  def main():
371
  st.set_page_config(page_title="Social Sentiment Analysis", layout="wide")
372
- st.title("Trending Social Sentiment")
373
  st.write(
374
- "This tool offers three analyses of social sentiment. "
375
- "They cover historical, trending, and changing sentiment from Stocktwits and Twitter. "
376
  )
377
 
378
  with st.sidebar.expander("Navigation and Options", expanded=True):
379
  page = st.radio(
380
  "Select Analysis Page",
381
  ("Historical Sentiment", "Trending Sentiment", "Sentiment Change"),
382
- help="Choose the type of sentiment analysis you want."
383
  )
384
 
385
  if page == "Historical Sentiment":
386
  ticker = st.text_input(
387
  "Ticker Symbol",
388
  value=st.session_state.historical_ticker,
389
- help="Enter the ticker symbol for historical sentiment."
390
  )
391
- if st.button("Run Historical Sentiment Analysis"):
392
  st.session_state.historical_run = True
393
  st.session_state.historical_ticker = ticker
394
 
@@ -398,9 +423,9 @@ def main():
398
  min_value=1,
399
  max_value=100,
400
  value=st.session_state.trending_top_n,
401
- help="Pick how many stocks to show."
402
  )
403
- if st.button("Run Trending Sentiment Analysis"):
404
  st.session_state.trending_run = True
405
  st.session_state.trending_top_n = top_n
406
 
@@ -410,9 +435,9 @@ def main():
410
  min_value=1,
411
  max_value=100,
412
  value=st.session_state.change_top_n,
413
- help="Pick how many stocks to show."
414
  )
415
- if st.button("Run Sentiment Change Analysis"):
416
  st.session_state.change_run = True
417
  st.session_state.change_top_n = top_n
418
 
@@ -428,18 +453,19 @@ def main():
428
  if st.session_state.trending_run:
429
  run_trending_sentiment(st.session_state.trending_top_n)
430
  else:
431
- st.info("Select a top N and run the analysis.")
432
 
433
  elif page == "Sentiment Change":
434
  st.header("Social Sentiment Change")
435
  if st.session_state.change_run:
436
  run_change_sentiment(st.session_state.change_top_n)
437
  else:
438
- st.info("Select a top N and run the analysis.")
439
 
440
  if __name__ == "__main__":
441
  main()
442
 
 
443
  hide_streamlit_style = """
444
  <style>
445
  #MainMenu {visibility: hidden;}
 
5
  from plotly.subplots import make_subplots
6
  import os
7
 
8
+ # Use an environment variable for the API key.
9
+ # This will not expose the data source.
10
+ DATA_API_KEY = os.getenv("DATA_API_KEY")
11
 
12
+ # Adjust pages and defaults as needed.
13
  MAX_PAGES = 5
14
  DEFAULT_TOP_N = 10
15
 
16
+ # Keep track of user selections in session state.
17
  if "historical_run" not in st.session_state:
18
  st.session_state.historical_run = False
19
  if "trending_run" not in st.session_state:
 
33
 
34
  @st.cache_data(show_spinner=False)
35
  def get_historical_sentiment(ticker, pages=MAX_PAGES):
36
+ """Fetch historical social sentiment data for a given ticker."""
37
  frames = []
38
  for page in range(pages):
39
  url = (
40
+ f"https://api.example.com/v4/historical/social-sentiment"
41
+ f"?symbol={ticker}&page={page}&apikey={DATA_API_KEY}"
42
  )
43
+ try:
44
+ response = requests.get(url)
45
+ response.raise_for_status()
46
+ data = response.json()
47
+ except Exception:
48
+ # Failover: Do not mention the data source
49
+ return pd.DataFrame()
50
+
51
  if not data:
52
  break
53
  df = pd.DataFrame(data)
54
  frames.append(df)
55
+
56
  if frames:
57
  full_df = pd.concat(frames, ignore_index=True)
58
  full_df["date"] = pd.to_datetime(full_df["date"])
59
  full_df.sort_values("date", inplace=True)
60
  return full_df
61
+
62
  return pd.DataFrame()
63
 
64
  def plot_dual_axes(
 
67
  left_color="red", right_color="blue",
68
  date_range=None
69
  ):
70
+ """Create a dual-axis line chart."""
71
  fig = make_subplots(specs=[[{"secondary_y": True}]])
72
  fig.add_trace(
73
  go.Scatter(
 
119
  return fig
120
 
121
  def run_historical_sentiment(ticker):
122
+ """Display the historical sentiment plots."""
123
+ st.write("This section shows how social sentiment changed over time.")
 
124
  df = get_historical_sentiment(ticker)
125
  if df.empty:
126
+ st.error("No data found.")
127
  return
128
+
129
  common_range = [df["date"].min(), df["date"].max()]
130
 
131
  st.subheader(f"{ticker} Sentiment Scores")
132
+ st.write("Compares sentiment scores from two platforms.")
133
  fig1 = plot_dual_axes(
134
  df, "date", "stocktwitsSentiment", "twitterSentiment",
135
  f"{ticker} Sentiment Scores",
 
139
  st.plotly_chart(fig1, use_container_width=True)
140
 
141
  st.subheader(f"{ticker} Posts")
142
+ st.write("Shows how many posts appear on each platform.")
143
  fig2 = plot_dual_axes(
144
  df, "date", "stocktwitsPosts", "twitterPosts",
145
  f"{ticker} Posts",
 
149
  st.plotly_chart(fig2, use_container_width=True)
150
 
151
  st.subheader(f"{ticker} Comments")
152
+ st.write("Shows the number of comments on each platform.")
153
  fig3 = plot_dual_axes(
154
  df, "date", "stocktwitsComments", "twitterComments",
155
  f"{ticker} Comments",
 
159
  st.plotly_chart(fig3, use_container_width=True)
160
 
161
  st.subheader(f"{ticker} Likes")
162
+ st.write("Shows how many likes each platform received.")
163
  fig4 = plot_dual_axes(
164
  df, "date", "stocktwitsLikes", "twitterLikes",
165
  f"{ticker} Likes",
 
169
  st.plotly_chart(fig4, use_container_width=True)
170
 
171
  st.subheader(f"{ticker} Impressions")
172
+ st.write("Shows the total impressions on each platform.")
173
  fig5 = plot_dual_axes(
174
  df, "date", "stocktwitsImpressions", "twitterImpressions",
175
  f"{ticker} Impressions",
 
184
 
185
  @st.cache_data(show_spinner=False)
186
  def fetch_trending_sentiment(sentiment_type="bullish", source="stocktwits"):
187
+ """Fetch trending sentiment data from an external service."""
188
  url = (
189
+ f"https://api.example.com/v4/social-sentiments/trending"
190
+ f"?type={sentiment_type}&source={source}&apikey={DATA_API_KEY}"
191
  )
192
+ try:
193
+ response = requests.get(url)
194
+ response.raise_for_status()
195
+ data = response.json()
196
+ except Exception:
197
+ return pd.DataFrame()
198
+
199
  if not data:
200
  return pd.DataFrame()
201
+
202
  df = pd.DataFrame(data)
203
  df = df.sort_values("rank").reset_index(drop=True)
204
  return df
205
 
206
  def plot_trending(df, title, top_n):
207
+ """Plot the top trending sentiment data."""
208
  if df.empty:
209
  st.error("No data available to plot.")
210
  return
211
+
212
  df = df.head(top_n)
213
  fig = go.Figure()
214
  fig.add_trace(
 
271
  st.plotly_chart(fig, use_container_width=True)
272
 
273
  def run_trending_sentiment(top_n):
274
+ """Display trending sentiment data."""
275
+ st.write("This section ranks stocks by bullish and bearish sentiment.")
276
+ st.write("## Bullish Trending")
277
+ st.write("Shows the top bullish symbols and last known sentiment.")
 
 
278
  bullish_df = fetch_trending_sentiment(sentiment_type="bullish", source="stocktwits")
279
+ plot_trending(bullish_df, title="Bullish Trending", top_n=top_n)
280
 
281
+ st.write("## Bearish Trending")
282
+ st.write("Shows the top bearish symbols and last known sentiment.")
283
  bearish_df = fetch_trending_sentiment(sentiment_type="bearish", source="stocktwits")
284
+ plot_trending(bearish_df, title="Bearish Trending", top_n=top_n)
285
 
286
  #############################
287
  # PAGE 3: Sentiment Change
 
289
 
290
  @st.cache_data(show_spinner=False)
291
  def fetch_change_sentiment(sentiment_type="bullish", source="stocktwits"):
292
+ """Fetch sentiment change data."""
293
  url = (
294
+ f"https://api.example.com/v4/social-sentiments/change"
295
+ f"?type={sentiment_type}&source={source}&apikey={DATA_API_KEY}"
296
  )
297
+ try:
298
+ response = requests.get(url)
299
+ response.raise_for_status()
300
+ data = response.json()
301
+ except Exception:
302
+ return pd.DataFrame()
303
+
304
  if not data:
305
  return pd.DataFrame()
306
+
307
  df = pd.DataFrame(data)
308
  df = df.sort_values("rank").reset_index(drop=True)
309
  return df
310
 
311
  def plot_change_sentiment(df, title, top_n):
312
+ """Plot the top sentiment change data."""
313
  if df.empty:
314
  st.error("No data available to plot.")
315
  return
316
+
317
  df = df.head(top_n)
318
  fig = go.Figure()
319
  fig.add_trace(
 
376
  st.plotly_chart(fig, use_container_width=True)
377
 
378
  def run_change_sentiment(top_n):
379
+ """Show how sentiment scores have changed."""
380
+ st.write("This section shows how sentiment scores have shifted.")
 
 
381
  st.write("## Bullish Sentiment Change")
382
+ st.write("Current bullish sentiment and its recent change.")
383
  bullish_df = fetch_change_sentiment(sentiment_type="bullish", source="stocktwits")
384
  plot_change_sentiment(bullish_df, title="Bullish Sentiment Change", top_n=top_n)
385
 
386
  st.write("## Bearish Sentiment Change")
387
+ st.write("Current bearish sentiment and its recent change.")
388
  bearish_df = fetch_change_sentiment(sentiment_type="bearish", source="stocktwits")
389
  plot_change_sentiment(bearish_df, title="Bearish Sentiment Change", top_n=top_n)
390
 
 
394
 
395
  def main():
396
  st.set_page_config(page_title="Social Sentiment Analysis", layout="wide")
397
+ st.title("Social Sentiment Analysis")
398
  st.write(
399
+ "This tool offers three analyses: "
400
+ "Historical, Trending, and Changing sentiment from two social platforms."
401
  )
402
 
403
  with st.sidebar.expander("Navigation and Options", expanded=True):
404
  page = st.radio(
405
  "Select Analysis Page",
406
  ("Historical Sentiment", "Trending Sentiment", "Sentiment Change"),
407
+ help="Pick which analysis you want."
408
  )
409
 
410
  if page == "Historical Sentiment":
411
  ticker = st.text_input(
412
  "Ticker Symbol",
413
  value=st.session_state.historical_ticker,
414
+ help="Type a ticker symbol."
415
  )
416
+ if st.button("Run Historical Analysis"):
417
  st.session_state.historical_run = True
418
  st.session_state.historical_ticker = ticker
419
 
 
423
  min_value=1,
424
  max_value=100,
425
  value=st.session_state.trending_top_n,
426
+ help="Choose how many symbols to show."
427
  )
428
+ if st.button("Run Trending Analysis"):
429
  st.session_state.trending_run = True
430
  st.session_state.trending_top_n = top_n
431
 
 
435
  min_value=1,
436
  max_value=100,
437
  value=st.session_state.change_top_n,
438
+ help="Choose how many symbols to show."
439
  )
440
+ if st.button("Run Change Analysis"):
441
  st.session_state.change_run = True
442
  st.session_state.change_top_n = top_n
443
 
 
453
  if st.session_state.trending_run:
454
  run_trending_sentiment(st.session_state.trending_top_n)
455
  else:
456
+ st.info("Pick a top N and run the analysis.")
457
 
458
  elif page == "Sentiment Change":
459
  st.header("Social Sentiment Change")
460
  if st.session_state.change_run:
461
  run_change_sentiment(st.session_state.change_top_n)
462
  else:
463
+ st.info("Pick a top N and run the analysis.")
464
 
465
  if __name__ == "__main__":
466
  main()
467
 
468
+ # Hide Streamlit's default style elements
469
  hide_streamlit_style = """
470
  <style>
471
  #MainMenu {visibility: hidden;}