File size: 10,439 Bytes
880d704
 
 
99fb303
880d704
99fb303
880d704
d5e84de
 
d8db7a8
 
d5e84de
 
 
d8db7a8
880d704
 
99fb303
880d704
 
2c5f362
 
99fb303
2c5f362
 
d5e84de
880d704
 
 
 
d5e84de
 
 
 
 
 
 
 
 
 
 
 
 
880d704
d5e84de
2c5f362
880d704
 
 
 
 
 
 
 
 
 
 
 
2c5f362
880d704
 
 
2c5f362
d5e84de
2c5f362
880d704
2c5f362
d5e84de
880d704
 
 
 
 
2c5f362
 
 
 
880d704
 
 
 
 
 
 
2c5f362
 
 
880d704
 
 
d5e84de
2c5f362
880d704
2c5f362
 
880d704
d5e84de
 
2c5f362
 
 
 
d5e84de
 
 
880d704
6ccf417
 
 
2c5f362
6ccf417
2c5f362
 
 
6dead6e
 
880d704
2c5f362
880d704
 
 
 
 
2c5f362
 
880d704
 
 
 
 
2c5f362
880d704
 
2c5f362
880d704
2c5f362
880d704
 
 
 
d5e84de
d8db7a8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
import gradio as gr
import pandas as pd
from scraper import YahooFinanceScraper
from sentiment_analyzer import NewsAnalyzer # Import new class
import time
from collections import Counter # For counting themes/impacts

# --- MODIFIED BLOCK 1 ---
# เราจะโหลด Scraper (เบา) แต่จะยังไม่โหลด Analyzer (หนัก)
print("Initializing Yahoo Finance News Analyzer...")
scraper = YahooFinanceScraper()
analyzer = None # <-- ตั้งเป็น None ก่อน จะโหลดเมื่อถูกเรียกใช้ครั้งแรก
# --- END MODIFIED BLOCK ---


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):
    """
    ฟังก์ชันหลักในการวิเคราะห์ข่าว
    """
    
    # --- MODIFIED BLOCK 2: LAZY LOADING ---
    # ตรวจสอบว่า analyzer ถูกโหลดหรือยัง
    global analyzer
    if analyzer is None:
        # ถ้ายัง ให้แสดงสถานะว่ากำลังโหลด
        yield "⌛ กำลังโหลดโมเดล AI (ครั้งแรก)...", None, "⌛ กำลังโหลดโมเดล AI (ครั้งแรก)..."
        print("Analyzer not loaded. Initializing NewsAnalyzer (lazy)...")
        # นี่คือจุดที่จะโหลดโมเดล (ใช้เวลา 1-2 นาทีในครั้งแรก)
        analyzer = NewsAnalyzer() 
        print("Analyzer loaded successfully.")
    # --- END MODIFIED BLOCK ---
    
    try:
        yield "กำลังดึงข่าว...", None, "กำลังดึงข่าว..." # <-- แก้ไข Output ที่ 3 ด้วย

        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:  # Keyword search
            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

# --- (Interface Block: ไม่มีการเปลี่ยนแปลง) ---
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]
    )

# Launch app
if __name__ == "__main__":
    demo.queue()
    demo.launch(share=False)