KYTHY commited on
Commit
76f40c6
·
verified ·
1 Parent(s): b14206a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +87 -72
app.py CHANGED
@@ -3,40 +3,45 @@ import requests
3
  import pandas as pd
4
  from datetime import datetime, timedelta
5
  from vaderSentiment.vaderSentiment import SentimentIntensityAnalyzer
 
 
6
  from wordcloud import WordCloud
7
  import base64
8
  from io import BytesIO
9
  import numpy as np
 
10
  import plotly.graph_objects as go
11
  import yfinance as yf
12
 
13
  # --------------------------
14
  # CONFIG
15
  # --------------------------
16
- st.set_page_config(page_title="📰 SentimentSync News+Stock", layout="wide")
17
- NEWS_API_KEY = "88bc396d4eab4be494a4b86ec842db47"
18
 
19
  # --------------------------
20
  # UTILITIES
21
  # --------------------------
22
- @st.cache_resource
23
- def load_sentiment_model():
24
- st.info("Loading sentiment analyzer...")
25
- vader = SentimentIntensityAnalyzer()
26
- return vader
27
-
28
  def analyze_text(text, vader):
29
  if not text.strip():
30
  return 0
31
- return vader.polarity_scores(text)["compound"]
 
 
 
 
 
 
 
 
 
 
32
 
33
- def resolve_company_symbol(keyword: str):
34
- """
35
- คืน (company_name, symbol) จากชื่อบริษัทหรือ ticker
36
- """
37
- import yfinance as yf
38
- import requests
39
 
 
 
 
 
40
  keyword = keyword.strip()
41
  ticker = None
42
  name = None
@@ -64,23 +69,27 @@ def resolve_company_symbol(keyword: str):
64
 
65
  return name, ticker
66
 
 
 
 
 
67
  @st.cache_data(ttl=3600)
68
- def fetch_financial_news(company, symbol):
69
- """ดึงข่าวย้อนหลัง 7 วันจาก NewsAPI.org"""
70
  to_date = datetime.now().strftime('%Y-%m-%d')
71
  from_date = (datetime.now() - timedelta(days=7)).strftime('%Y-%m-%d')
72
 
73
- query_keyword = f"({company} OR {symbol})"
74
 
75
  all_articles = []
76
  page = 1
77
  while True:
78
  url = (
79
  f"https://newsapi.org/v2/everything?"
80
- f"q={query_keyword}+finance+stock&"
81
  f"from={from_date}&to={to_date}&"
82
  f"language=en&sortBy=publishedAt&"
83
- f"pageSize=100&page={page}&apiKey={NEWS_API_KEY}"
84
  )
85
  r = requests.get(url)
86
  data = r.json()
@@ -107,52 +116,44 @@ def fetch_financial_news(company, symbol):
107
 
108
  return pd.DataFrame(all_articles)
109
 
110
- def generate_wordcloud(text):
111
- stopwords = ["the","and","of","to","in","for","on","with","at","a","an"]
112
- wordcloud = WordCloud(width=800, height=400, background_color="white", stopwords=stopwords).generate(text)
113
- buf = BytesIO()
114
- wordcloud.to_image().save(buf, format="PNG")
115
- return base64.b64encode(buf.getvalue()).decode()
116
 
117
- def get_stock_prices(symbol: str, start_date: datetime, end_date: datetime):
118
- """ดึงราคาหุ้น Close price ของ symbol"""
 
 
 
119
  try:
120
- stock_df = yf.download(symbol, start=start_date.strftime('%Y-%m-%d'),
121
- end=end_date.strftime('%Y-%m-%d'), progress=False)
122
- if stock_df.empty or 'Close' not in stock_df.columns:
123
- return pd.DataFrame()
124
- stock_df = stock_df.reset_index()[['Date', 'Close']]
125
- stock_df.rename(columns={'Date': 'date', 'Close': 'price'}, inplace=True)
126
- stock_df['date'] = pd.to_datetime(stock_df['date'].dt.date)
127
- return stock_df
128
  except Exception as e:
129
- print(f"Error fetching stock data: {e}")
130
  return pd.DataFrame()
131
 
 
132
  # --------------------------
133
  # MAIN APP
134
  # --------------------------
135
  def main():
136
- st.title("📰 SentimentSync News + Stock Prices")
137
- st.markdown("วิเคราะห์แนวโน้มอารมณ์ข่าวและราคาหุ้นย้อนหลัง 7 วัน")
138
 
 
139
  with st.sidebar:
140
- keyword = st.text_input("ค้นหาบริษัทหรือ Symbol:", "")
141
  analyze_btn = st.button("วิเคราะห์เลย")
142
 
143
  if not analyze_btn:
144
- st.info("กรอกคำค้นแล้วกด 'วิเคราะห์เลย' เพื่อเริ่ม")
145
  return
146
 
147
- vader = load_sentiment_model()
148
-
149
- # แปลงชื่อ → (company, symbol)
150
- company, symbol = resolve_company_symbol(keyword)
151
- st.write(f"Resolved: {company} ({symbol})")
152
 
153
  # ดึงข่าว
154
- st.info(f"กำลังดึงข่าวย้อนหลัง 7 วันสำหรับ '{company} / {symbol}' ...")
155
- news_df = fetch_financial_news(company, symbol)
156
  if news_df.empty:
157
  st.warning("ไม่พบบทความข่าวในช่วง 7 วันที่ผ่านมา")
158
  return
@@ -162,7 +163,6 @@ def main():
162
  news_df["sentiment"] = news_df["text"].apply(lambda x: analyze_text(x, vader))
163
  news_df["date"] = pd.to_datetime(news_df["date"])
164
 
165
- # สรุป sentiment
166
  avg_sentiment = news_df["sentiment"].mean()
167
  pos_pct = (news_df["sentiment"] > 0.1).mean() * 100
168
  neg_pct = (news_df["sentiment"] < -0.1).mean() * 100
@@ -179,48 +179,63 @@ def main():
179
  img = generate_wordcloud(all_text)
180
  st.image(f"data:image/png;base64,{img}", use_column_width=True)
181
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
182
  # ดึงราคาหุ้น
183
- stock_prices = get_stock_prices(symbol, news_df['date'].min(), news_df['date'].max())
 
184
 
185
- # กราฟ sentiment + stock
186
- st.subheader("📈 แนวโน้มอารมณ์ข่าว + ราคาหุ้น")
187
  fig = go.Figure()
188
-
189
- # Sentiment
190
  fig.add_trace(go.Scatter(
191
- x=news_df["date"], y=news_df["sentiment"],
192
- mode="lines+markers", name="Sentiment",
193
- line=dict(color="orange")
 
 
 
 
 
 
194
  ))
195
-
196
  # Stock price
197
- if not stock_prices.empty:
198
  fig.add_trace(go.Scatter(
199
- x=stock_prices["date"], y=stock_prices["price"],
200
  mode="lines+markers", name=f"{symbol} Stock Price",
201
- line=dict(color="blue"),
202
- yaxis="y2"
203
  ))
204
- fig.update_layout(
205
- yaxis2=dict(
206
- title="Stock Price",
207
- overlaying="y",
208
- side="right"
209
- )
210
- )
211
 
212
  fig.update_layout(
213
- title=f"Sentiment & Stock Price for {company} ({symbol})",
214
- xaxis_title="Date",
215
- yaxis_title="Sentiment Score",
 
216
  hovermode="x unified",
217
  template="plotly_white"
218
  )
 
219
  st.plotly_chart(fig, use_container_width=True)
220
 
221
  # แสดงข่าว
222
  st.subheader("📰 รายการข่าว")
223
  st.dataframe(news_df[["date", "source", "text", "sentiment", "url"]], use_container_width=True)
224
 
 
225
  if __name__ == "__main__":
 
226
  main()
 
3
  import pandas as pd
4
  from datetime import datetime, timedelta
5
  from vaderSentiment.vaderSentiment import SentimentIntensityAnalyzer
6
+ from textblob import TextBlob
7
+ import nltk
8
  from wordcloud import WordCloud
9
  import base64
10
  from io import BytesIO
11
  import numpy as np
12
+ from sklearn.linear_model import LinearRegression
13
  import plotly.graph_objects as go
14
  import yfinance as yf
15
 
16
  # --------------------------
17
  # CONFIG
18
  # --------------------------
19
+ st.set_page_config(page_title="📰 SentimentSync NewsAI", layout="wide")
20
+ API_KEY = "88bc396d4eab4be494a4b86ec842db47"
21
 
22
  # --------------------------
23
  # UTILITIES
24
  # --------------------------
 
 
 
 
 
 
25
  def analyze_text(text, vader):
26
  if not text.strip():
27
  return 0
28
+ vader_score = vader.polarity_scores(text)["compound"]
29
+ textblob_score = TextBlob(text).sentiment.polarity
30
+ return np.mean([vader_score, textblob_score])
31
+
32
+
33
+ def generate_wordcloud(text):
34
+ stopwords = nltk.corpus.stopwords.words('english')
35
+ wordcloud = WordCloud(width=800, height=400, background_color="white", stopwords=stopwords).generate(text)
36
+ buf = BytesIO()
37
+ wordcloud.to_image().save(buf, format="PNG")
38
+ return base64.b64encode(buf.getvalue()).decode()
39
 
 
 
 
 
 
 
40
 
41
+ # --------------------------
42
+ # แปลงชื่อ/ตัวย่อ → (Company Name, Symbol)
43
+ # --------------------------
44
+ def resolve_company_symbol(keyword: str):
45
  keyword = keyword.strip()
46
  ticker = None
47
  name = None
 
69
 
70
  return name, ticker
71
 
72
+
73
+ # --------------------------
74
+ # ดึงข่าว 7 วัน สำหรับ Company + Symbol
75
+ # --------------------------
76
  @st.cache_data(ttl=3600)
77
+ def fetch_financial_news(keyword):
78
+ company, symbol = resolve_company_symbol(keyword)
79
  to_date = datetime.now().strftime('%Y-%m-%d')
80
  from_date = (datetime.now() - timedelta(days=7)).strftime('%Y-%m-%d')
81
 
82
+ query_keyword = f"({company} OR {symbol}) finance stock"
83
 
84
  all_articles = []
85
  page = 1
86
  while True:
87
  url = (
88
  f"https://newsapi.org/v2/everything?"
89
+ f"q={query_keyword}&"
90
  f"from={from_date}&to={to_date}&"
91
  f"language=en&sortBy=publishedAt&"
92
+ f"pageSize=100&page={page}&apiKey={API_KEY}"
93
  )
94
  r = requests.get(url)
95
  data = r.json()
 
116
 
117
  return pd.DataFrame(all_articles)
118
 
 
 
 
 
 
 
119
 
120
+ # --------------------------
121
+ # ดึงราคาหุ้นย้อนหลัง 14 วัน
122
+ # --------------------------
123
+ @st.cache_data(ttl=3600)
124
+ def fetch_stock_price(symbol):
125
  try:
126
+ df = yf.download(symbol, period="14d", interval="1d")
127
+ df = df.reset_index()[["Date", "Close"]]
128
+ df.rename(columns={"Date": "date", "Close": "price"}, inplace=True)
129
+ df["date"] = pd.to_datetime(df["date"])
130
+ return df
 
 
 
131
  except Exception as e:
132
+ st.warning(f"ไม่สามารถดึงราคาหุ้นได้: {e}")
133
  return pd.DataFrame()
134
 
135
+
136
  # --------------------------
137
  # MAIN APP
138
  # --------------------------
139
  def main():
140
+ st.title("📰 SentimentSync NewsAI")
141
+ st.markdown("วิเคราะห์แนวโน้มอารมณ์ของข่าวย้อนหลัง 7 วัน พร้อมราคาหุ้น")
142
 
143
+ # Sidebar
144
  with st.sidebar:
145
+ keyword = st.text_input("ค้นหาคำ / ตัวย่อหุ้น (เช่น Tesla หรือ TSLA):", "")
146
  analyze_btn = st.button("วิเคราะห์เลย")
147
 
148
  if not analyze_btn:
149
+ st.info("กรอกคำค้นแล้วกด 'วิเคราะห์เลย' เพื่อเริ่มต้น")
150
  return
151
 
152
+ vader = SentimentIntensityAnalyzer()
 
 
 
 
153
 
154
  # ดึงข่าว
155
+ st.info(f"กำลังดึงข่าวย้อนหลัง 7 วันสำหรับ '{keyword}' ...")
156
+ news_df = fetch_financial_news(keyword)
157
  if news_df.empty:
158
  st.warning("ไม่พบบทความข่าวในช่วง 7 วันที่ผ่านมา")
159
  return
 
163
  news_df["sentiment"] = news_df["text"].apply(lambda x: analyze_text(x, vader))
164
  news_df["date"] = pd.to_datetime(news_df["date"])
165
 
 
166
  avg_sentiment = news_df["sentiment"].mean()
167
  pos_pct = (news_df["sentiment"] > 0.1).mean() * 100
168
  neg_pct = (news_df["sentiment"] < -0.1).mean() * 100
 
179
  img = generate_wordcloud(all_text)
180
  st.image(f"data:image/png;base64,{img}", use_column_width=True)
181
 
182
+ # แนวโน้ม + พยากรณ์ + ราคาหุ้น
183
+ st.subheader("📈 แนวโน้มอารมณ์ของข่าว & ราคาหุ้น")
184
+ df_sorted = news_df.sort_values("date").copy()
185
+ df_sorted["timestamp"] = (df_sorted["date"] - df_sorted["date"].min()).dt.days
186
+
187
+ # Train sentiment model
188
+ model = LinearRegression()
189
+ model.fit(df_sorted[["timestamp"]], df_sorted["sentiment"])
190
+
191
+ # Forecast next 7 days
192
+ future_days = 7
193
+ future_timestamps = np.arange(df_sorted["timestamp"].max() + 1, df_sorted["timestamp"].max() + future_days + 1)
194
+ future_dates = [df_sorted["date"].max() + timedelta(days=i) for i in range(1, future_days + 1)]
195
+ future_preds = model.predict(future_timestamps.reshape(-1, 1))
196
+
197
  # ดึงราคาหุ้น
198
+ _, symbol = resolve_company_symbol(keyword)
199
+ stock_df = fetch_stock_price(symbol)
200
 
201
+ # Plot
 
202
  fig = go.Figure()
203
+ # Actual sentiment
 
204
  fig.add_trace(go.Scatter(
205
+ x=df_sorted["date"], y=df_sorted["sentiment"],
206
+ mode="lines+markers", name="Actual Sentiment",
207
+ line=dict(color="blue")
208
+ ))
209
+ # Predicted sentiment
210
+ fig.add_trace(go.Scatter(
211
+ x=future_dates, y=future_preds,
212
+ mode="lines+markers", name="Predicted Sentiment (7-day Forecast)",
213
+ line=dict(color="orange", dash="dash")
214
  ))
 
215
  # Stock price
216
+ if not stock_df.empty:
217
  fig.add_trace(go.Scatter(
218
+ x=stock_df["date"], y=stock_df["price"],
219
  mode="lines+markers", name=f"{symbol} Stock Price",
220
+ line=dict(color="green"), yaxis="y2"
 
221
  ))
 
 
 
 
 
 
 
222
 
223
  fig.update_layout(
224
+ title=f"แนวโน้มและพยากรณ์อารมณ์ข่าว & ราคาหุ้น '{keyword}'",
225
+ xaxis_title="วันที่",
226
+ yaxis=dict(title="Sentiment", side="left", range=[-1, 1]),
227
+ yaxis2=dict(title="Stock Price", overlaying="y", side="right", showgrid=False),
228
  hovermode="x unified",
229
  template="plotly_white"
230
  )
231
+
232
  st.plotly_chart(fig, use_container_width=True)
233
 
234
  # แสดงข่าว
235
  st.subheader("📰 รายการข่าว")
236
  st.dataframe(news_df[["date", "source", "text", "sentiment", "url"]], use_container_width=True)
237
 
238
+
239
  if __name__ == "__main__":
240
+ nltk.download("stopwords", quiet=True)
241
  main()