|
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(',')] |
|
|
|
|
|
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'] |
|
|
|
|
|
output_text = "" |
|
for i, ticker_code in enumerate(ticker_list, 1): |
|
try: |
|
|
|
full_ticker = f"{ticker_code}.TW" |
|
|
|
|
|
etf = yf.Ticker(full_ticker) |
|
|
|
|
|
df = etf.history(period="1y") |
|
|
|
|
|
if df.empty: |
|
output_text += f"警告:未找到 {full_ticker} 的數據\n" |
|
continue |
|
|
|
|
|
results[ticker_code] = df |
|
|
|
|
|
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), |
|
title='台灣ETF走勢圖', |
|
xaxis_rangeslider_visible=False |
|
) |
|
|
|
|
|
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 |
|
|
|
|
|
def main(): |
|
demo = create_gradio_interface() |
|
demo.launch( |
|
share=True, |
|
server_name='0.0.0.0', |
|
server_port=7860 |
|
) |
|
|
|
|
|
if __name__ == "__main__": |
|
main() |