import streamlit as st import pandas as pd import requests import numpy as np import plotly.express as px import nltk import time from datetime import datetime, timedelta from vaderSentiment.vaderSentiment import SentimentIntensityAnalyzer from textblob import TextBlob from transformers import pipeline from wordcloud import WordCloud from sklearn.linear_model import LinearRegression import matplotlib.pyplot as plt # ========================================= # SETUP # ========================================= st.set_page_config(page_title="Financial Sentiment Analyzer", layout="wide") nltk.download("stopwords", quiet=True) # โหลดโมเดล sentiment ของ BERT @st.cache_resource def load_bert_model(): return pipeline("sentiment-analysis", model="nlptown/bert-base-multilingual-uncased-sentiment") bert_model = load_bert_model() vader = SentimentIntensityAnalyzer() # ใส่ API key ของคุณ API_KEY = st.secrets["NEWS_API_KEY"] # ใส่ใน .streamlit/secrets.toml # หรือถ้ารัน local: # API_KEY = "88bc396d4eab4be494a4b86ec842db47" # ========================================= # FUNCTION: ดึงข่าวจาก NewsAPI.org # ========================================= @st.cache_data(ttl=3600, show_spinner="Fetching financial news...") def fetch_financial_news(keyword, days=7, limit=50): """ดึงข่าวการเงินย้อนหลังจาก NewsAPI.org""" to_date = datetime.now().strftime('%Y-%m-%d') from_date = (datetime.now() - timedelta(days=days)).strftime('%Y-%m-%d') url = ( f"https://newsapi.org/v2/everything?" f"q={keyword}+finance+stock&" f"from={from_date}&to={to_date}&" f"language=en&" f"sortBy=publishedAt&" f"pageSize={limit}&" f"apiKey={API_KEY}" ) response = requests.get(url) data = response.json() if data.get("status") != "ok": st.error(f"Error fetching news: {data.get('message', 'Unknown error')}") return pd.DataFrame() articles = [] for a in data["articles"]: articles.append({ "date": pd.to_datetime(a["publishedAt"]), "text": f"{a['title']}\n{a.get('description', '')}", "source": a["source"]["name"], "url": a["url"] }) return pd.DataFrame(articles) # ========================================= # FUNCTION: วิเคราะห์อารมณ์ # ========================================= def analyze_sentiment(text): """รวมผลจาก BERT, VADER, TextBlob""" try: bert_label = bert_model(text[:512])[0]["label"] vader_score = vader.polarity_scores(text)["compound"] blob_score = TextBlob(text).sentiment.polarity bert_score = ( 1 if "5" in bert_label or "4" in bert_label else -1 if "1" in bert_label or "2" in bert_label else 0 ) final_score = np.mean([bert_score, np.sign(vader_score), np.sign(blob_score)]) return final_score except Exception: return 0 # ========================================= # FUNCTION: สร้าง Word Cloud # ========================================= def create_wordcloud(texts): text = " ".join(texts) wc = WordCloud(width=800, height=400, background_color="white", stopwords=set(nltk.corpus.stopwords.words("english"))).generate(text) return wc # ========================================= # FUNCTION: พยากรณ์แนวโน้มอารมณ์ # ========================================= def forecast_sentiment_trend(df): df = df.sort_values("date") df["timestamp"] = (df["date"] - df["date"].min()).dt.days model = LinearRegression() model.fit(df[["timestamp"]], df["sentiment"]) future = pd.DataFrame({"timestamp": np.arange(df["timestamp"].max()+1, df["timestamp"].max()+8)}) pred = model.predict(future) return pred # ========================================= # MAIN APP # ========================================= st.title("💹 Financial News Sentiment Analyzer (NewsAPI.org version)") st.markdown("วิเคราะห์อารมณ์ของข่าวการเงินย้อนหลังจาก **NewsAPI.org** โดยใช้ BERT + VADER + TextBlob") keyword = st.text_input("🔍 ใส่ชื่อบริษัท / หุ้น / คำค้นหา", "Tesla") limit = st.slider("จำนวนข่าวที่ต้องการดึง", 10, 100, 50) if st.button("เริ่มวิเคราะห์ข่าว"): with st.spinner(f"กำลังดึงข่าวเกี่ยวกับ '{keyword}' ..."): news_df = fetch_financial_news(keyword, days=7, limit=limit) if news_df.empty: st.error("❌ ไม่พบข่าวในช่วง 7 วันที่ผ่านมา") st.stop() st.success(f"✅ ดึงข่าวได้ {len(news_df)} รายการจาก NewsAPI.org") # วิเคราะห์ sentiment st.info("🔎 กำลังวิเคราะห์อารมณ์ของข่าวแต่ละรายการ...") news_df["sentiment"] = news_df["text"].apply(analyze_sentiment) # แสดงผลรวม avg_sentiment = news_df["sentiment"].mean() st.metric("📊 ค่าเฉลี่ยอารมณ์โดยรวม", f"{avg_sentiment:.2f}") # กราฟแนวโน้ม fig = px.line(news_df.sort_values("date"), x="date", y="sentiment", title=f"แนวโน้มอารมณ์ของข่าว '{keyword}'", markers=True) st.plotly_chart(fig, use_container_width=True) # Word Cloud st.subheader("☁️ คำที่ถูกใช้บ่อยในข่าว") wc = create_wordcloud(news_df["text"].tolist()) st.image(wc.to_array()) # พยากรณ์แนวโน้ม st.subheader("📈 พยากรณ์แนวโน้มอารมณ์ใน 7 วันข้างหน้า") forecast = forecast_sentiment_trend(news_df) st.line_chart(forecast) # แสดงข่าวต้นฉบับ st.subheader("📰 ข่าวที่ใช้ในการวิเคราะห์") for _, row in news_df.iterrows(): st.markdown(f"**[{row['source']}]({row['url']})** — {row['date'].strftime('%Y-%m-%d')} \n{row['text']}")