QuantumLearner commited on
Commit
7b73ee2
·
verified ·
1 Parent(s): ef842a0

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +440 -0
app.py ADDED
@@ -0,0 +1,440 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import requests
3
+ import pandas as pd
4
+ 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
+ # Global constants (not exposed to the frontend)
11
+ MAX_PAGES = 5
12
+ DEFAULT_TOP_N = 10
13
+
14
+ # Initialize session state variables if not already set
15
+ if "historical_run" not in st.session_state:
16
+ st.session_state.historical_run = False
17
+ if "trending_run" not in st.session_state:
18
+ st.session_state.trending_run = False
19
+ if "change_run" not in st.session_state:
20
+ st.session_state.change_run = False
21
+ if "historical_ticker" not in st.session_state:
22
+ st.session_state.historical_ticker = "AAPL"
23
+ if "trending_top_n" not in st.session_state:
24
+ st.session_state.trending_top_n = DEFAULT_TOP_N
25
+ if "change_top_n" not in st.session_state:
26
+ st.session_state.change_top_n = DEFAULT_TOP_N
27
+
28
+ #############################
29
+ # PAGE 1: Historical Sentiment
30
+ #############################
31
+
32
+ @st.cache_data(show_spinner=False)
33
+ def get_historical_sentiment(ticker, pages=MAX_PAGES):
34
+ frames = []
35
+ for page in range(pages):
36
+ url = (
37
+ f"https://financialmodelingprep.com/api/v4/historical/social-sentiment?"
38
+ f"symbol={ticker}&page={page}&apikey={API_KEY}"
39
+ )
40
+ r = requests.get(url)
41
+ r.raise_for_status()
42
+ data = r.json()
43
+ if not data:
44
+ break
45
+ df = pd.DataFrame(data)
46
+ frames.append(df)
47
+ if frames:
48
+ full_df = pd.concat(frames, ignore_index=True)
49
+ full_df["date"] = pd.to_datetime(full_df["date"])
50
+ full_df.sort_values("date", inplace=True)
51
+ return full_df
52
+ return pd.DataFrame()
53
+
54
+ def plot_dual_axes(
55
+ df, x_col, y_left, y_right,
56
+ title, y_left_label, y_right_label,
57
+ left_color="red", right_color="blue",
58
+ date_range=None
59
+ ):
60
+ fig = make_subplots(specs=[[{"secondary_y": True}]])
61
+ fig.add_trace(
62
+ go.Scatter(
63
+ x=df[x_col],
64
+ y=df[y_left],
65
+ mode="lines+markers",
66
+ name=y_left_label,
67
+ line=dict(color=left_color)
68
+ ),
69
+ secondary_y=False
70
+ )
71
+ fig.add_trace(
72
+ go.Scatter(
73
+ x=df[x_col],
74
+ y=df[y_right],
75
+ mode="lines+markers",
76
+ name=y_right_label,
77
+ line=dict(color=right_color)
78
+ ),
79
+ secondary_y=True
80
+ )
81
+ fig.update_layout(
82
+ title=title,
83
+ xaxis=dict(range=date_range),
84
+ xaxis_title="Date",
85
+ legend=dict(
86
+ orientation="h",
87
+ yanchor="bottom",
88
+ y=1.02,
89
+ xanchor="center",
90
+ x=0.5
91
+ ),
92
+ margin=dict(l=60, r=60, t=80, b=60),
93
+ width=1600,
94
+ height=800
95
+ )
96
+ fig.update_yaxes(
97
+ title_text=y_left_label,
98
+ secondary_y=False,
99
+ tickfont_color=left_color,
100
+ titlefont_color=left_color
101
+ )
102
+ fig.update_yaxes(
103
+ title_text=y_right_label,
104
+ secondary_y=True,
105
+ tickfont_color=right_color,
106
+ titlefont_color=right_color
107
+ )
108
+ return fig
109
+
110
+ def run_historical_sentiment(ticker):
111
+ st.write(
112
+ "This page displays historical social sentiment data for the ticker. "
113
+ "Five separate charts are generated: sentiment scores, posts, comments, likes, and impressions."
114
+ )
115
+ df = get_historical_sentiment(ticker)
116
+ if df.empty:
117
+ st.error("No data found for ticker " + ticker)
118
+ return
119
+ common_range = [df["date"].min(), df["date"].max()]
120
+
121
+ st.subheader(f"{ticker} Sentiment Scores")
122
+ fig1 = plot_dual_axes(
123
+ df, "date", "stocktwitsSentiment", "twitterSentiment",
124
+ f"{ticker} Sentiment Scores",
125
+ "Stocktwits Sentiment", "Twitter Sentiment",
126
+ date_range=common_range
127
+ )
128
+ st.plotly_chart(fig1, use_container_width=True)
129
+
130
+ st.subheader(f"{ticker} Posts")
131
+ fig2 = plot_dual_axes(
132
+ df, "date", "stocktwitsPosts", "twitterPosts",
133
+ f"{ticker} Posts",
134
+ "Stocktwits Posts", "Twitter Posts",
135
+ date_range=common_range
136
+ )
137
+ st.plotly_chart(fig2, use_container_width=True)
138
+
139
+ st.subheader(f"{ticker} Comments")
140
+ fig3 = plot_dual_axes(
141
+ df, "date", "stocktwitsComments", "twitterComments",
142
+ f"{ticker} Comments",
143
+ "Stocktwits Comments", "Twitter Comments",
144
+ date_range=common_range
145
+ )
146
+ st.plotly_chart(fig3, use_container_width=True)
147
+
148
+ st.subheader(f"{ticker} Likes")
149
+ fig4 = plot_dual_axes(
150
+ df, "date", "stocktwitsLikes", "twitterLikes",
151
+ f"{ticker} Likes",
152
+ "Stocktwits Likes", "Twitter Likes",
153
+ date_range=common_range
154
+ )
155
+ st.plotly_chart(fig4, use_container_width=True)
156
+
157
+ st.subheader(f"{ticker} Impressions")
158
+ fig5 = plot_dual_axes(
159
+ df, "date", "stocktwitsImpressions", "twitterImpressions",
160
+ f"{ticker} Impressions",
161
+ "Stocktwits Impressions", "Twitter Impressions",
162
+ date_range=common_range
163
+ )
164
+ st.plotly_chart(fig5, use_container_width=True)
165
+
166
+ #############################
167
+ # PAGE 2: Trending Sentiment
168
+ #############################
169
+
170
+ @st.cache_data(show_spinner=False)
171
+ def fetch_trending_sentiment(sentiment_type="bullish", source="stocktwits"):
172
+ url = (
173
+ f"https://financialmodelingprep.com/api/v4/social-sentiments/trending?"
174
+ f"type={sentiment_type}&source={source}&apikey={API_KEY}"
175
+ )
176
+ response = requests.get(url)
177
+ response.raise_for_status()
178
+ data = response.json()
179
+ if not data:
180
+ return pd.DataFrame()
181
+ df = pd.DataFrame(data)
182
+ df = df.sort_values("rank").reset_index(drop=True)
183
+ return df
184
+
185
+ def plot_trending(df, title, top_n):
186
+ if df.empty:
187
+ st.error("No data available to plot.")
188
+ return
189
+ df = df.head(top_n)
190
+ fig = go.Figure()
191
+ fig.add_trace(
192
+ go.Bar(
193
+ x=df["symbol"],
194
+ y=df["sentiment"],
195
+ name="Sentiment",
196
+ text=df["sentiment"],
197
+ textposition="auto",
198
+ marker_color="red",
199
+ offsetgroup="group1"
200
+ )
201
+ )
202
+ fig.add_trace(
203
+ go.Bar(
204
+ x=df["symbol"],
205
+ y=df["lastSentiment"],
206
+ name="Last Sentiment",
207
+ text=df["lastSentiment"],
208
+ textposition="auto",
209
+ marker_color="blue",
210
+ offsetgroup="group2",
211
+ yaxis="y2"
212
+ )
213
+ )
214
+ fig.update_layout(
215
+ title=title,
216
+ barmode="group",
217
+ bargap=0.15,
218
+ bargroupgap=0.1,
219
+ xaxis=dict(
220
+ title="Ticker",
221
+ type="category",
222
+ categoryorder="array",
223
+ categoryarray=df["symbol"].tolist()
224
+ ),
225
+ yaxis=dict(
226
+ title="Sentiment",
227
+ titlefont=dict(color="red"),
228
+ tickfont=dict(color="red")
229
+ ),
230
+ yaxis2=dict(
231
+ title="Last Sentiment",
232
+ titlefont=dict(color="blue"),
233
+ tickfont=dict(color="blue"),
234
+ anchor="x",
235
+ overlaying="y",
236
+ side="right"
237
+ ),
238
+ width=1000 + 100 * len(df),
239
+ height=600,
240
+ legend=dict(
241
+ orientation="h",
242
+ yanchor="bottom",
243
+ y=1.02,
244
+ xanchor="center",
245
+ x=0.5
246
+ )
247
+ )
248
+ st.plotly_chart(fig, use_container_width=True)
249
+
250
+ def run_trending_sentiment(top_n):
251
+ st.write(
252
+ "This page shows trending sentiment data. Two grouped bar charts are created for bullish and bearish sentiments. "
253
+ "Each chart displays two bars per ticker: one for Sentiment and one for Last Sentiment."
254
+ )
255
+ st.write("## Bullish Trending Sentiment")
256
+ bullish_df = fetch_trending_sentiment(sentiment_type="bullish", source="stocktwits")
257
+ plot_trending(bullish_df, title="Bullish Trending Sentiment", top_n=top_n)
258
+ st.write("## Bearish Trending Sentiment")
259
+ bearish_df = fetch_trending_sentiment(sentiment_type="bearish", source="stocktwits")
260
+ plot_trending(bearish_df, title="Bearish Trending Sentiment", top_n=top_n)
261
+
262
+ #############################
263
+ # PAGE 3: Sentiment Change
264
+ #############################
265
+
266
+ @st.cache_data(show_spinner=False)
267
+ def fetch_change_sentiment(sentiment_type="bullish", source="stocktwits"):
268
+ url = (
269
+ f"https://financialmodelingprep.com/api/v4/social-sentiments/change?"
270
+ f"type={sentiment_type}&source={source}&apikey={API_KEY}"
271
+ )
272
+ response = requests.get(url)
273
+ response.raise_for_status()
274
+ data = response.json()
275
+ if not data:
276
+ return pd.DataFrame()
277
+ df = pd.DataFrame(data)
278
+ df = df.sort_values("rank").reset_index(drop=True)
279
+ return df
280
+
281
+ def plot_change_sentiment(df, title, top_n):
282
+ if df.empty:
283
+ st.error("No data available to plot.")
284
+ return
285
+ df = df.head(top_n)
286
+ fig = go.Figure()
287
+ fig.add_trace(
288
+ go.Bar(
289
+ x=df["symbol"],
290
+ y=df["sentiment"],
291
+ name="Sentiment",
292
+ text=df["sentiment"],
293
+ textposition="auto",
294
+ marker_color="red",
295
+ offsetgroup="group1"
296
+ )
297
+ )
298
+ fig.add_trace(
299
+ go.Bar(
300
+ x=df["symbol"],
301
+ y=df["sentimentChange"],
302
+ name="Sentiment Change",
303
+ text=df["sentimentChange"],
304
+ textposition="auto",
305
+ marker_color="blue",
306
+ offsetgroup="group2",
307
+ yaxis="y2"
308
+ )
309
+ )
310
+ fig.update_layout(
311
+ title=title,
312
+ barmode="group",
313
+ bargap=0.15,
314
+ bargroupgap=0.1,
315
+ xaxis=dict(
316
+ title="Ticker",
317
+ type="category",
318
+ categoryorder="array",
319
+ categoryarray=df["symbol"].tolist()
320
+ ),
321
+ yaxis=dict(
322
+ title="Sentiment",
323
+ titlefont=dict(color="red"),
324
+ tickfont=dict(color="red")
325
+ ),
326
+ yaxis2=dict(
327
+ title="Sentiment Change",
328
+ titlefont=dict(color="blue"),
329
+ tickfont=dict(color="blue"),
330
+ anchor="x",
331
+ overlaying="y",
332
+ side="right"
333
+ ),
334
+ width=1000 + 100 * len(df),
335
+ height=600,
336
+ legend=dict(
337
+ orientation="h",
338
+ yanchor="bottom",
339
+ y=1.02,
340
+ xanchor="center",
341
+ x=0.5
342
+ )
343
+ )
344
+ st.plotly_chart(fig, use_container_width=True)
345
+
346
+ def run_change_sentiment(top_n):
347
+ st.write(
348
+ "This page displays social sentiment change data. Two grouped bar charts are created for bullish and bearish sentiments. "
349
+ "Each chart shows two bars per ticker: one for Sentiment and one for Sentiment Change."
350
+ )
351
+ st.write("## Bullish Sentiment Change")
352
+ bullish_df = fetch_change_sentiment(sentiment_type="bullish", source="stocktwits")
353
+ plot_change_sentiment(bullish_df, title="Bullish Sentiment Change", top_n=top_n)
354
+ st.write("## Bearish Sentiment Change")
355
+ bearish_df = fetch_change_sentiment(sentiment_type="bearish", source="stocktwits")
356
+ plot_change_sentiment(bearish_df, title="Bearish Sentiment Change", top_n=top_n)
357
+
358
+ #############################
359
+ # MAIN APP
360
+ #############################
361
+
362
+ def main():
363
+ st.set_page_config(page_title="Social Sentiment Analysis", layout="wide")
364
+ st.title("Social Sentiment Analysis Dashboard")
365
+ st.write(
366
+ "This application provides three types of analysis on social sentiment data from FinancialModelingPrep. "
367
+ "Use the sidebar menu to select the type of analysis you want to run and adjust the options. "
368
+ "Click the 'Run' button to update the results."
369
+ )
370
+
371
+ # Sidebar: Navigation and Options (inside an expander)
372
+ with st.sidebar.expander("Navigation and Options", expanded=True):
373
+ page = st.radio(
374
+ "Select Analysis Page",
375
+ ("Historical Sentiment", "Trending Sentiment", "Sentiment Change"),
376
+ help="Choose the analysis page you want to view."
377
+ )
378
+
379
+ if page == "Historical Sentiment":
380
+ ticker = st.text_input(
381
+ "Ticker Symbol",
382
+ value=st.session_state.historical_ticker,
383
+ help="Enter the ticker symbol for which to display historical sentiment data."
384
+ )
385
+ if st.button("Run Historical Sentiment Analysis"):
386
+ st.session_state.historical_run = True
387
+ st.session_state.historical_ticker = ticker
388
+ elif page == "Trending Sentiment":
389
+ top_n = st.number_input(
390
+ "Select Top N Stocks",
391
+ min_value=1, max_value=100,
392
+ value=st.session_state.trending_top_n,
393
+ help="Number of top stocks to display based on rank."
394
+ )
395
+ if st.button("Run Trending Sentiment Analysis"):
396
+ st.session_state.trending_run = True
397
+ st.session_state.trending_top_n = top_n
398
+ elif page == "Sentiment Change":
399
+ top_n = st.number_input(
400
+ "Select Top N Stocks",
401
+ min_value=1, max_value=100,
402
+ value=st.session_state.change_top_n,
403
+ help="Number of top stocks to display based on rank."
404
+ )
405
+ if st.button("Run Sentiment Change Analysis"):
406
+ st.session_state.change_run = True
407
+ st.session_state.change_top_n = top_n
408
+
409
+ # Main body: Display results based on the selected page and if run button was clicked
410
+ if page == "Historical Sentiment":
411
+ st.header("Historical Social Sentiment")
412
+ if st.session_state.historical_run:
413
+ run_historical_sentiment(st.session_state.historical_ticker)
414
+ else:
415
+ st.info("Please select options in the sidebar and click 'Run Historical Sentiment Analysis'.")
416
+ elif page == "Trending Sentiment":
417
+ st.header("Trending Social Sentiment")
418
+ if st.session_state.trending_run:
419
+ run_trending_sentiment(st.session_state.trending_top_n)
420
+ else:
421
+ st.info("Please select options in the sidebar and click 'Run Trending Sentiment Analysis'.")
422
+ elif page == "Sentiment Change":
423
+ st.header("Social Sentiment Change")
424
+ if st.session_state.change_run:
425
+ run_change_sentiment(st.session_state.change_top_n)
426
+ else:
427
+ st.info("Please select options in the sidebar and click 'Run Sentiment Change Analysis'.")
428
+
429
+ if __name__ == "__main__":
430
+ main()
431
+
432
+
433
+ hide_streamlit_style = """
434
+ <style>
435
+ #MainMenu {visibility: hidden;}
436
+ footer {visibility: hidden;}
437
+ </style>
438
+ """
439
+ st.markdown(hide_streamlit_style, unsafe_allow_html=True)
440
+