KYTHY commited on
Commit
064e28f
·
verified ·
1 Parent(s): 9bca51b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +134 -178
app.py CHANGED
@@ -1,203 +1,159 @@
1
  import streamlit as st
2
  import requests
3
  import pandas as pd
 
 
4
  from datetime import datetime, timedelta
5
- from transformers import pipeline
6
- from vaderSentiment.vaderSentiment import SentimentIntensityAnalyzer
7
  from textblob import TextBlob
8
- import nltk
9
- from wordcloud import WordCloud
10
- import base64
11
- from io import BytesIO
12
- import numpy as np
13
  from sklearn.linear_model import LinearRegression
14
  import plotly.graph_objects as go
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
- @st.cache_resource
26
- def load_models():
27
- st.info("Loading sentiment models...")
28
- bert_model = pipeline("sentiment-analysis", model="nlptown/bert-base-multilingual-uncased-sentiment")
29
- vader = SentimentIntensityAnalyzer()
30
- return bert_model, vader
31
-
32
-
33
- def analyze_text(text, bert_model, vader):
34
- if not text.strip():
35
- return 0
36
-
37
- vader_score = vader.polarity_scores(text)["compound"]
38
- textblob_score = TextBlob(text).sentiment.polarity
39
- bert_result = bert_model(text[:512])[0]
40
- label_map = {
41
- "1 star": -1,
42
- "2 stars": -0.5,
43
- "3 stars": 0,
44
- "4 stars": 0.5,
45
- "5 stars": 1
46
- }
47
- bert_score = label_map.get(bert_result["label"], 0)
48
- return np.mean([vader_score, textblob_score, bert_score])
49
-
50
-
51
- @st.cache_data(ttl=3600)
52
- def fetch_financial_news(keyword):
53
- """ดึงข่าวย้อนหลัง 7 วันจาก NewsAPI.org"""
54
- to_date = datetime.now().strftime('%Y-%m-%d')
55
- from_date = (datetime.now() - timedelta(days=7)).strftime('%Y-%m-%d')
 
 
 
 
 
56
 
57
  all_articles = []
58
- page = 1
59
- while True:
60
  url = (
61
  f"https://newsapi.org/v2/everything?"
62
- f"q={keyword}+finance+stock&"
63
  f"from={from_date}&to={to_date}&"
64
  f"language=en&sortBy=publishedAt&"
65
- f"pageSize=100&page={page}&apiKey={API_KEY}"
66
  )
67
- r = requests.get(url)
68
- data = r.json()
69
-
70
- if data.get("status") != "ok":
71
- st.error(f"API Error: {data}")
72
- break
73
-
74
- articles = data.get("articles", [])
75
- if not articles:
76
  break
77
-
78
- for a in articles:
79
- if a["description"]:
80
- all_articles.append({
81
- "date": pd.to_datetime(a["publishedAt"]),
82
- "text": f"{a['title']} {a['description']}",
83
- "source": a["source"]["name"],
84
- "url": a["url"]
85
- })
86
-
87
- if len(articles) < 100:
88
- break # หมดแล้ว
89
- page += 1
90
-
91
- return pd.DataFrame(all_articles)
92
-
93
-
94
- def generate_wordcloud(text):
95
- stopwords = nltk.corpus.stopwords.words('english')
96
- wordcloud = WordCloud(width=800, height=400, background_color="white", stopwords=stopwords).generate(text)
97
- buf = BytesIO()
98
- wordcloud.to_image().save(buf, format="PNG")
99
- return base64.b64encode(buf.getvalue()).decode()
100
-
101
-
102
- # --------------------------
103
- # MAIN APP
104
- # --------------------------
105
- def main():
106
- st.title("📰 SentimentSync NewsAI")
107
- st.markdown("วิเคราะห์แนวโน้มอารมณ์ของข่าวการเงินย้อนหลัง 7 วัน พร้อมพยากรณ์แนวโน้มในอนาคต")
108
-
109
- # Sidebar
110
- with st.sidebar:
111
- keyword = st.text_input("ค้นหาคำ (เช่น Tesla, Bitcoin, Inflation):", "")
112
- analyze_btn = st.button("วิเคราะห์เลย")
113
-
114
- if not analyze_btn:
115
- st.info("กรอกคำค้นแล้วกด 'วิเคราะห์เลย' เพื่อเริ่มต้น")
116
- return
117
-
118
- bert_model, vader = load_models()
119
-
120
- # ดึงข่าว
121
- st.info(f"กำลังดึงข่าวย้อนหลัง 7 วันจาก NewsAPI.org สำหรับ '{keyword}' ...")
122
- news_df = fetch_financial_news(keyword)
123
- if news_df.empty:
124
- st.warning("ไม่พบบทความข่าวในช่วง 7 วันที่ผ่านมา")
125
- return
126
-
127
- # วิเคราะห์ sentiment
128
- st.info("กำลังวิเคราะห์อารมณ์ของข่าว...")
129
- news_df["sentiment"] = news_df["text"].apply(lambda x: analyze_text(x, bert_model, vader))
130
- news_df["date"] = pd.to_datetime(news_df["date"])
131
-
132
- avg_sentiment = news_df["sentiment"].mean()
133
- pos_pct = (news_df["sentiment"] > 0.1).mean() * 100
134
- neg_pct = (news_df["sentiment"] < -0.1).mean() * 100
135
-
136
- col1, col2, col3 = st.columns(3)
137
- col1.metric("ค่าเฉลี่ยอารมณ์ข่าว", f"{avg_sentiment:.2f}",
138
- "Positive" if avg_sentiment > 0 else "Negative" if avg_sentiment < 0 else "Neutral")
139
- col2.metric("ข่าวเชิงบวก", f"{pos_pct:.1f}%")
140
- col3.metric("ข่าวเชิงลบ", f"{neg_pct:.1f}%")
141
-
142
- # Wordcloud
143
- st.subheader("☁️ Word Cloud ของข่าว")
144
- all_text = " ".join(news_df["text"].tolist())
145
- img = generate_wordcloud(all_text)
146
- st.image(f"data:image/png;base64,{img}", use_column_width=True)
147
-
148
- # แนวโน้มและพยากรณ์ในกราฟเดียว
149
- st.subheader("📈 แนวโน้มและพยากรณ์อารมณ์ของข่าว")
150
-
151
- df_sorted = news_df.sort_values("date").copy()
152
  df_sorted["timestamp"] = (df_sorted["date"] - df_sorted["date"].min()).dt.days
153
 
154
- # Train model
155
  model = LinearRegression()
156
  model.fit(df_sorted[["timestamp"]], df_sorted["sentiment"])
157
 
158
- # Forecast next 7 days
159
  future_days = 7
160
- future_timestamps = np.arange(df_sorted["timestamp"].max() + 1, df_sorted["timestamp"].max() + future_days + 1)
 
161
  future_dates = [df_sorted["date"].max() + timedelta(days=i) for i in range(1, future_days + 1)]
162
  future_preds = model.predict(future_timestamps.reshape(-1, 1))
163
 
164
- # Plot both actual + prediction
165
- fig = go.Figure()
166
-
167
- fig.add_trace(go.Scatter(
168
- x=df_sorted["date"], y=df_sorted["sentiment"],
169
- mode="lines+markers", name="Actual Sentiment",
170
- line=dict(color="blue")
171
- ))
172
-
173
- fig.add_trace(go.Scatter(
174
- x=future_dates, y=future_preds,
175
- mode="lines+markers", name="Predicted Sentiment (7-day Forecast)",
176
- line=dict(color="orange", dash="dash")
177
- ))
178
-
179
- fig.add_trace(go.Scatter(
180
- x=future_dates + future_dates[::-1],
181
- y=list(future_preds + 0.1) + list((future_preds - 0.1)[::-1]),
182
- fill='toself', fillcolor='rgba(255,165,0,0.2)',
183
- line=dict(color='rgba(255,255,255,0)'),
184
- hoverinfo="skip",
185
- showlegend=False
186
- ))
187
-
188
- fig.update_layout(
189
- title=f"แนวโน้มและพยากรณ์อารมณ์ของข่าว '{keyword}'",
190
- xaxis_title="วันที่",
191
- yaxis_title="ค่าอารมณ์ (Sentiment)",
192
- hovermode="x unified",
193
- template="plotly_white"
194
- )
195
- st.plotly_chart(fig, use_container_width=True)
196
-
197
- st.subheader("📰 รายการข่าว")
198
- st.dataframe(news_df[["date", "source", "text", "sentiment", "url"]], use_container_width=True)
199
-
200
-
201
- if __name__ == "__main__":
202
- nltk.download("stopwords", quiet=True)
203
- main()
 
 
 
 
 
 
 
 
 
 
1
  import streamlit as st
2
  import requests
3
  import pandas as pd
4
+ import numpy as np
5
+ import yfinance as yf
6
  from datetime import datetime, timedelta
 
 
7
  from textblob import TextBlob
 
 
 
 
 
8
  from sklearn.linear_model import LinearRegression
9
  import plotly.graph_objects as go
10
 
11
+ # -------------------------------
12
+ # 🔧 ตั้งค่า API Key
13
+ # -------------------------------
14
+ NEWS_API_KEY = "88bc396d4eab4be494a4b86ec842db47"
15
+
16
+ st.set_page_config(page_title="📊 วิเคราะห์อารมณ์ข่าวหุ้น", layout="wide")
17
+
18
+ st.title("📈 วิเคราะห์แนวโน้มอารมณ์ข่าวหุ้นด้วย AI")
19
+ st.caption("ใช้ NewsAPI + yfinance เพื่อค้นหาข่าวหุ้น และพยากรณ์อารมณ์ข่าว 7 วันข้างหน้า")
20
+
21
+ # -------------------------------
22
+ # 🧠 ฟังก์ชัน: แปลงชื่อบริษัท ↔️ Symbol โดยอัตโนมัติ
23
+ # -------------------------------
24
+ def resolve_company_symbol(keyword: str):
25
+ keyword = keyword.strip()
26
+ ticker = None
27
+ name = None
28
+ try:
29
+ data = yf.Ticker(keyword)
30
+ info = data.info
31
+ if "symbol" in info and info["symbol"]: # ถ้าเป็น ticker
32
+ ticker = info["symbol"]
33
+ name = info.get("longName", info.get("shortName", keyword))
34
+ else:
35
+ # ถ้าไม่ใช่ ticker → ค้นจากชื่อบริษัท
36
+ url = f"https://query2.finance.yahoo.com/v1/finance/search?q={keyword}"
37
+ res = requests.get(url).json()
38
+ if "quotes" in res and len(res["quotes"]) > 0:
39
+ q = res["quotes"][0]
40
+ ticker = q.get("symbol", keyword.upper())
41
+ name = q.get("longname", q.get("shortname", keyword.capitalize()))
42
+ except Exception:
43
+ ticker = keyword.upper()
44
+ name = keyword.capitalize()
45
+ return name, ticker
46
+
47
+ # -------------------------------
48
+ # 📰 ฟังก์ชัน: ดึงข่าวจาก NewsAPI
49
+ # -------------------------------
50
+ def fetch_financial_news(keyword: str):
51
+ company, symbol = resolve_company_symbol(keyword)
52
+ query = f"({company} OR {symbol})"
53
+
54
+ to_date = datetime.utcnow().isoformat() # ดึงถึงเวลาปัจจุบัน (UTC)
55
+ from_date = (datetime.utcnow() - timedelta(days=7)).strftime('%Y-%m-%d')
56
 
57
  all_articles = []
58
+ for page in range(1, 6): # ดึงได้สูงสุด 500 ข่าว (100 x 5 หน้า)
 
59
  url = (
60
  f"https://newsapi.org/v2/everything?"
61
+ f"q={query}+finance+stock&"
62
  f"from={from_date}&to={to_date}&"
63
  f"language=en&sortBy=publishedAt&"
64
+ f"pageSize=100&page={page}&apiKey={NEWS_API_KEY}"
65
  )
66
+ response = requests.get(url).json()
67
+ if response.get("status") != "ok" or not response.get("articles"):
 
 
 
 
 
 
 
68
  break
69
+ all_articles.extend(response["articles"])
70
+
71
+ if not all_articles:
72
+ return pd.DataFrame()
73
+
74
+ df = pd.DataFrame(all_articles)
75
+ df["publishedAt"] = pd.to_datetime(df["publishedAt"])
76
+ df["date"] = df["publishedAt"].dt.date
77
+ df["title"] = df["title"].fillna("")
78
+ df["description"] = df["description"].fillna("")
79
+ df["content"] = df["content"].fillna("")
80
+ df["url"] = df["url"].fillna("")
81
+ df["source"] = df["source"].apply(lambda x: x.get("name") if isinstance(x, dict) else x)
82
+ df["company"] = company
83
+ df["symbol"] = symbol
84
+ return df
85
+
86
+ # -------------------------------
87
+ # 😊 ฟังก์ชัน: วิเคราะห์อารมณ์ข่าว
88
+ # -------------------------------
89
+ def analyze_sentiment(text):
90
+ if not text:
91
+ return 0
92
+ analysis = TextBlob(text)
93
+ return analysis.sentiment.polarity # ค่า -1 (ลบ) → +1 (บวก)
94
+
95
+ # -------------------------------
96
+ # 🧮 ฟังก์ชัน: พยากรณ์แนวโน้มด้วย Linear Regression
97
+ # -------------------------------
98
+ def forecast_sentiment_trend(df):
99
+ df_sorted = df.groupby("date")["sentiment"].mean().reset_index()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
  df_sorted["timestamp"] = (df_sorted["date"] - df_sorted["date"].min()).dt.days
101
 
 
102
  model = LinearRegression()
103
  model.fit(df_sorted[["timestamp"]], df_sorted["sentiment"])
104
 
 
105
  future_days = 7
106
+ future_timestamps = np.arange(df_sorted["timestamp"].max() + 1,
107
+ df_sorted["timestamp"].max() + future_days + 1)
108
  future_dates = [df_sorted["date"].max() + timedelta(days=i) for i in range(1, future_days + 1)]
109
  future_preds = model.predict(future_timestamps.reshape(-1, 1))
110
 
111
+ forecast_df = pd.DataFrame({"date": future_dates, "predicted_sentiment": future_preds})
112
+ return df_sorted, forecast_df
113
+
114
+ # -------------------------------
115
+ # 🎯 ส่วนหลักของแอป
116
+ # -------------------------------
117
+ keyword = st.text_input("🔍 พิมพ์ชื่อบริษัทหรืออักษรย่อหุ้น (เช่น Apple หรือ AAPL):", "AAPL")
118
+
119
+ if st.button("ค้นหาข่าว"):
120
+ with st.spinner("กำลังดึงข่าวและวิเคราะห์อารมณ์..."):
121
+ news_df = fetch_financial_news(keyword)
122
+
123
+ if news_df.empty:
124
+ st.warning("ไม่พบข่าวในช่วง 7 วันที่ผ่านมา 😢")
125
+ else:
126
+ st.success(f"✅ พบข่าวทั้งหมด {len(news_df)} รายการ")
127
+
128
+ # วิเคราะห์อารมณ์
129
+ news_df["sentiment"] = news_df["title"].apply(analyze_sentiment)
130
+
131
+ # แสดงตารางข่าว
132
+ st.subheader("📰 ข่าวล่าสุด")
133
+ st.dataframe(news_df[["date", "title", "source", "sentiment", "url"]])
134
+
135
+ # สร้างกราฟแนวโน้ม + พยากรณ์
136
+ st.subheader("📊 แนวโน้มและพยากรณ์อารมณ์ข่าว 7 วันข้างหน้า")
137
+ df_actual, df_forecast = forecast_sentiment_trend(news_df)
138
+
139
+ fig = go.Figure()
140
+ fig.add_trace(go.Scatter(
141
+ x=df_actual["date"], y=df_actual["sentiment"],
142
+ mode="lines+markers",
143
+ name="Actual Sentiment",
144
+ line=dict(color="blue")
145
+ ))
146
+ fig.add_trace(go.Scatter(
147
+ x=df_forecast["date"], y=df_forecast["predicted_sentiment"],
148
+ mode="lines+markers",
149
+ name="Predicted Trend (Next 7 Days)",
150
+ line=dict(color="orange", dash="dash")
151
+ ))
152
+ fig.update_layout(
153
+ title=f"📈 แนวโน้มอารมณ์ข่าวของ {df_actual['date'].min()} ถึง {df_forecast['date'].max()}",
154
+ xaxis_title="วันที่",
155
+ yaxis_title="ค่าอารมณ์ (Sentiment)",
156
+ hovermode="x unified",
157
+ template="plotly_white"
158
+ )
159
+ st.plotly_chart(fig, use_container_width=True)