File size: 4,373 Bytes
fdbabd0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import gradio as gr
import yfinance as yf
import pandas as pd
import plotly.graph_objs as go
from plotly.subplots import make_subplots

def fetch_etf_data(ticker_codes):
    """
    抓取台灣ETF歷史數據
    
    參數:
    ticker_codes (str): ETF代碼,逗號分隔
    
    返回:
    tuple: 數據框和圖表
    """
    # 拆分代碼
    ticker_list = [code.strip() for code in ticker_codes.split(',')]
    
    # 儲存所有ETF的數據
    results = {}
    
    # 創建子圖
    fig = make_subplots(
        rows=len(ticker_list), 
        cols=1, 
        shared_xaxes=True, 
        vertical_spacing=0.02,
        subplot_titles=[f"{code}.TW" for code in ticker_list]
    )

    # 顏色列表
    colors = ['blue', 'green', 'red', 'purple', 'orange']

    # 遍歷每個ETF代碼
    output_text = ""
    for i, ticker_code in enumerate(ticker_list, 1):
        try:
            # 添加 ".TW" 後綴(台灣交易所)
            full_ticker = f"{ticker_code}.TW"
            
            # 獲取ETF數據
            etf = yf.Ticker(full_ticker)
            
            # 獲取1年歷史數據
            df = etf.history(period="1y")
            
            # 如果數據為空,跳過此ETF
            if df.empty:
                output_text += f"警告:未找到 {full_ticker} 的數據\n"
                continue
            
            # 儲存數據
            results[ticker_code] = df
            
            # 添加K線圖
            fig.add_trace(
                go.Candlestick(
                    x=df.index,
                    open=df['Open'],
                    high=df['High'],
                    low=df['Low'],
                    close=df['Close'],
                    name=full_ticker,
                    increasing_line_color=colors[i % len(colors)],
                    decreasing_line_color='gray'
                ),
                row=i, col=1
            )
            
            # 添加成交量柱狀圖
            fig.add_trace(
                go.Bar(
                    x=df.index, 
                    y=df['Volume'], 
                    name=f'{full_ticker} 成交量',
                    opacity=0.5
                ),
                row=i, col=1
            )
            
            # 生成文本輸出
            output_text += f"\n{full_ticker} 最近數據:\n"
            output_text += df.tail().to_string() + "\n"
            
        except Exception as e:
            output_text += f"{ticker_code} 發生錯誤: {e}\n"

    # 更新佈局
    fig.update_layout(
        height=300 * len(ticker_list),  # 根據ETF數量調整高度
        title='台灣ETF走勢圖',
        xaxis_rangeslider_visible=False  # 關閉範圍滑塊
    )

    # 合併CSV
    if results:
        combined_df = pd.concat([df for df in results.values()], keys=results.keys(), names=['ETF', 'index'])
        combined_df.to_csv("combined_etf_data.csv", encoding='utf-8-sig')
        output_text += "\n所有ETF數據已合併保存到 combined_etf_data.csv"

    return fig, output_text

def create_gradio_interface():
    """
    創建Gradio界面
    """
    with gr.Blocks(title="台灣ETF數據分析") as demo:
        gr.Markdown("## 台灣ETF數據抓取與分析")
        
        with gr.Row():
            with gr.Column():
                # 輸入框
                ticker_input = gr.Textbox(
                    label="輸入ETF代碼", 
                    placeholder="例如:00878, 00940, 0050",
                    value="00878, 00940, 0050"
                )
                
                # 提交按鈕
                submit_btn = gr.Button("分析ETF")
            
            with gr.Column():
                # 文本輸出
                output_text = gr.Textbox(label="分析結果")
        
        # 圖表輸出
        output_plot = gr.Plot(label="ETF走勢圖")
        
        # 提交事件
        submit_btn.click(
            fn=fetch_etf_data, 
            inputs=ticker_input, 
            outputs=[output_plot, output_text]
        )
    
    return demo

# 啟動Gradio應用
def main():
    demo = create_gradio_interface()
    demo.launch(
        share=True,  # 創建公開連結
        server_name='0.0.0.0',  # 允許外部訪問
        server_port=7860  # 指定端口
    )

# 運行主程序
if __name__ == "__main__":
    main()