Ruby20260314/StockData0401
0
1"""2櫃買中心 本益比/殖利率/股價淨值比 分析儀表板3執行方式:4 pip install streamlit pandas requests plotly openpyxl5 streamlit run tpex_streamlit_app.py6"""7 8import io9import requests10import pandas as pd11import plotly.express as px12import streamlit as st13 14# ── 頁面設定 ──────────────────────────────────────────────────────────────────15st.set_page_config(16 page_title="櫃買中心 股票指標分析",17 page_icon="📊",18 layout="wide",19)20 21st.title("📊 櫃買中心 股票指標分析儀表板")22st.caption("資料來源:臺灣證券交易所 TPEx 即時資料")23 24# ── 1. 抓取資料(快取 10 分鐘)────────────────────────────────────────────────25@st.cache_data(ttl=600)26def load_data():27 url = (28 "https://www.tpex.org.tw/web/stock/aftertrading/peratio_analysis/"29 "pera_result.php?l=zh-tw&o=data"30 )31 resp = requests.get(url, timeout=15)32 resp.encoding = "utf-8-sig"33 df = pd.read_csv(io.StringIO(resp.text), encoding="utf-8-sig")34 df = df.dropna()35 df["本益比"] = pd.to_numeric(df["本益比"], errors="coerce")36 df["殖利率"] = pd.to_numeric(df["殖利率"], errors="coerce")37 df["股價淨值比"] = pd.to_numeric(df["股價淨值比"], errors="coerce")38 df = df.dropna(subset=["本益比", "殖利率", "股價淨值比"])39 df["股票代號"] = df["股票代號"].astype(str)40 return df41 42with st.spinner("資料載入中…"):43 df = load_data()44 45st.success(f"✅ 共載入 {len(df)} 筆有效資料")46 47# ── 2. 側邊欄篩選器 ───────────────────────────────────────────────────────────48with st.sidebar:49 st.header("⚙️ 篩選條件")50 51 top_n = st.slider("顯示前幾筆(圖表)", 10, 100, 60, step=10)52 53 yield_threshold = st.number_input(54 "殖利率標記閾值(塗黃條件 >)", min_value=0.0, max_value=20.0,55 value=6.0, step=0.556 )57 58 pe_range = st.slider(59 "本益比範圍篩選",60 float(df["本益比"].min()), float(df["本益比"].max()),61 (float(df["本益比"].min()), float(df["本益比"].max())),62 )63 64 yield_range = st.slider(65 "殖利率範圍篩選",66 float(df["殖利率"].min()), float(df["殖利率"].max()),67 (float(df["殖利率"].min()), float(df["殖利率"].max())),68 )69 70 st.markdown("---")71 st.info("圖表支援互動操作(縮放、懸停、點擊圖例篩選)")72 73# ── 套用篩選 ──────────────────────────────────────────────────────────────────74filtered_df = df[75 df["本益比"].between(*pe_range) &76 df["殖利率"].between(*yield_range)77].copy()78 79plot_df = filtered_df.head(top_n).copy()80 81st.markdown(f"**篩選後:{len(filtered_df)} 筆 | 圖表顯示前 {top_n} 筆**")82 83# ── 3. KPI 摘要卡片 ───────────────────────────────────────────────────────────84col1, col2, col3, col4 = st.columns(4)85col1.metric("平均本益比", f"{filtered_df['本益比'].mean():.2f}")86col2.metric("平均殖利率", f"{filtered_df['殖利率'].mean():.2f}%")87col3.metric("平均股價淨值比", f"{filtered_df['股價淨值比'].mean():.2f}")88col4.metric(f"殖利率 > {yield_threshold}% 家數",89 f"{(filtered_df['殖利率'] > yield_threshold).sum()} 家")90 91st.divider()92 93# ── 4. 動態圓餅圖 ─────────────────────────────────────────────────────────────94st.subheader("🥧 殖利率分布圓餅圖")95fig_pie = px.pie(96 plot_df,97 names="股票代號",98 values="殖利率",99 title=f"股票殖利率分布(前 {top_n} 筆)",100 hover_data=["本益比", "股價淨值比"],101)102fig_pie.update_traces(textposition="inside", textinfo="label+percent")103fig_pie.update_layout(height=520)104st.plotly_chart(fig_pie, use_container_width=True)105 106st.divider()107 108# ── 5. 動態柱狀圖 ─────────────────────────────────────────────────────────────109st.subheader("📊 本益比柱狀圖")110fig_bar = px.bar(111 plot_df,112 x="股票代號",113 y="本益比",114 color="殖利率",115 color_continuous_scale="Viridis",116 title=f"各股票本益比(前 {top_n} 筆)",117 hover_data=["殖利率", "股價淨值比"],118 text="本益比",119)120fig_bar.update_traces(texttemplate="%{text:.1f}", textposition="outside")121fig_bar.update_layout(122 xaxis_tickangle=-45,123 coloraxis_colorbar_title="殖利率",124 height=520,125)126st.plotly_chart(fig_bar, use_container_width=True)127 128st.divider()129 130# ── 6. 動態旭日圖 ─────────────────────────────────────────────────────────────131st.subheader("☀️ 旭日圖(殖利率分組)")132 133def yield_group(v):134 if v >= 6: return "高殖利率(≥6%)"135 elif v >= 3: return "中殖利率(3~6%)"136 else: return "低殖利率(<3%)"137 138plot_df["殖利率分組"] = plot_df["殖利率"].apply(yield_group)139 140fig_sun = px.sunburst(141 plot_df,142 path=["殖利率分組", "股票代號"],143 values="股價淨值比",144 color="殖利率",145 color_continuous_scale="RdYlGn",146 title="旭日圖:殖利率分組 → 股票代號(大小 = 股價淨值比)",147 hover_data=["本益比"],148)149fig_sun.update_layout(height=600)150st.plotly_chart(fig_sun, use_container_width=True)151 152st.divider()153 154# ── 7. 資料表格(Styler 塗黃)────────────────────────────────────────────────155st.subheader(f"📋 資料明細(殖利率 > {yield_threshold}% 標記黃色)")156 157cols = ["股票代號", "本益比", "殖利率", "股價淨值比"]158export_df = filtered_df[cols].copy()159 160def highlight_yield(row):161 color = "background-color: #FFD700; color: #333;" if row["殖利率"] > yield_threshold else ""162 return [color] * len(row)163 164styled = (165 export_df.style166 .apply(highlight_yield, axis=1)167 .format({"本益比": "{:.2f}", "殖利率": "{:.2f}", "股價淨值比": "{:.2f}"})168)169 170st.dataframe(styled, use_container_width=True, height=420)171 172# ── 8. Excel 下載按鈕 ─────────────────────────────────────────────────────────173st.subheader("⬇️ 下載 Excel 報表")174 175def build_excel(df_export, threshold):176 buf = io.BytesIO()177 with pd.ExcelWriter(buf, engine="openpyxl") as writer:178 # 塗黃樣式179 styled_export = (180 df_export.style181 .apply(182 lambda row: ["background-color: yellow" if row["殖利率"] > threshold else "" for _ in row],183 axis=1184 )185 .format({"本益比": "{:.2f}", "殖利率": "{:.2f}", "股價淨值比": "{:.2f}"})186 )187 styled_export.to_excel(writer, sheet_name="個股指標", index=False)188 189 ws = writer.sheets["個股指標"]190 for col in ws.columns:191 max_len = max(len(str(cell.value)) if cell.value else 0 for cell in col)192 ws.column_dimensions[col[0].column_letter].width = max_len + 4193 194 wb = writer.book195 info_ws = wb.create_sheet("說明")196 info_ws["A1"] = f"黃色標記條件:殖利率 > {threshold}%"197 info_ws["A2"] = "資料來源:臺灣證券交易所 TPEx"198 199 return buf.getvalue()200 201excel_bytes = build_excel(export_df, yield_threshold)202 203st.download_button(204 label="📥 下載 Excel(含黃色標記)",205 data=excel_bytes,206 file_name="tpex_analysis.xlsx",207 mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",208)209 