KYTHY's picture
Update app.py
3439737 verified
raw
history blame
8.61 kB
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"
# -------------------------------
# 📦 โหลดโมเดล
# -------------------------------
@st.cache_resource
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()
# -------------------------------
# 🧠 ฟังก์ชันแปลงชื่อบริษัท <-> ตัวย่อหุ้น
# -------------------------------
@st.cache_data(ttl=86400)
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 วัน
# -------------------------------
@st.cache_data(ttl=3600)
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