ETF_TW / app.py
Rooobert's picture
Create app.py
fdbabd0 verified
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()