Spaces:
Sleeping
Sleeping
| import streamlit as st | |
| import pandas as pd | |
| import requests | |
| import yfinance as yf | |
| from transformers import pipeline | |
| from vaderSentiment.vaderSentiment import SentimentIntensityAnalyzer | |
| from textblob import TextBlob | |
| from datetime import datetime, timedelta | |
| import plotly.graph_objects as go | |
| import nltk | |
| import numpy as np | |
| from sklearn.linear_model import Ridge | |
| from sklearn.preprocessing import PolynomialFeatures | |
| from sklearn.pipeline import make_pipeline | |
| import os | |
| # ------------------------------- | |
| # 🔧 CONFIG | |
| # ------------------------------- | |
| st.set_page_config(page_title="📈 News Sentiment & Stock Tracker", layout="wide") | |
| API_KEY = "88bc396d4eab4be494a4b86ec842db47" | |
| # ------------------------------- | |
| # 📦 โหลดโมเดล | |
| # ------------------------------- | |
| def load_models(): | |
| bert = pipeline("sentiment-analysis", model="nlptown/bert-base-multilingual-uncased-sentiment") | |
| vader = SentimentIntensityAnalyzer() | |
| return bert, vader | |
| bert_model, vader_analyzer = load_models() | |
| # ------------------------------- | |
| # 🧠 ฟังก์ชันแปลงชื่อบริษัท <-> ตัวย่อหุ้น | |
| # ------------------------------- | |
| def resolve_company_symbol(keyword: str): | |
| keyword = keyword.strip() | |
| ticker = None | |
| name = None | |
| try: | |
| data = yf.Ticker(keyword) | |
| info = data.info | |
| if "symbol" in info and info["symbol"]: | |
| ticker = info["symbol"] | |
| name = info.get("longName", info.get("shortName", keyword)) | |
| else: | |
| url = f"https://query2.finance.yahoo.com/v1/finance/search?q={keyword}" | |
| res = requests.get(url).json() | |
| if "quotes" in res and len(res["quotes"]) > 0: | |
| q = res["quotes"][0] | |
| ticker = q.get("symbol") | |
| name = q.get("longname", q.get("shortname", keyword)) | |
| except Exception as e: | |
| st.warning(f"⚠️ ไม่สามารถค้นหาข้อมูลบริษัทได้: {e}") | |
| if not ticker: | |
| ticker = keyword.upper() | |
| if not name: | |
| name = keyword.capitalize() | |
| return name, ticker | |
| # ------------------------------- | |
| # 📰 ดึงข่าวย้อนหลัง 7 วัน | |
| # ------------------------------- | |
| def fetch_news(company, symbol): | |
| to_date = datetime.utcnow() | |
| from_date = to_date - timedelta(days=7) | |
| query = f"({company} OR {symbol}) finance stock" | |
| url = ( | |
| f"https://newsapi.org/v2/everything?" | |
| f"q={query}&from={from_date.date()}&to={to_date.isoformat()}&" | |
| f"language=en&sortBy=publishedAt&pageSize=100&apiKey={API_KEY}" | |
| ) | |
| res = requests.get(url) | |
| data = res.json() | |
| if data.get("status") != "ok": | |
| st.error("❌ ดึงข้อมูลข่าวไม่สำเร็จ") | |
| return pd.DataFrame() | |
| articles = data.get("articles", []) | |
| df = pd.DataFrame([{ | |
| "date": datetime.fromisoformat(a["publishedAt"].replace("Z", "+00:00")), | |
| "title": a["title"], | |
| "description": a["description"], | |
| "source": a["source"]["name"], | |
| "url": a["url"], | |
| } for a in articles]) | |
| df["text"] = df["title"].fillna('') + " " + df["description"].fillna('') | |
| df["company"] = company | |
| df["symbol"] = symbol | |
| return df | |
| # ------------------------------- | |
| # 💬 วิเคราะห์อารมณ์ข่าว | |
| # ------------------------------- | |
| def analyze_sentiment(text, models): | |
| bert, vader = models | |
| if not text.strip(): | |
| return 0 | |
| try: | |
| vader_score = vader.polarity_scores(text)["compound"] | |
| tb_score = TextBlob(text).sentiment.polarity | |
| bert_res = bert(text[:512])[0] | |
| label_map = { | |
| "1 star": -1, "2 stars": -0.5, "3 stars": 0, | |
| "4 stars": 0.5, "5 stars": 1 | |
| } | |
| bert_score = label_map.get(bert_res["label"], 0) | |
| return np.mean([vader_score, tb_score, bert_score]) | |
| except Exception: | |
| return 0 | |
| # ------------------------------- | |
| # 📈 สร้างโมเดลพยากรณ์ | |
| # ------------------------------- | |
| def forecast_sentiment_trend(df): | |
| # ensure datetime format | |
| df["date"] = pd.to_datetime(df["date"], errors="coerce") | |
| df = df.dropna(subset=["date"]) | |
| df_daily = df.groupby(df["date"].dt.date)["sentiment"].mean().reset_index() | |
| df_daily["date"] = pd.to_datetime(df_daily["date"]) | |
| df_daily["days"] = (df_daily["date"] - df_daily["date"].min()).dt.days | |
| X = df_daily["days"].values.reshape(-1, 1) | |
| y = df_daily["sentiment"].values | |
| model = make_pipeline(PolynomialFeatures(2), Ridge(alpha=1.0)) | |
| model.fit(X, y) | |
| last_day = df_daily["days"].max() | |
| future_days = np.arange(last_day + 1, last_day + 8).reshape(-1, 1) | |
| future_preds = model.predict(future_days) | |
| future_dates = [df_daily["date"].max() + timedelta(days=i) for i in range(1, 8)] | |
| forecast_df = pd.DataFrame({"date": future_dates, "predicted_sentiment": future_preds}) | |
| return df_daily, forecast_df | |
| # ------------------------------- | |
| # 📊 ส่วนแสดงผลหลัก | |
| # ------------------------------- | |
| st.title("📈 News Sentiment & Stock Tracker") | |
| keyword = st.text_input("🔍 ค้นหาบริษัทหรือตัวย่อหุ้น (เช่น Apple หรือ AAPL):", "AAPL") | |
| if st.button("Analyze"): | |
| company, symbol = resolve_company_symbol(keyword) | |
| st.info(f"📊 กำลังวิเคราะห์ข่าวของ **{company} ({symbol})**...") | |
| news_df = fetch_news(company, symbol) | |
| if news_df.empty: | |
| st.warning("ไม่พบข่าวในช่วง 7 วันที่ผ่านมา") | |
| st.stop() | |
| news_df["sentiment"] = news_df["text"].apply(lambda x: analyze_sentiment(x, (bert_model, vader_analyzer))) | |
| avg_sent = news_df["sentiment"].mean() | |
| st.metric("📈 ค่าเฉลี่ยอารมณ์ข่าว (7 วัน)", f"{avg_sent:.2f}", | |
| "Positive" if avg_sent > 0 else "Negative" if avg_sent < 0 else "Neutral") | |
| # ------------------------------- | |
| # 📈 แนวโน้มอารมณ์ + ราคาหุ้น | |
| # ------------------------------- | |
| st.subheader("📊 แนวโน้มอารมณ์ข่าว & ราคาหุ้น") | |
| df_actual, df_forecast = forecast_sentiment_trend(news_df) | |
| # ดึงราคาหุ้นจาก yfinance | |
| price_df = yf.download(symbol, period="14d", interval="1d") | |
| price_df = price_df.reset_index()[["Date", "Close"]] | |
| price_df.rename(columns={"Date": "date"}, inplace=True) | |
| price_df["date"] = pd.to_datetime(price_df["date"]).dt.date | |
| df_actual["date"] = pd.to_datetime(df_actual["date"]).dt.date | |
| df_forecast["date"] = pd.to_datetime(df_forecast["date"]).dt.date | |
| # สร้างกราฟรวม | |
| fig = go.Figure() | |
| fig.add_trace(go.Scatter( | |
| x=df_actual["date"], y=df_actual["sentiment"], | |
| mode="lines+markers", name="Actual Sentiment", line=dict(color="blue") | |
| )) | |
| fig.add_trace(go.Scatter( | |
| x=df_forecast["date"], y=df_forecast["predicted_sentiment"], | |
| mode="lines+markers", name="Predicted Sentiment (Next 7 Days)", | |
| line=dict(color="orange", dash="dash") | |
| )) | |
| fig.add_trace(go.Scatter( | |
| x=price_df["date"], y=price_df["Close"], | |
| mode="lines+markers", name=f"{symbol} Stock Price", | |
| line=dict(color="green"), yaxis="y2" | |
| )) | |
| fig.update_layout( | |
| title=f"📈 แนวโน้มอารมณ์ข่าว & ราคาหุ้น ({symbol})", | |
| xaxis=dict(title="วันที่"), | |
| yaxis=dict(title="Sentiment", side="left", range=[-1, 1]), | |
| yaxis2=dict(title="Stock Price (USD)", overlaying="y", side="right", showgrid=False), | |
| legend=dict(x=0, y=1.1, orientation="h"), | |
| hovermode="x unified", | |
| template="plotly_white" | |
| ) | |
| st.plotly_chart(fig, use_container_width=True) | |
| # ------------------------------- | |
| # 📰 แสดงข่าวที่ใช้วิเคราะห์ | |
| # ------------------------------- | |
| st.subheader("📰 ข่าวที่ใช้วิเคราะห์") | |
| st.dataframe(news_df[["date", "source", "title", "sentiment"]]) | |
| # ------------------------------- | |
| # 📚 โหลด NLTK | |
| # ------------------------------- | |
| try: | |
| nltk.download("punkt", quiet=True) | |
| nltk.download("stopwords", quiet=True) | |
| except: | |
| pass | |