Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -17,7 +17,7 @@ import yfinance as yf
|
|
| 17 |
# CONFIG
|
| 18 |
# --------------------------
|
| 19 |
st.set_page_config(page_title="📰 SentimentSync NewsAI", layout="wide")
|
| 20 |
-
API_KEY = "88bc396d4eab4be494a4b86ec842db47"
|
| 21 |
|
| 22 |
# --------------------------
|
| 23 |
# UTILITIES
|
|
@@ -118,27 +118,29 @@ def fetch_financial_news(keyword):
|
|
| 118 |
|
| 119 |
|
| 120 |
# --------------------------
|
| 121 |
-
#
|
|
|
|
| 122 |
# --------------------------
|
| 123 |
@st.cache_data(ttl=3600)
|
| 124 |
def fetch_stock_price(symbol):
|
| 125 |
try:
|
| 126 |
-
#
|
| 127 |
end_date = datetime.now()
|
| 128 |
-
start_date = end_date - timedelta(days=8)
|
| 129 |
|
| 130 |
df = yf.download(symbol,
|
| 131 |
start=start_date.strftime('%Y-%m-%d'),
|
| 132 |
end=end_date.strftime('%Y-%m-%d'),
|
| 133 |
interval="1d")
|
| 134 |
-
|
| 135 |
if df.empty:
|
| 136 |
st.warning("ไม่พบข้อมูลราคาหุ้นในช่วง 8 วันที่ผ่านมา")
|
| 137 |
return pd.DataFrame()
|
| 138 |
|
| 139 |
df = df.reset_index()[["Date", "Close"]]
|
| 140 |
df.rename(columns={"Date": "date", "Close": "price"}, inplace=True)
|
| 141 |
-
|
|
|
|
| 142 |
return df
|
| 143 |
except Exception as e:
|
| 144 |
st.warning(f"ไม่สามารถดึงราคาหุ้นได้: {e}")
|
|
@@ -146,7 +148,7 @@ def fetch_stock_price(symbol):
|
|
| 146 |
|
| 147 |
|
| 148 |
# --------------------------
|
| 149 |
-
# MAIN APP
|
| 150 |
# --------------------------
|
| 151 |
def main():
|
| 152 |
st.title("📰 SentimentSync NewsAI")
|
|
@@ -170,11 +172,12 @@ def main():
|
|
| 170 |
st.warning("ไม่พบบทความข่าวในช่วง 7 วันที่ผ่านมา")
|
| 171 |
return
|
| 172 |
|
| 173 |
-
# วิเคราะห์ sentiment
|
| 174 |
st.info("กำลังวิเคราะห์อารมณ์ของข่าว...")
|
| 175 |
news_df["sentiment"] = news_df["text"].apply(lambda x: analyze_text(x, vader))
|
| 176 |
news_df["date"] = pd.to_datetime(news_df["date"])
|
| 177 |
|
|
|
|
| 178 |
avg_sentiment = news_df["sentiment"].mean()
|
| 179 |
pos_pct = (news_df["sentiment"] > 0.1).mean() * 100
|
| 180 |
neg_pct = (news_df["sentiment"] < -0.1).mean() * 100
|
|
@@ -185,55 +188,67 @@ def main():
|
|
| 185 |
col2.metric("ข่าวเชิงบวก", f"{pos_pct:.1f}%")
|
| 186 |
col3.metric("ข่าวเชิงลบ", f"{neg_pct:.1f}%")
|
| 187 |
|
| 188 |
-
# Wordcloud
|
| 189 |
st.subheader("☁️ Word Cloud ของข่าว")
|
| 190 |
all_text = " ".join(news_df["text"].tolist())
|
| 191 |
img = generate_wordcloud(all_text)
|
| 192 |
st.image(f"data:image/png;base64,{img}", use_column_width=True)
|
| 193 |
|
| 194 |
-
#
|
|
|
|
|
|
|
| 195 |
st.subheader("📈 แนวโน้มอารมณ์ของข่าว & ราคาหุ้น")
|
| 196 |
-
|
| 197 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 198 |
|
| 199 |
# Train sentiment model
|
| 200 |
model = LinearRegression()
|
| 201 |
-
model.fit(df_sorted[["timestamp"]], df_sorted["
|
| 202 |
|
| 203 |
# Forecast next 7 days
|
| 204 |
future_days = 7
|
| 205 |
future_timestamps = np.arange(df_sorted["timestamp"].max() + 1, df_sorted["timestamp"].max() + future_days + 1)
|
| 206 |
-
future_dates = [df_sorted["
|
| 207 |
future_preds = model.predict(future_timestamps.reshape(-1, 1))
|
| 208 |
|
| 209 |
-
# ดึงราคาหุ้น
|
| 210 |
_, symbol = resolve_company_symbol(keyword)
|
| 211 |
-
stock_df = fetch_stock_price(symbol)
|
| 212 |
|
| 213 |
-
# Plot
|
| 214 |
fig = go.Figure()
|
| 215 |
-
|
|
|
|
| 216 |
fig.add_trace(go.Scatter(
|
| 217 |
-
x=df_sorted["
|
| 218 |
-
mode="lines+markers", name="Actual Sentiment",
|
| 219 |
line=dict(color="blue")
|
| 220 |
))
|
| 221 |
-
|
|
|
|
| 222 |
fig.add_trace(go.Scatter(
|
| 223 |
x=future_dates, y=future_preds,
|
| 224 |
mode="lines+markers", name="Predicted Sentiment (7-day Forecast)",
|
| 225 |
line=dict(color="orange", dash="dash")
|
| 226 |
))
|
| 227 |
|
| 228 |
-
# Stock price
|
| 229 |
if not stock_df.empty:
|
| 230 |
-
|
| 231 |
-
fig.add_trace(go.Scatter(
|
| 232 |
x=stock_df["date"], y=stock_df["price"],
|
| 233 |
mode="lines+markers", name=f"{symbol} Stock Price",
|
| 234 |
line=dict(color="green"), yaxis="y2"
|
| 235 |
))
|
| 236 |
-
# --------------------------------
|
| 237 |
|
| 238 |
fig.update_layout(
|
| 239 |
title=f"แนวโน้มและพยากรณ์อารมณ์ข่าว & ราคาหุ้น '{keyword}'",
|
|
@@ -246,7 +261,7 @@ def main():
|
|
| 246 |
|
| 247 |
st.plotly_chart(fig, use_container_width=True)
|
| 248 |
|
| 249 |
-
# แสดงข่าว
|
| 250 |
st.subheader("📰 รายการข่าว")
|
| 251 |
st.dataframe(news_df[["date", "source", "text", "sentiment", "url"]], use_container_width=True)
|
| 252 |
|
|
|
|
| 17 |
# CONFIG
|
| 18 |
# --------------------------
|
| 19 |
st.set_page_config(page_title="📰 SentimentSync NewsAI", layout="wide")
|
| 20 |
+
API_KEY = "88bc396d4eab4be494a4b86ec842db47" # (อย่าลืมเปลี่ยนคีย์ของคุณถ้าแชร์โค้ด)
|
| 21 |
|
| 22 |
# --------------------------
|
| 23 |
# UTILITIES
|
|
|
|
| 118 |
|
| 119 |
|
| 120 |
# --------------------------
|
| 121 |
+
# (*** ส่วนที่ 1: แก้ไข)
|
| 122 |
+
# ดึงราคาหุ้นย้อนหลัง 8 วัน
|
| 123 |
# --------------------------
|
| 124 |
@st.cache_data(ttl=3600)
|
| 125 |
def fetch_stock_price(symbol):
|
| 126 |
try:
|
| 127 |
+
# แก้ไข: ใช้ start/end date ที่ชัดเจน ให้ตรงกับช่วงข่าว
|
| 128 |
end_date = datetime.now()
|
| 129 |
+
start_date = end_date - timedelta(days=8) # ดึง 8 วัน เผื่อวันหยุด
|
| 130 |
|
| 131 |
df = yf.download(symbol,
|
| 132 |
start=start_date.strftime('%Y-%m-%d'),
|
| 133 |
end=end_date.strftime('%Y-%m-%d'),
|
| 134 |
interval="1d")
|
| 135 |
+
|
| 136 |
if df.empty:
|
| 137 |
st.warning("ไม่พบข้อมูลราคาหุ้นในช่วง 8 วันที่ผ่านมา")
|
| 138 |
return pd.DataFrame()
|
| 139 |
|
| 140 |
df = df.reset_index()[["Date", "Close"]]
|
| 141 |
df.rename(columns={"Date": "date", "Close": "price"}, inplace=True)
|
| 142 |
+
# **สำคัญมาก** เราต้อง normalize date ให้เป็น date (00:00)
|
| 143 |
+
df["date"] = pd.to_datetime(df["date"].dt.date)
|
| 144 |
return df
|
| 145 |
except Exception as e:
|
| 146 |
st.warning(f"ไม่สามารถดึงราคาหุ้นได้: {e}")
|
|
|
|
| 148 |
|
| 149 |
|
| 150 |
# --------------------------
|
| 151 |
+
# MAIN APP (*** ส่วนที่ 2: แก้ไข)
|
| 152 |
# --------------------------
|
| 153 |
def main():
|
| 154 |
st.title("📰 SentimentSync NewsAI")
|
|
|
|
| 172 |
st.warning("ไม่พบบทความข่าวในช่วง 7 วันที่ผ่านมา")
|
| 173 |
return
|
| 174 |
|
| 175 |
+
# วิเคราะห์ sentiment (รายบทความ)
|
| 176 |
st.info("กำลังวิเคราะห์อารมณ์ของข่าว...")
|
| 177 |
news_df["sentiment"] = news_df["text"].apply(lambda x: analyze_text(x, vader))
|
| 178 |
news_df["date"] = pd.to_datetime(news_df["date"])
|
| 179 |
|
| 180 |
+
# แสดง Metric (ยังใช้ข้อมูลทั้งหมด)
|
| 181 |
avg_sentiment = news_df["sentiment"].mean()
|
| 182 |
pos_pct = (news_df["sentiment"] > 0.1).mean() * 100
|
| 183 |
neg_pct = (news_df["sentiment"] < -0.1).mean() * 100
|
|
|
|
| 188 |
col2.metric("ข่าวเชิงบวก", f"{pos_pct:.1f}%")
|
| 189 |
col3.metric("ข่าวเชิงลบ", f"{neg_pct:.1f}%")
|
| 190 |
|
| 191 |
+
# Wordcloud (ยังใช้ข้อมูลทั้งหมด)
|
| 192 |
st.subheader("☁️ Word Cloud ของข่าว")
|
| 193 |
all_text = " ".join(news_df["text"].tolist())
|
| 194 |
img = generate_wordcloud(all_text)
|
| 195 |
st.image(f"data:image/png;base64,{img}", use_column_width=True)
|
| 196 |
|
| 197 |
+
# -----------------------------------------------------------------
|
| 198 |
+
# (ส่วนที่แก้ไข) แนวโน้ม + พยากรณ์ + ราคาหุ้น
|
| 199 |
+
# -----------------------------------------------------------------
|
| 200 |
st.subheader("📈 แนวโน้มอารมณ์ของข่าว & ราคาหุ้น")
|
| 201 |
+
|
| 202 |
+
# 1. (ใหม่) รวบรวมข้อมูลข่าวเป็น "ค่าเฉลี่ยรายวัน"
|
| 203 |
+
# **สำคัญมาก** เราต้อง normalize date ให้เป็น date (00:00)
|
| 204 |
+
news_df["date_day"] = pd.to_datetime(news_df["date"].dt.date)
|
| 205 |
+
|
| 206 |
+
daily_sentiment_df = news_df.groupby("date_day").agg(
|
| 207 |
+
avg_sentiment=('sentiment', 'mean')
|
| 208 |
+
).reset_index()
|
| 209 |
+
|
| 210 |
+
# 2. (ใหม่) ใช้ข้อมูล "รายวัน" นี้ในการเทรนโมเดล
|
| 211 |
+
df_sorted = daily_sentiment_df.sort_values("date_day").copy()
|
| 212 |
+
df_sorted["timestamp"] = (df_sorted["date_day"] - df_sorted["date_day"].min()).dt.days
|
| 213 |
|
| 214 |
# Train sentiment model
|
| 215 |
model = LinearRegression()
|
| 216 |
+
model.fit(df_sorted[["timestamp"]], df_sorted["avg_sentiment"])
|
| 217 |
|
| 218 |
# Forecast next 7 days
|
| 219 |
future_days = 7
|
| 220 |
future_timestamps = np.arange(df_sorted["timestamp"].max() + 1, df_sorted["timestamp"].max() + future_days + 1)
|
| 221 |
+
future_dates = [df_sorted["date_day"].max() + timedelta(days=i) for i in range(1, future_days + 1)]
|
| 222 |
future_preds = model.predict(future_timestamps.reshape(-1, 1))
|
| 223 |
|
| 224 |
+
# 3. ดึงราคาหุ้น (ซึ่งตอนนี้เราแก้ฟังก์ชัน `fetch_stock_price` แล้ว)
|
| 225 |
_, symbol = resolve_company_symbol(keyword)
|
| 226 |
+
stock_df = fetch_stock_price(symbol)
|
| 227 |
|
| 228 |
+
# 4. Plot (ตอนนี้ X-axis ทั้ง 3 เส้นจะตรงกันแล้ว)
|
| 229 |
fig = go.Figure()
|
| 230 |
+
|
| 231 |
+
# Actual sentiment (จากค่าเฉลี่ยรายวัน)
|
| 232 |
fig.add_trace(go.Scatter(
|
| 233 |
+
x=df_sorted["date_day"], y=df_sorted["avg_sentiment"],
|
| 234 |
+
mode="lines+markers", name="Actual Sentiment (Daily Avg)",
|
| 235 |
line=dict(color="blue")
|
| 236 |
))
|
| 237 |
+
|
| 238 |
+
# Predicted sentiment (จากโมเดลรายวัน)
|
| 239 |
fig.add_trace(go.Scatter(
|
| 240 |
x=future_dates, y=future_preds,
|
| 241 |
mode="lines+markers", name="Predicted Sentiment (7-day Forecast)",
|
| 242 |
line=dict(color="orange", dash="dash")
|
| 243 |
))
|
| 244 |
|
| 245 |
+
# Stock price (จาก yfinance ที่เป็นรายวัน)
|
| 246 |
if not stock_df.empty:
|
| 247 |
+
fig.add_trace(go.Scatter(
|
|
|
|
| 248 |
x=stock_df["date"], y=stock_df["price"],
|
| 249 |
mode="lines+markers", name=f"{symbol} Stock Price",
|
| 250 |
line=dict(color="green"), yaxis="y2"
|
| 251 |
))
|
|
|
|
| 252 |
|
| 253 |
fig.update_layout(
|
| 254 |
title=f"แนวโน้มและพยากรณ์อารมณ์ข่าว & ราคาหุ้น '{keyword}'",
|
|
|
|
| 261 |
|
| 262 |
st.plotly_chart(fig, use_container_width=True)
|
| 263 |
|
| 264 |
+
# แสดงข่าว (ยังใช้ข้อมูลดิบรายบทความ)
|
| 265 |
st.subheader("📰 รายการข่าว")
|
| 266 |
st.dataframe(news_df[["date", "source", "text", "sentiment", "url"]], use_container_width=True)
|
| 267 |
|