|
|
import gradio as gr |
|
|
import pandas as pd |
|
|
from scraper import YahooFinanceScraper |
|
|
from sentiment_analyzer import NewsAnalyzer |
|
|
import time |
|
|
from collections import Counter |
|
|
|
|
|
|
|
|
|
|
|
print("Initializing Yahoo Finance News Analyzer...") |
|
|
scraper = YahooFinanceScraper() |
|
|
analyzer = None |
|
|
|
|
|
|
|
|
|
|
|
def get_sentiment_color(sentiment): |
|
|
"""กำหนดสีตาม sentiment""" |
|
|
colors = { "Positive": "🟢", "Negative": "🔴", "Neutral": "🟡" } |
|
|
return colors.get(sentiment, "⚪") |
|
|
|
|
|
def get_impact_color(impact): |
|
|
"""กำหนดสีตาม impact""" |
|
|
colors = { "Opportunity": "🟢", "Risk": "🔴", "Neutral": "🟡" } |
|
|
return colors.get(impact, "⚪") |
|
|
|
|
|
|
|
|
def analyze_news(search_type, search_input, num_articles): |
|
|
""" |
|
|
ฟังก์ชันหลักในการวิเคราะห์ข่าว |
|
|
""" |
|
|
|
|
|
|
|
|
|
|
|
global analyzer |
|
|
if analyzer is None: |
|
|
|
|
|
yield "⌛ กำลังโหลดโมเดล AI (ครั้งแรก)...", None, "⌛ กำลังโหลดโมเดล AI (ครั้งแรก)..." |
|
|
print("Analyzer not loaded. Initializing NewsAnalyzer (lazy)...") |
|
|
|
|
|
analyzer = NewsAnalyzer() |
|
|
print("Analyzer loaded successfully.") |
|
|
|
|
|
|
|
|
try: |
|
|
yield "กำลังดึงข่าว...", None, "กำลังดึงข่าว..." |
|
|
|
|
|
if search_type == "Latest News": |
|
|
news_list = scraper.get_latest_news(max_articles=int(num_articles)) |
|
|
elif search_type == "Stock Symbol": |
|
|
if not search_input: |
|
|
yield "⚠️ กรุณาใส่ ticker symbol (เช่น AAPL, TSLA)", None, None |
|
|
return |
|
|
news_list = scraper.get_latest_news(symbol=search_input.upper(), max_articles=int(num_articles)) |
|
|
else: |
|
|
if not search_input: |
|
|
yield "⚠️ กรุณาใส่คำค้นหา", None, None |
|
|
return |
|
|
news_list = scraper.search_news(keyword=search_input, max_articles=int(num_articles)) |
|
|
|
|
|
if not news_list: |
|
|
yield "❌ ไม่พบข่าว กรุณาลองใหม่อีกครั้ง", None, None |
|
|
return |
|
|
|
|
|
yield f"พบข่าว {len(news_list)} รายการ | กำลังวิเคราะห์...", None, f"พบข่าว {len(news_list)} รายการ | กำลังวิเคราะห์..." |
|
|
|
|
|
results = analyzer.analyze_batch(news_list) |
|
|
|
|
|
|
|
|
total = len(results) |
|
|
positive = sum(1 for r in results if r['sentiment'] == 'Positive') |
|
|
negative = sum(1 for r in results if r['sentiment'] == 'Negative') |
|
|
neutral = sum(1 for r in results if r['sentiment'] == 'Neutral') |
|
|
avg_score = sum(r['score'] for r in results) / total if total > 0 else 0 |
|
|
|
|
|
theme_counts = Counter(r.get('theme', 'N/A') for r in results) |
|
|
impact_counts = Counter(r.get('impact', 'N/A') for r in results) |
|
|
|
|
|
if positive > negative and positive > neutral: |
|
|
overall = "📈 Positive (Bullish)" |
|
|
elif negative > positive and negative > neutral: |
|
|
overall = "📉 Negative (Bearish)" |
|
|
else: |
|
|
overall = "📊 Neutral" |
|
|
|
|
|
summary = f"""## 📊 สรุปผลการวิเคราะห์ |
|
|
**ภาพรวม:** {overall} | **คะแนนเฉลี่ย:** {avg_score:.2f}/1.0 |
|
|
### การกระจาย Sentiment |
|
|
- 🟢 Positive: {positive} ({positive/total*100:.1f}%) |
|
|
- 🔴 Negative: {negative} ({negative/total*100:.1f}%) |
|
|
- 🟡 Neutral: {neutral} ({neutral/total*100:.1f}%) |
|
|
--- |
|
|
### 📌 สรุปหัวข้อ (Themes) |
|
|
""" |
|
|
for theme, count in theme_counts.most_common(): |
|
|
summary += f"- **{theme}:** {count} รายการ ({count/total*100:.1f}%)\n" |
|
|
|
|
|
summary += "\n---\n" |
|
|
|
|
|
summary += "\n### ⚡ สรุปผลกระทบ (Impact)\n" |
|
|
for impact, count in impact_counts.most_common(): |
|
|
emoji = get_impact_color(impact) |
|
|
summary += f"- {emoji} **{impact}:** {count} รายการ ({count/total*100:.1f}%)\n" |
|
|
|
|
|
summary += "\n\n---\n\n## 📰 รายละเอียดแต่ละข่าว\n\n" |
|
|
detailed_results = "" |
|
|
|
|
|
for i, result in enumerate(results, 1): |
|
|
s_emoji = get_sentiment_color(result['sentiment']) |
|
|
i_emoji = get_impact_color(result.get('impact', 'N/A')) |
|
|
|
|
|
detailed_results += f"""### {i}. {s_emoji} {result['title']} |
|
|
**Theme:** {result.get('theme', 'N/A')} | **Impact:** {i_emoji} {result.get('impact', 'N/A')} |
|
|
**Sentiment:** {result['sentiment']} | **Score:** {result['score']:.2f} |
|
|
**เผยแพร่:** {result['published']} |
|
|
**คำอธิบาย:** {result['explanation']} |
|
|
{result['summary'][:200]}{'...' if len(result['summary']) > 200 else ''} |
|
|
[🔗 อ่านต่อ]({result['link']}) |
|
|
---""" |
|
|
|
|
|
df_data = [] |
|
|
for result in results: |
|
|
df_data.append({ |
|
|
'Title': result['title'][:60] + '...' if len(result['title']) > 60 else result['title'], |
|
|
'Theme': result.get('theme', 'N/A'), |
|
|
'Impact': f"{get_impact_color(result.get('impact', 'N/A'))} {result.get('impact', 'N/A')}", |
|
|
'Sentiment': f"{get_sentiment_color(result['sentiment'])} {result['sentiment']}", |
|
|
'Score': f"{result['score']:.2f}", |
|
|
'Published': result['published'], |
|
|
'Link': result['link'] |
|
|
}) |
|
|
|
|
|
df = pd.DataFrame(df_data) |
|
|
final_output = summary + detailed_results |
|
|
|
|
|
yield final_output, df, "✅ วิเคราะห์เสร็จสมบูรณ์!" |
|
|
|
|
|
except Exception as e: |
|
|
error_msg = f"❌ เกิดข้อผิดพลาด: {str(e)}" |
|
|
yield error_msg, None, error_msg |
|
|
|
|
|
|
|
|
with gr.Blocks(title="Yahoo Finance News Analyzer", theme=gr.themes.Soft()) as demo: |
|
|
gr.Markdown(""" |
|
|
# 📈 Yahoo Finance News Analyzer |
|
|
วิเคราะห์ข่าวการเงินจาก Yahoo Finance ด้วย AI แบบครบวงจร |
|
|
|
|
|
🔍 **ฟีเจอร์:** |
|
|
- วิเคราะห์ Sentiment (ความรู้สึก) |
|
|
- วิเคราะห์ Theme (หัวข้อข่าว) |
|
|
- วิเคราะห์ Impact (ผลกระทบ: โอกาส หรือ ความเสี่ยง) |
|
|
- ให้คะแนนความมั่นใจและคำอธิบาย |
|
|
- รองรับค้นหาตาม Stock Symbol และ Keyword |
|
|
""") |
|
|
|
|
|
with gr.Row(): |
|
|
with gr.Column(scale=1): |
|
|
search_type = gr.Radio( |
|
|
choices=["Latest News", "Stock Symbol", "Keyword"], |
|
|
value="Latest News", |
|
|
label="ประเภทการค้นหา" |
|
|
) |
|
|
search_input = gr.Textbox( |
|
|
label="Ticker Symbol / Keyword", |
|
|
placeholder="เช่น AAPL, TSLA, AI, cryptocurrency", |
|
|
info="ใส่เฉพาะเมื่อเลือก Stock Symbol หรือ Keyword" |
|
|
) |
|
|
num_articles = gr.Slider( |
|
|
minimum=5, |
|
|
maximum=20, |
|
|
value=10, |
|
|
step=1, |
|
|
label="จำนวนข่าว" |
|
|
) |
|
|
analyze_btn = gr.Button("🔍 วิเคราะห์ข่าว", variant="primary", size="lg") |
|
|
status = gr.Textbox(label="สถานะ", interactive=False) |
|
|
|
|
|
with gr.Column(scale=2): |
|
|
results_md = gr.Markdown("### กดปุ่ม 'วิเคราะห์ข่าว' เพื่อเริ่มต้น") |
|
|
|
|
|
with gr.Row(): |
|
|
results_table = gr.Dataframe( |
|
|
label="📊 ตารางสรุปผล", |
|
|
wrap=True |
|
|
) |
|
|
|
|
|
gr.Markdown(""" |
|
|
--- |
|
|
### 💡 วิธีใช้งาน: |
|
|
1. เลือกประเภทการค้นหา (ข่าวล่าสุด / หุ้นเฉพาะ / คำค้นหา) |
|
|
2. ใส่ ticker symbol หรือ keyword (ถ้าต้องการ) |
|
|
3. เลือกจำนวนข่าวที่ต้องการวิเคราะห์ |
|
|
4. กดปุ่ม "วิเคราะห์ข่าว" |
|
|
|
|
|
### 🎯 ตัวอย่าง Stock Symbols: |
|
|
- **AAPL** - Apple Inc. |
|
|
- **TSLA** - Tesla |
|
|
- **NVDA** - Nvidia |
|
|
- **MSFT** - Microsoft |
|
|
- **GOOGL** - Google |
|
|
|
|
|
### 📌 หมายเหตุ: |
|
|
- **Sentiment:** Positive (ดี), Negative (แย่), Neutral (กลาง) |
|
|
- **Theme:** หัวข้อหลักของข่าว (เช่น ผลประกอบการ, สินค้าใหม่) |
|
|
- **Impact:** ผลกระทบต่อบริษัท (🟢 Opportunity - โอกาส, 🔴 Risk - ความเสี่ยง) |
|
|
""") |
|
|
|
|
|
analyze_btn.click( |
|
|
fn=analyze_news, |
|
|
inputs=[search_type, search_input, num_articles], |
|
|
outputs=[results_md, results_table, status] |
|
|
) |
|
|
|
|
|
|
|
|
if __name__ == "__main__": |
|
|
demo.queue() |
|
|
demo.launch(share=False) |