Rooobert commited on
Commit
99eeda2
1 Parent(s): 10cd19c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +142 -73
app.py CHANGED
@@ -1,13 +1,102 @@
1
  import streamlit as st
 
2
  import twstock
 
 
3
  import pandas as pd
4
- import matplotlib.pyplot as plt
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
 
6
  def fetch_recent_stock_data(stock_code):
7
  """
8
- 使用 twstock 獲取近期股票交易數據
9
  """
10
  try:
 
11
  stock = twstock.Stock(stock_code)
12
  recent_data = stock.fetch_31() # 抓取最近 31 天的交易數據
13
 
@@ -18,100 +107,80 @@ def fetch_recent_stock_data(stock_code):
18
  # 將數據整理為 DataFrame 格式
19
  data_list = [
20
  {
21
- "Date": data.date.strftime('%Y-%m-%d'),
22
- "Open": data.open,
23
- "High": data.high,
24
- "Low": data.low,
25
- "Close": data.close,
26
- "Transaction": data.transaction,
27
- "Capacity": data.capacity,
28
- "Turnover": data.turnover
29
  }
30
  for data in recent_data
31
  ]
32
  df = pd.DataFrame(data_list)
33
- df['Date'] = pd.to_datetime(df['Date'])
34
  return df
35
 
36
  except Exception as e:
37
  st.error(f"發生錯誤: {e}")
 
38
  return None
39
 
40
- def plot_stock_price(df):
41
- """
42
- 使用 matplotlib 繪製股價走勢
43
- """
44
- plt.figure(figsize=(12, 6))
45
- plt.plot(df['Date'], df['Close'], label='收盤價')
46
- plt.plot(df['Date'], df['Close'].rolling(window=5).mean(), label='5日移動平均', linestyle='--')
47
- plt.title('股價走勢')
48
- plt.xlabel('日期')
49
- plt.ylabel('股價')
50
- plt.legend()
51
- plt.xticks(rotation=45)
52
- plt.tight_layout()
53
- return plt
54
-
55
  def main():
56
- st.set_page_config(page_title="��股分析工具", page_icon=":chart_with_upwards_trend:", layout="wide")
57
-
58
- st.title("🚀 台股分析工具")
59
 
60
- # 側邊欄設置
61
- with st.sidebar:
62
- st.header("股票分析")
63
-
64
- # 股票代碼輸入
65
- stock_code = st.text_input(
66
- "股票代號",
67
- value="2330",
68
- placeholder="例如: 2330"
69
  )
70
 
71
- # 股票分析頁籤
72
- tab1, tab2 = st.tabs(["股價走勢圖", "近期交易數據"])
 
 
 
 
73
 
74
- with tab1:
75
- # 股價走勢圖
76
- if st.button("繪製股價走勢圖"):
77
- # 獲取股票數據
78
- df = fetch_recent_stock_data(stock_code)
79
-
80
- if df is not None:
81
- # 繪製股價圖
82
- fig = plot_stock_price(df)
83
- st.pyplot(fig)
84
 
85
- with tab2:
86
- # 近期交易數據
87
- st.subheader("個股近期交易數據")
88
-
89
- if st.button("查詢交易數據"):
90
- # 獲取近期股票數據
 
 
 
91
  df = fetch_recent_stock_data(stock_code)
92
 
93
  if df is not None:
94
- # 顯示數據
 
95
  st.dataframe(df)
96
-
97
- # 統計資訊
98
  st.subheader("基本統計")
99
  col1, col2, col3 = st.columns(3)
100
-
101
- with col1:
102
- st.metric("平均收盤價", f"{df['Close'].mean():.2f}")
103
-
104
- with col2:
105
- st.metric("最高價", f"{df['High'].max():.2f}")
106
-
107
- with col3:
108
- st.metric("最低價", f"{df['Low'].min():.2f}")
109
-
110
- # 匯出 CSV
111
- csv_data = df.to_csv(index=False).encode('utf-8-sig')
112
  st.download_button(
113
- label="下載CSV",
114
- data=csv_data,
115
  file_name=f"{stock_code}_recent_30days.csv",
116
  mime="text/csv"
117
  )
 
1
  import streamlit as st
2
+ import yfinance as yf
3
  import twstock
4
+ import plotly.graph_objects as go
5
+ from plotly.subplots import make_subplots
6
  import pandas as pd
7
+ from datetime import datetime, timedelta
8
+
9
+ def plot_stock_data(stock_symbols, period='1y'):
10
+ """
11
+ 繪製股票價格圖表
12
+ :param stock_symbols: 股票代號列表
13
+ :param period: 時間區間
14
+ :return: Plotly figure
15
+ """
16
+ # 創建子圖
17
+ fig = make_subplots(
18
+ rows=len(stock_symbols),
19
+ cols=1,
20
+ subplot_titles=[f"股價走勢: {symbol}" for symbol in stock_symbols],
21
+ vertical_spacing=0.05,
22
+ specs=[[{"secondary_y": True}] for _ in stock_symbols]
23
+ )
24
+
25
+ # 為每個股票繪製圖形
26
+ for idx, symbol in enumerate(stock_symbols, 1):
27
+ try:
28
+ # 獲取股票數據
29
+ stock = yf.Ticker(symbol)
30
+ df = stock.history(period=period)
31
+
32
+ if df.empty:
33
+ st.warning(f"無法獲取 {symbol} 的股票數據")
34
+ continue
35
+
36
+ # 添加蠟燭圖
37
+ fig.add_trace(
38
+ go.Candlestick(
39
+ x=df.index,
40
+ open=df['Open'],
41
+ high=df['High'],
42
+ low=df['Low'],
43
+ close=df['Close'],
44
+ name=f'{symbol} 價格'
45
+ ),
46
+ row=idx, col=1
47
+ )
48
+
49
+ # 添加成交量柱狀圖
50
+ fig.add_trace(
51
+ go.Bar(
52
+ x=df.index,
53
+ y=df['Volume'],
54
+ name=f'{symbol} 成交量',
55
+ opacity=0.3
56
+ ),
57
+ row=idx, col=1,
58
+ secondary_y=True
59
+ )
60
+
61
+ # 添加移動平均線
62
+ for ma_days in [5, 20, 60]:
63
+ ma = df['Close'].rolling(window=ma_days).mean()
64
+ fig.add_trace(
65
+ go.Scatter(
66
+ x=df.index,
67
+ y=ma,
68
+ name=f'{symbol} MA{ma_days}',
69
+ line=dict(width=1)
70
+ ),
71
+ row=idx, col=1
72
+ )
73
+
74
+ except Exception as e:
75
+ st.error(f"處理 {symbol} 時發生錯誤: {str(e)}")
76
+
77
+ # 更新布局
78
+ fig.update_layout(
79
+ height=400 * len(stock_symbols),
80
+ title_text="台股分析圖",
81
+ showlegend=True,
82
+ xaxis_rangeslider_visible=False,
83
+ template="plotly_white"
84
+ )
85
+
86
+ # 更新軸標籤
87
+ for i in range(1, len(stock_symbols) + 1):
88
+ fig.update_xaxes(title_text="日期", row=i, col=1)
89
+ fig.update_yaxes(title_text="價格 (TWD)", row=i, col=1)
90
+ fig.update_yaxes(title_text="成交量", row=i, col=1, secondary_y=True)
91
+
92
+ return fig
93
 
94
  def fetch_recent_stock_data(stock_code):
95
  """
96
+ 使用 twstock 獲取近 30 天的交易數據
97
  """
98
  try:
99
+ # 使用 twstock 獲取近 30 天的交易數據
100
  stock = twstock.Stock(stock_code)
101
  recent_data = stock.fetch_31() # 抓取最近 31 天的交易數據
102
 
 
107
  # 將數據整理為 DataFrame 格式
108
  data_list = [
109
  {
110
+ "Date": data.date.strftime('%Y-%m-%d'), # 日期
111
+ "Open": data.open, # 開盤價
112
+ "High": data.high, # 最高價
113
+ "Low": data.low, # 最低價
114
+ "Close": data.close, # 收盤價
115
+ "Transaction": data.transaction, # 成交筆數
116
+ "Capacity": data.capacity, # 成交股數
117
+ "Turnover": data.turnover # 成交金額
118
  }
119
  for data in recent_data
120
  ]
121
  df = pd.DataFrame(data_list)
122
+
123
  return df
124
 
125
  except Exception as e:
126
  st.error(f"發生錯誤: {e}")
127
+ st.error("請確認股票代碼是否正確,或是否為台股上市/上櫃股票。")
128
  return None
129
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
130
  def main():
131
+ st.set_page_config(page_title="台股分析工具", layout="wide")
132
+ st.title("台股分析工具")
 
133
 
134
+ # 選擇分析模式
135
+ mode = st.sidebar.radio("選擇分析模式", ["歷史股價圖", "近期交易資料"])
136
+
137
+ if mode == "歷史股價圖":
138
+ # 股票代號輸入
139
+ stock_input = st.text_input(
140
+ "股票代號 (用逗號分隔)",
141
+ value="2330.TW,2454.TW",
142
+ placeholder="例如: 2330,2454"
143
  )
144
 
145
+ # 時間區間選擇
146
+ period_select = st.selectbox(
147
+ "時間區間",
148
+ ["1mo", "3mo", "6mo", "1y", "2y", "5y", "max"],
149
+ index=3 # 預設為 1y
150
+ )
151
 
152
+ # 處理股票代號
153
+ stocks = [s.strip() for s in stock_input.split(',')]
154
+ stocks = [f"{s}.TW" if not s.endswith('.TW') and s.isdigit() else s for s in stocks]
 
 
 
 
 
 
 
155
 
156
+ # 繪製圖表按鈕
157
+ if st.button("繪製圖表"):
158
+ fig = plot_stock_data(stocks, period_select)
159
+ st.plotly_chart(fig, use_container_width=True)
160
+
161
+ else: # 近期交易資料模式
162
+ stock_code = st.text_input("請輸入股票代碼", value="2330")
163
+
164
+ if st.button("查詢資料"):
165
  df = fetch_recent_stock_data(stock_code)
166
 
167
  if df is not None:
168
+ # 顯示 DataFrame
169
+ st.subheader(f"股票代碼: {stock_code} - 最近30天交易數據")
170
  st.dataframe(df)
171
+
172
+ # 基本統計
173
  st.subheader("基本統計")
174
  col1, col2, col3 = st.columns(3)
175
+ col1.metric("平均收盤價", f"{df['Close'].mean():.2f}")
176
+ col2.metric("最高價", f"{df['High'].max():.2f}")
177
+ col3.metric("最低價", f"{df['Low'].min():.2f}")
178
+
179
+ # 下載 CSV
180
+ csv = df.to_csv(index=False, encoding="utf-8-sig")
 
 
 
 
 
 
181
  st.download_button(
182
+ label="下載 CSV 檔案",
183
+ data=csv,
184
  file_name=f"{stock_code}_recent_30days.csv",
185
  mime="text/csv"
186
  )