petertulip86 commited on
Commit
8b07166
·
verified ·
1 Parent(s): 5dd4cfa

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +39 -38
src/streamlit_app.py CHANGED
@@ -1473,7 +1473,8 @@ st.markdown("""
1473
  """, unsafe_allow_html=True)
1474
 
1475
  # ------ 功能函數 ------ #
1476
-
 
1477
  參數:
1478
  stock_ids (list): 股票代碼列表
1479
  period (str): 時間週期
@@ -1481,9 +1482,9 @@ st.markdown("""
1481
 
1482
  返回:
1483
  dict: 股票數據字典
1484
- """
1485
  stock_data = {}
1486
-
1487
  with st.spinner("正在獲取股票數據..."):
1488
  progress_bar = st.progress(0)
1489
  for i, stock_id in enumerate(stock_ids):
@@ -1517,7 +1518,7 @@ st.markdown("""
1517
  time.sleep(0.5) # 為了視覺效果添加的短暫延遲
1518
 
1519
  progress_bar.empty()
1520
-
1521
  return stock_data
1522
 
1523
  rs = gain / loss
@@ -1526,24 +1527,24 @@ st.markdown("""
1526
 
1527
  # 累積回報率
1528
  cumulative_return = (data.iloc[-1] / data.iloc[0] - 1) * 100
1529
-
1530
  # 年化回報率 (假設252個交易日)
1531
  days = len(data)
1532
  annualized_return = ((1 + cumulative_return / 100) ** (252 / days) - 1) * 100
1533
-
1534
  # 波動率 (年化標準差)
1535
  volatility = daily_returns.std() * np.sqrt(252) * 100
1536
-
1537
  # 夏普比率 (假設無風險利率為2%)
1538
  risk_free_rate = 0.02
1539
  sharpe_ratio = (annualized_return / 100 - risk_free_rate) / (volatility / 100)
1540
-
1541
  # 最大回撤
1542
  cum_returns = (1 + daily_returns).cumprod()
1543
  running_max = cum_returns.cummax()
1544
  drawdown = (cum_returns / running_max - 1) * 100
1545
  max_drawdown = drawdown.min()
1546
-
1547
  return {
1548
  'cumulative_return': cumulative_return,
1549
  'annualized_return': annualized_return,
@@ -1554,7 +1555,7 @@ st.markdown("""
1554
 
1555
  # 創建列來顯示指標
1556
  cols = st.columns(len(stock_data))
1557
-
1558
  for i, (stock_id, data) in enumerate(stock_data.items()):
1559
  hist = data['history']
1560
  if hist.empty:
@@ -1606,13 +1607,13 @@ st.markdown("""
1606
  vertical_spacing=0.1,
1607
  subplot_titles=("股價走勢比較", "正規化股價比較 (基準=100)"),
1608
  row_heights=[0.6, 0.4])
1609
-
1610
  colors = ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', '#9467bd']
1611
  color_idx = 0
1612
-
1613
  # 用於正規化的數據
1614
  normalized_data = {}
1615
-
1616
  # 添加每個股票的數據到圖表
1617
  for stock_id, data in stock_data.items():
1618
  hist = data['history']
@@ -1654,7 +1655,7 @@ st.markdown("""
1654
  ),
1655
  row=2, col=1
1656
  )
1657
-
1658
  # 更新布局
1659
  fig.update_layout(
1660
  height=700,
@@ -1670,24 +1671,24 @@ st.markdown("""
1670
  hovermode="x unified",
1671
  plot_bgcolor='rgba(240,242,246,0.8)',
1672
  )
1673
-
1674
  # 更新Y軸標題
1675
  fig.update_yaxes(title_text="價格 (TWD)", row=1, col=1, gridcolor='rgba(220,220,220,0.5)')
1676
  fig.update_yaxes(title_text="正規化價格 (基準=100)", row=2, col=1, gridcolor='rgba(220,220,220,0.5)')
1677
  fig.update_xaxes(gridcolor='rgba(220,220,220,0.5)')
1678
-
1679
  # 顯示圖表
1680
  st.plotly_chart(fig, use_container_width=True)
1681
 
1682
  # 創建圖表
1683
  fig = go.Figure()
1684
-
1685
  colors = ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', '#9467bd']
1686
  color_idx = 0
1687
-
1688
  # 添加每個股票的成交量到圖表
1689
  has_volume_data = False
1690
-
1691
  for stock_id, data in stock_data.items():
1692
  hist = data['history']
1693
  # 確保數據不為空且有成交量數據
@@ -1713,11 +1714,11 @@ st.markdown("""
1713
  opacity=0.7
1714
  )
1715
  )
1716
-
1717
  if not has_volume_data:
1718
  st.warning("沒有可用的成交量數據")
1719
  return
1720
-
1721
  # 更新布局
1722
  fig.update_layout(
1723
  title="股票成交量比較",
@@ -1736,10 +1737,10 @@ st.markdown("""
1736
  margin=dict(l=40, r=40, t=60, b=40),
1737
  plot_bgcolor='rgba(240,242,246,0.8)',
1738
  )
1739
-
1740
  fig.update_yaxes(title_text="成交量", gridcolor='rgba(220,220,220,0.5)')
1741
  fig.update_xaxes(gridcolor='rgba(220,220,220,0.5)')
1742
-
1743
  # 顯示圖表
1744
  st.plotly_chart(fig, use_container_width=True)
1745
 
@@ -1747,7 +1748,7 @@ st.markdown("""
1747
  if hist.empty:
1748
  st.warning("所選股票沒有足夠的歷史數據")
1749
  return
1750
-
1751
  # RSI 指標
1752
  if indicator_type == "RSI":
1753
  rsi = calculate_rsi(hist['Close'])
@@ -1809,7 +1810,7 @@ st.markdown("""
1809
  fig.update_xaxes(gridcolor='rgba(220,220,220,0.5)')
1810
 
1811
  st.plotly_chart(fig, use_container_width=True)
1812
-
1813
  # MACD 指標
1814
  elif indicator_type == "MACD":
1815
  macd, signal, histogram = calculate_macd(hist['Close'])
@@ -1888,7 +1889,7 @@ st.markdown("""
1888
  fig.update_xaxes(gridcolor='rgba(220,220,220,0.5)')
1889
 
1890
  st.plotly_chart(fig, use_container_width=True)
1891
-
1892
  # 布林帶
1893
  elif indicator_type == "布林帶":
1894
  ma, upper, lower = calculate_bollinger_bands(hist['Close'])
@@ -1972,7 +1973,7 @@ st.markdown("""
1972
  fig.update_xaxes(gridcolor='rgba(220,220,220,0.5)')
1973
 
1974
  st.plotly_chart(fig, use_container_width=True)
1975
-
1976
  # 移動��均線
1977
  elif indicator_type == "移動平均線":
1978
  ma20, ma50, ma100, ma200 = calculate_moving_averages(hist['Close'])
@@ -2059,17 +2060,17 @@ st.markdown("""
2059
  if not hist.empty:
2060
  metrics = calculate_performance_metrics(hist['Close'])
2061
  performance_metrics[stock_id] = metrics
2062
-
2063
  if not performance_metrics:
2064
  st.warning("沒有足夠的數據來計算績效指標")
2065
  return
2066
-
2067
  # 創建圖表數據
2068
  metrics_df = pd.DataFrame(performance_metrics).T
2069
-
2070
  # 創建多圖表比較
2071
  cols = st.columns(2)
2072
-
2073
  # 1. 累積回報率比較
2074
  with cols[0]:
2075
  fig_return = px.bar(
@@ -2098,7 +2099,7 @@ st.markdown("""
2098
  )
2099
 
2100
  st.plotly_chart(fig_return, use_container_width=True)
2101
-
2102
  # 2. 波動率比較
2103
  with cols[1]:
2104
  fig_vol = px.bar(
@@ -2127,9 +2128,9 @@ st.markdown("""
2127
  )
2128
 
2129
  st.plotly_chart(fig_vol, use_container_width=True)
2130
-
2131
  cols = st.columns(2)
2132
-
2133
  # 3. 夏普比率比較
2134
  with cols[0]:
2135
  sharpe_colors = ['#d62728' if s < 0 else '#2ca02c' for s in metrics_df['sharpe_ratio']]
@@ -2159,7 +2160,7 @@ st.markdown("""
2159
  )
2160
 
2161
  st.plotly_chart(fig_sharpe, use_container_width=True)
2162
-
2163
  # 4. 最大回撤比較
2164
  with cols[1]:
2165
  fig_drawdown = px.bar(
@@ -2188,10 +2189,10 @@ st.markdown("""
2188
  )
2189
 
2190
  st.plotly_chart(fig_drawdown, use_container_width=True)
2191
-
2192
  # 5. 績效指標比較表格
2193
  st.markdown("### 績效指標詳細比較")
2194
-
2195
  # 準備顯示的數據
2196
  display_df = pd.DataFrame({
2197
  '累積報酬率 (%)': metrics_df['cumulative_return'].round(2),
@@ -2200,7 +2201,7 @@ st.markdown("""
2200
  '夏普比率': metrics_df['sharpe_ratio'].round(2),
2201
  '最大回撤 (%)': metrics_df['max_drawdown'].round(2)
2202
  })
2203
-
2204
  # 表格高亮設定
2205
  st.dataframe(
2206
  display_df,
 
1473
  """, unsafe_allow_html=True)
1474
 
1475
  # ------ 功能函數 ------ #
1476
+ def get_stock_data(stock_ids, period="1y", interval="1d"):
1477
+ '''
1478
  參數:
1479
  stock_ids (list): 股票代碼列表
1480
  period (str): 時間週期
 
1482
 
1483
  返回:
1484
  dict: 股票數據字典
1485
+ '''
1486
  stock_data = {}
1487
+
1488
  with st.spinner("正在獲取股票數據..."):
1489
  progress_bar = st.progress(0)
1490
  for i, stock_id in enumerate(stock_ids):
 
1518
  time.sleep(0.5) # 為了視覺效果添加的短暫延遲
1519
 
1520
  progress_bar.empty()
1521
+
1522
  return stock_data
1523
 
1524
  rs = gain / loss
 
1527
 
1528
  # 累積回報率
1529
  cumulative_return = (data.iloc[-1] / data.iloc[0] - 1) * 100
1530
+
1531
  # 年化回報率 (假設252個交易日)
1532
  days = len(data)
1533
  annualized_return = ((1 + cumulative_return / 100) ** (252 / days) - 1) * 100
1534
+
1535
  # 波動率 (年化標準差)
1536
  volatility = daily_returns.std() * np.sqrt(252) * 100
1537
+
1538
  # 夏普比率 (假設無風險利率為2%)
1539
  risk_free_rate = 0.02
1540
  sharpe_ratio = (annualized_return / 100 - risk_free_rate) / (volatility / 100)
1541
+
1542
  # 最大回撤
1543
  cum_returns = (1 + daily_returns).cumprod()
1544
  running_max = cum_returns.cummax()
1545
  drawdown = (cum_returns / running_max - 1) * 100
1546
  max_drawdown = drawdown.min()
1547
+
1548
  return {
1549
  'cumulative_return': cumulative_return,
1550
  'annualized_return': annualized_return,
 
1555
 
1556
  # 創建列來顯示指標
1557
  cols = st.columns(len(stock_data))
1558
+
1559
  for i, (stock_id, data) in enumerate(stock_data.items()):
1560
  hist = data['history']
1561
  if hist.empty:
 
1607
  vertical_spacing=0.1,
1608
  subplot_titles=("股價走勢比較", "正規化股價比較 (基準=100)"),
1609
  row_heights=[0.6, 0.4])
1610
+
1611
  colors = ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', '#9467bd']
1612
  color_idx = 0
1613
+
1614
  # 用於正規化的數據
1615
  normalized_data = {}
1616
+
1617
  # 添加每個股票的數據到圖表
1618
  for stock_id, data in stock_data.items():
1619
  hist = data['history']
 
1655
  ),
1656
  row=2, col=1
1657
  )
1658
+
1659
  # 更新布局
1660
  fig.update_layout(
1661
  height=700,
 
1671
  hovermode="x unified",
1672
  plot_bgcolor='rgba(240,242,246,0.8)',
1673
  )
1674
+
1675
  # 更新Y軸標題
1676
  fig.update_yaxes(title_text="價格 (TWD)", row=1, col=1, gridcolor='rgba(220,220,220,0.5)')
1677
  fig.update_yaxes(title_text="正規化價格 (基準=100)", row=2, col=1, gridcolor='rgba(220,220,220,0.5)')
1678
  fig.update_xaxes(gridcolor='rgba(220,220,220,0.5)')
1679
+
1680
  # 顯示圖表
1681
  st.plotly_chart(fig, use_container_width=True)
1682
 
1683
  # 創建圖表
1684
  fig = go.Figure()
1685
+
1686
  colors = ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', '#9467bd']
1687
  color_idx = 0
1688
+
1689
  # 添加每個股票的成交量到圖表
1690
  has_volume_data = False
1691
+
1692
  for stock_id, data in stock_data.items():
1693
  hist = data['history']
1694
  # 確保數據不為空且有成交量數據
 
1714
  opacity=0.7
1715
  )
1716
  )
1717
+
1718
  if not has_volume_data:
1719
  st.warning("沒有可用的成交量數據")
1720
  return
1721
+
1722
  # 更新布局
1723
  fig.update_layout(
1724
  title="股票成交量比較",
 
1737
  margin=dict(l=40, r=40, t=60, b=40),
1738
  plot_bgcolor='rgba(240,242,246,0.8)',
1739
  )
1740
+
1741
  fig.update_yaxes(title_text="成交量", gridcolor='rgba(220,220,220,0.5)')
1742
  fig.update_xaxes(gridcolor='rgba(220,220,220,0.5)')
1743
+
1744
  # 顯示圖表
1745
  st.plotly_chart(fig, use_container_width=True)
1746
 
 
1748
  if hist.empty:
1749
  st.warning("所選股票沒有足夠的歷史數據")
1750
  return
1751
+
1752
  # RSI 指標
1753
  if indicator_type == "RSI":
1754
  rsi = calculate_rsi(hist['Close'])
 
1810
  fig.update_xaxes(gridcolor='rgba(220,220,220,0.5)')
1811
 
1812
  st.plotly_chart(fig, use_container_width=True)
1813
+
1814
  # MACD 指標
1815
  elif indicator_type == "MACD":
1816
  macd, signal, histogram = calculate_macd(hist['Close'])
 
1889
  fig.update_xaxes(gridcolor='rgba(220,220,220,0.5)')
1890
 
1891
  st.plotly_chart(fig, use_container_width=True)
1892
+
1893
  # 布林帶
1894
  elif indicator_type == "布林帶":
1895
  ma, upper, lower = calculate_bollinger_bands(hist['Close'])
 
1973
  fig.update_xaxes(gridcolor='rgba(220,220,220,0.5)')
1974
 
1975
  st.plotly_chart(fig, use_container_width=True)
1976
+
1977
  # 移動��均線
1978
  elif indicator_type == "移動平均線":
1979
  ma20, ma50, ma100, ma200 = calculate_moving_averages(hist['Close'])
 
2060
  if not hist.empty:
2061
  metrics = calculate_performance_metrics(hist['Close'])
2062
  performance_metrics[stock_id] = metrics
2063
+
2064
  if not performance_metrics:
2065
  st.warning("沒有足夠的數據來計算績效指標")
2066
  return
2067
+
2068
  # 創建圖表數據
2069
  metrics_df = pd.DataFrame(performance_metrics).T
2070
+
2071
  # 創建多圖表比較
2072
  cols = st.columns(2)
2073
+
2074
  # 1. 累積回報率比較
2075
  with cols[0]:
2076
  fig_return = px.bar(
 
2099
  )
2100
 
2101
  st.plotly_chart(fig_return, use_container_width=True)
2102
+
2103
  # 2. 波動率比較
2104
  with cols[1]:
2105
  fig_vol = px.bar(
 
2128
  )
2129
 
2130
  st.plotly_chart(fig_vol, use_container_width=True)
2131
+
2132
  cols = st.columns(2)
2133
+
2134
  # 3. 夏普比率比較
2135
  with cols[0]:
2136
  sharpe_colors = ['#d62728' if s < 0 else '#2ca02c' for s in metrics_df['sharpe_ratio']]
 
2160
  )
2161
 
2162
  st.plotly_chart(fig_sharpe, use_container_width=True)
2163
+
2164
  # 4. 最大回撤比較
2165
  with cols[1]:
2166
  fig_drawdown = px.bar(
 
2189
  )
2190
 
2191
  st.plotly_chart(fig_drawdown, use_container_width=True)
2192
+
2193
  # 5. 績效指標比較表格
2194
  st.markdown("### 績效指標詳細比較")
2195
+
2196
  # 準備顯示的數據
2197
  display_df = pd.DataFrame({
2198
  '累積報酬率 (%)': metrics_df['cumulative_return'].round(2),
 
2201
  '夏普比率': metrics_df['sharpe_ratio'].round(2),
2202
  '最大回撤 (%)': metrics_df['max_drawdown'].round(2)
2203
  })
2204
+
2205
  # 表格高亮設定
2206
  st.dataframe(
2207
  display_df,