《台灣股市產業清單擷取模組》
在打造六國股市資料擷取系統的過程中,台灣市場是最值得優先處理的模組之一。除了資料來源穩定、格式清晰,台灣股市還具備一項獨特優勢:官方網站直接提供上市、上櫃、興櫃的完整產業分類清單,而且可以透過 pandas.read_html() 一鍵解析。
這篇文章將帶你一步步建構「台灣股市產業清單擷取模組」,並最終輸出一份可供後續分析使用的 stock_metadata.csv,內容包含:
- 股票代碼(含市場後綴 .TW / .TWO)
- 股票名稱
- 所屬產業別
- 上市/上櫃/興櫃分類
- 發行日與 ISIN 編碼(國際證券代碼)
import pandas as pd
import requests
# 取得上市股票產業資料
res = requests.get("https://isin.twse.com.tw/isin/class_main.jsp?market=1&issuetype=1&Page=1&chklike=Y")
tse_df = pd.read_html(res.text)[0]
tse_df.columns = tse_df.iloc[0]
tse_df = tse_df.iloc[1:]
# 取得上櫃股票產業資料
otc_res = requests.get("https://isin.twse.com.tw/isin/class_main.jsp?market=2&issuetype=4&Page=1&chklike=Y")
otc_df = pd.read_html(otc_res.text)[0]
otc_df.columns = otc_df.iloc[0]
otc_df = otc_df.iloc[1:]
# 取得興櫃股票產業資料
rotc_res = requests.get("https://isin.twse.com.tw/isin/class_main.jsp?owncode=&stockname=&isincode=&market=E&issuetype=R&industry_code=&Page=1&chklike=Y")
rotc_df = pd.read_html(rotc_res.text)[0]
rotc_df.columns = rotc_df.iloc[0]
rotc_df = rotc_df.iloc[1:]
# 合併所有資料
all_df = pd.concat([tse_df, otc_df, rotc_df], ignore_index=True)
# 重新命名欄位以符合所需格式
all_df.rename(columns={
"有價證券代號": "stock_id",
"有價證券名稱": "stock_name",
"產業別": "industry",
"市場別": "market"
}, inplace=True)
# 建立完整的股票代碼(加上市場後綴)
def add_market_suffix(row):
if row["market"] == "上市":
return f"{row['stock_id']}.TW"
elif row["market"] in ["上櫃", "興櫃"]:
return f"{row['stock_id']}.TWO"
return row["stock_id"]
all_df["stock_id"] = all_df.apply(add_market_suffix, axis=1)
# 選擇需要的欄位
final_df = all_df[["stock_id", "stock_name", "industry"]]
# 加入其他可能需要的元資料
final_df["listed_date"] = pd.to_datetime(all_df["公開發行/上市(櫃)/發行日"], errors="coerce")
final_df["isin"] = all_df["國際證券編碼"]
# 儲存為 CSV 檔案
output_path = "/content/stock_metadata.csv"
final_df.to_csv(output_path, index=False, encoding="utf-8-sig")
print(f"✅ 股票元資料已儲存至:{output_path}")
print(f"📊 總筆數:{len(final_df)}")
print(final_df.head())
📘 模組介紹:台灣股市清單擷取雷達(含創新版與權證)
這段程式碼是台灣市場的「清單雷達模組」,能夠一次性擷取所有主要市場的股票代碼與名稱,並自動加上.TW / .TWO 後綴,與日K資料格式一致。模組特色如下:⚙️ 模組功能亮點- ✅ 多市場並行擷取:使用 ThreadPoolExecutor 同時抓取各類清單
- ✅ 自動加後綴:依市場別補上 .TW / .TWO,與日K資料一致
- ✅ 權證獨立處理:將權證名稱獨立收集,避免混入股票清單
- ✅ 創新版支援:涵蓋創新版 A(TWO)與 C(TW),完整性提升
- ✅ 清單格式統一:輸出格式為 stockid、stockname、Warrantid
📊 執行結果範例
- 抓取股票代碼數量:約 2000+
- 抓取股票名稱數量:與代碼一致
- 抓取權證數量:數千筆以上(依市場狀況而定)
- 顯示前10筆 stockname、stockid、權證名稱,方便快速驗證
import requests
from io import StringIO
import pandas as pd
import concurrent.futures
import time
import subprocess
import sys
# 自動安裝 tqdm 如果未安裝
try:
from tqdm import tqdm
except ImportError:
print("📦 未安裝 tqdm,正在自動安裝...")
subprocess.check_call([sys.executable, "-m", "pip", "install", "tqdm"])
from tqdm import tqdm
# 設定全域變數
stockid = []
stockname = []
Warrantid = []
# 定義各類證券網址,加入創新版 A 和 C
url_configs = [
{'name': 'listed', 'url': 'https://isin.twse.com.tw/isin/class_main.jsp?market=1&issuetype=1&Page=1&chklike=Y', 'suffix': '.TW'},
{'name': 'dr', 'url': 'https://isin.twse.com.tw/isin/class_main.jsp?owncode=&stockname=&isincode=&market=1&issuetype=J&industry_code=&Page=1&chklike=Y', 'suffix': '.TW'},
{'name': 'warrant', 'url': 'https://isin.twse.com.tw/isin/class_main.jsp?owncode=&stockname=&isincode=&market=1&issuetype=2&industry_code=&Page=1&chklike=Y', 'suffix': None},
{'name': 'otc', 'url': 'https://isin.twse.com.tw/isin/class_main.jsp?market=2&issuetype=4&Page=1&chklike=Y', 'suffix': '.TWO'},
{'name': 'otc_warrant', 'url': 'https://isin.twse.com.tw/isin/class_main.jsp?owncode=&stockname=&isincode=&market=2&issuetype=2&industry_code=&Page=1&chklike=Y', 'suffix': None},
{'name': 'etf', 'url': 'https://isin.twse.com.tw/isin/class_main.jsp?owncode=&stockname=&isincode=&market=1&issuetype=I&industry_code=&Page=1&chklike=Y', 'suffix': '.TW'},
{'name': 'rotc', 'url': 'https://isin.twse.com.tw/isin/class_main.jsp?owncode=&stockname=&isincode=&market=E&issuetype=R&industry_code=&Page=1&chklike=Y', 'suffix': '.TWO'},
# ✅ 新增創新版 C 市場(TW)
{'name': 'tw_innovation', 'url': 'https://isin.twse.com.tw/isin/class_main.jsp?owncode=&stockname=&isincode=&market=C&issuetype=C&industry_code=&Page=1&chklike=Y', 'suffix': '.TW'},
# ✅ 新增創新版 A 市場(TWO)
{'name': 'otc_innovation', 'url': 'https://isin.twse.com.tw/isin/class_main.jsp?owncode=&stockname=&isincode=&market=A&issuetype=C&industry_code=&Page=1&chklike=Y', 'suffix': '.TWO'},
]
def fetch_data(url_config):
"""抓取單一類別證券資料的函數"""
try:
time.sleep(0.5)
response = requests.get(url_config['url'], timeout=10)
response.raise_for_status()
df = pd.read_html(StringIO(response.text), header=0)[0]
# 處理權證類別
if 'warrant' in url_config['name']:
warrant_names = [str(row['有價證券名稱']) for _, row in df.iterrows()]
return {'type': 'warrant', 'data': warrant_names}
# 處理一般證券類別
stock_ids = []
stock_names = []
for _, row in df.iterrows():
code_col = '有價證券代號'
name_col = '有價證券名稱'
stock_code = str(row[code_col]).strip()
stock_label = str(row[name_col]).strip()
suffix = url_config['suffix']
if not stock_code or suffix is None:
continue
stock_id = f"{stock_code}{suffix}"
stock_ids.append(stock_id)
stock_names.append(f"{stock_id}&{stock_label}")
return {'type': 'stock', 'ids': stock_ids, 'names': stock_names}
except Exception as e:
print(f"❌ 抓取 {url_config['name']} 時發生錯誤: {str(e)}")
return None
def main():
global stockid, stockname, Warrantid
print("🚀 開始抓取證券資料...")
# 使用執行緒池處理所有類別,加入 tqdm 進度條
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
# 提交所有任務
futures = [executor.submit(fetch_data, config) for config in url_configs]
# 使用 tqdm 顯示進度
for future in tqdm(concurrent.futures.as_completed(futures),
total=len(futures),
desc="📊 抓取進度",
unit="類別"):
result = future.result()
if not result:
continue
if result['type'] == 'stock':
stockid.extend(result['ids'])
stockname.extend(result['names'])
elif result['type'] == 'warrant':
Warrantid.extend(result['data'])
Warrantid.sort()
# 顯示統計與前10筆
print(f"\n✅ 完成!統計結果:")
print(f"📈 總共抓取 stockid 數量: {len(stockid)}")
print(f"📈 總共抓取 stockname 數量: {len(stockname)}")
print(f"📈 總共抓取權證數量: {len(Warrantid)}")
print("\n🔹 前10個 stockname 範例:")
for i in range(min(10, len(stockname))):
print(f" {stockname[i]}")
print("\n🔹 前10個 stockid 範例:")
for i in range(min(10, len(stockid))):
print(f" {stockid[i]}")
print("\n🔹 前10個權證範例:")
for i in range(min(10, len(Warrantid))):
print(f" {Warrantid[i]}")
if __name__ == "__main__":
main()
這是主力模組,負責根據 stockname 清單,從 Yahoo Finance 擷取每檔股票的完整歷史日K資料,並儲存為 CSV。
🧠 智慧續傳機制:
- 每次執行會自動建立或讀取 checkpoint_tw.csv
- 若檔案已存在且大小合理(>100 bytes),自動跳過
- 若下載失敗,會記錄錯誤訊息並保留於 checkpoint,供下次續傳
- 可隨時中斷與重啟,不會重複下載已完成的檔案
⚙️ 多執行緒加速:
- 使用 ThreadPoolExecutor 同時下載多檔(預設 8 執行緒)
- 每檔下載後即時更新 checkpoint,避免資料遺失
- 加入 time.sleep(0.1) 降低被 Yahoo 節流風險
📁 輸出內容:
- 每檔股票一個 CSV,命名格式為:<stock_id>_<stock_name>.csv
- 儲存於:/content/drive/MyDrive/各國股票檔案/tw-share/dayK/
- 同步輸出:
- checkpoint_tw.csv:續傳狀態
- success_list.csv:成功清單與筆數
- failed_list.csv:失敗清單與錯誤訊息
- download_tw_YYYYMMDD.txt:完整日誌
# -*- coding: utf-8 -*-
# =========================================================
# 台股歷史資料下載器(多執行緒;✅ 智慧續傳版;✅ 生產就緒)
# 存放路徑:/content/drive/MyDrive/各國股票檔案/tw-share/dayK
# Log 路徑:/content/drive/MyDrive/Log/台股日K資料下載器/
# Copyright (c) 2025 <Your Name>
# License: MIT
#
# 免責聲明:
# 1) 僅供研究與教學,不構成投資建議。
# 2) 資料來自 Yahoo Finance,可能有延遲或錯誤。
# 3) 再執行一次即可續傳未完成項目。
# =========================================================
import os, time, threading
from concurrent.futures import ThreadPoolExecutor, as_completed
import pandas as pd
import yfinance as yf
from tqdm import tqdm
from pathlib import Path
# ========== 使用者可調整參數 ==========
# 🔽 你可以修改以下參數以適應需求
MARKET_CODE = "tw-share" # 資料夾名稱(可用 cn-share, us-share...)
DATA_SUBDIR = "dayK" # 日K子資料夾名(可改為 daily, kbar...)
PROJECT_NAME = "台股日K資料下載器" # 專案名稱(用於 Log)
BASE_DATA_DIR = f"/content/drive/MyDrive/各國股票檔案/{MARKET_CODE}"
DATA_DIR = f"{BASE_DATA_DIR}/{DATA_SUBDIR}"
LOG_PARENT_DIR = "/content/drive/MyDrive/各國股票檔案/Log"
LOG_DIR = f"{LOG_PARENT_DIR}/{PROJECT_NAME}"
CKPT_FILE = f"{LOG_DIR}/checkpoint_tw.csv"
MAX_WORKERS = 8 # 多執行緒數量(Colab 免費版建議 5~8)
RETRY_DELAY = 1.0 # 下載失敗後等待秒數
MIN_FILE_SIZE = 100 # 判斷檔案是否有效的最小 byte 數
AUTO_ADJUST = False # 是否使用 yfinance 的 auto_adjust(建議 False)
# ========== 環境檢查與路徑建立 ==========
try:
from google.colab import drive
print("🔗 正在掛載 Google Drive...")
drive.mount('/content/drive', force_remount=False)
print("✅ Drive 已掛載")
except Exception:
raise RuntimeError("此程式設計於 Google Colab 環境使用,請在 Colab 中執行。")
# 建立所有需要的資料夾
for d in [DATA_DIR, LOG_DIR]:
Path(d).mkdir(parents=True, exist_ok=True)
# ========== 日誌函數 ==========
def log(msg: str):
now = pd.Timestamp.now()
log_path = f"{LOG_DIR}/download_tw_{now:%Y%m%d}.txt"
with open(log_path, "a", encoding="utf-8-sig") as f:
f.write(f"{now:%Y-%m-%d %H:%M:%S}: {msg}\n")
print(msg)
# ========== 啟動資訊與使用者提示 ==========
now = pd.Timestamp.now()
print(f"""\
=========================================================
【台股日K資料下載器】
📁 資料存放:{DATA_DIR}
📝 Log 路徑:{LOG_DIR}
🕒 啟動時間:{now:%Y-%m-%d %H:%M:%S}
🔧 多執行緒數:{MAX_WORKERS}
🔄 續傳功能:✅ 啟用(透過 {os.path.basename(CKPT_FILE)})
❗ 若要「強制重新下載全部」,請刪除 checkpoint 檔案:
→ 刪除:{CKPT_FILE}
或執行:
!rm "{CKPT_FILE}" # Colab / Linux
del /f "{CKPT_FILE.replace('/', '\\')}" # Windows
=========================================================
【免責聲明】
1) 僅供研究與教學,不構成投資建議。
2) 資料來自 Yahoo Finance,可能有延遲或錯誤。
3) 再執行一次即可續傳未完成項目。
=========================================================
""")
# --- 檢查 stockname 是否存在 ---
try:
_ = stockname # 例如 ['2330.TW&台積電', '2317.TW&鴻海']
except NameError:
raise RuntimeError("❌ 找不到變數 `stockname`。請先定義股票清單再執行本段。")
tickers = [t.strip() for t in stockname if t.strip()]
log(f"📋 原始清單數量:{len(tickers)} 支股票")
# ========== 安全檔名處理與解析 ==========
def safe_filename(s: str) -> str:
return (s.replace("/", "_").replace("\\", "_").replace(":", "_")
.replace("*", "_").replace("?", "_").replace('"', "_")
.replace("<", "_").replace(">", "_").replace("|", "_"))
def parse_item(item: str):
if '&' in item:
tkr, nm = item.split('&', 1)
else:
tkr, nm = item.strip(), "未知股票"
return tkr.strip(), nm.strip()
# ========== 智慧型續傳機制 ==========
def build_checkpoint(items):
"""根據當前 DATA_DIR 實際狀態重建 checkpoint"""
rows = []
for it in items:
tkr, nm = parse_item(it)
out_path = os.path.join(DATA_DIR, f"{tkr}_{safe_filename(nm)}.csv")
if os.path.exists(out_path) and os.path.getsize(out_path) > MIN_FILE_SIZE:
status, err = "skipped", ""
else:
status, err = "pending", ""
rows.append((tkr, nm, status, err))
df = pd.DataFrame(rows, columns=["ticker", "name", "status", "last_error"])
df.to_csv(CKPT_FILE, index=False, encoding='utf-8-sig')
return df
def validate_and_load_checkpoint():
"""
智慧載入 checkpoint:
- 檢查欄位是否完整
- 抽查 'skipped' 項目的實際檔案是否存在
- 若都不存在 → 可能是路徑變更 → 重建
"""
if not os.path.exists(CKPT_FILE):
log("🆕 建立新的 checkpoint ...")
return build_checkpoint(tickers)
try:
ckpt = pd.read_csv(CKPT_FILE)
required_cols = {"ticker", "name", "status", "last_error"}
if not required_cols.issubset(ckpt.columns):
log("⚠️ checkpoint 欄位不完整,重新建立")
return build_checkpoint(tickers)
# 抽查 skipped 項目是否「真的存在」
skipped_items = ckpt[ckpt["status"] == "skipped"]
if len(skipped_items) == 0:
log(f"🔁 讀取 checkpoint:{len(ckpt)} 項(無跳過項目)")
return ckpt
found_any = False
sample_check_count = 0
for _, row in skipped_items.iterrows():
if sample_check_count >= 5:
break
tkr, nm = row["ticker"], row["name"]
out_path = os.path.join(DATA_DIR, f"{tkr}_{safe_filename(nm)}.csv")
if os.path.exists(out_path) and os.path.getsize(out_path) > MIN_FILE_SIZE:
found_any = True
break
sample_check_count += 1
if found_any:
log(f"🔁 讀取既有 checkpoint:{CKPT_FILE}({len(ckpt)} 項),驗證到部分檔案確實存在")
return ckpt
else:
log("⚠️ 檢測到 checkpoint 中的檔案均不存在 → 可能資料夾路徑已變更 → 重建 checkpoint")
log(f" 上次路徑可能為:.../dayK ?目前路徑:{DATA_DIR}")
return build_checkpoint(tickers)
except Exception as e:
log(f"⚠️ checkpoint 讀取失敗,重建:{e}")
return build_checkpoint(tickers)
# ✅ 使用智慧型載入
ckpt = validate_and_load_checkpoint()
# 篩選本次要處理的項目
todo = ckpt[ckpt["status"].isin(["pending", "failed"])].copy()
log(f"🚀 本輪待處理:{len(todo)} 支")
# --- 多執行緒鎖 ---
print_lock = threading.Lock()
def download_stock_data(row):
"""下載單一股票歷史資料(日K)"""
ticker_id, name = row["ticker"], row["name"]
try:
with print_lock:
print(f"📥 下載中: {name}({ticker_id})...")
out_path = os.path.join(DATA_DIR, f"{ticker_id}_{safe_filename(name)}.csv")
# 若已存在且有資料,跳過
if os.path.exists(out_path) and os.path.getsize(out_path) > MIN_FILE_SIZE:
return {"ticker": ticker_id, "name": name, "status": "skipped", "err": "", "rows": 0}
# 下載資料
tk = yf.Ticker(ticker_id)
hist = tk.history(period="max", auto_adjust=AUTO_ADJUST)
if hist is None or hist.empty:
with print_lock:
print(f"⚠️ 無資料: {name}({ticker_id})")
return {"ticker": ticker_id, "name": name, "status": "failed", "err": "empty_history", "rows": 0}
# 重設索引並儲存
hist.reset_index(inplace=True)
hist.to_csv(out_path, index=False, encoding='utf-8-sig')
num_rows = len(hist)
with print_lock:
print(f"✅ 完成: {name}({ticker_id}) | {num_rows} 筆資料 | 已存 {os.path.basename(out_path)}")
time.sleep(0.1) # 減少被節流風險
return {"ticker": ticker_id, "name": name, "status": "success", "err": "", "rows": num_rows}
except Exception as e:
err_msg = str(e)
with print_lock:
print(f"❌ 失敗: {name}({ticker_id}) → {err_msg[:100]}...")
log(f"[ERROR] {ticker_id}&{name}: {err_msg}")
time.sleep(RETRY_DELAY)
return {"ticker": ticker_id, "name": name, "status": "failed", "err": err_msg, "rows": 0}
# ========== 主流程 ==========
if __name__ == "__main__":
start_time = time.time()
total_count = len(todo)
if total_count == 0:
print("🎉 所有股票資料均已下載完成,無需處理。")
log("ℹ️ 無需處理項目。")
else:
print("="*60)
print(f"開始下載 {total_count} 支股票的日K資料...")
print("="*60)
pbar = tqdm(total=total_count, desc="整體進度", unit="支")
results = []
with ThreadPoolExecutor(max_workers=MAX_WORKERS) as executor:
future_to_row = {executor.submit(download_stock_data, r): r for _, r in todo.iterrows()}
for future in as_completed(future_to_row):
r0 = future_to_row[future]
try:
result = future.result()
except Exception as e:
result = {
"ticker": r0["ticker"],
"name": r0["name"],
"status": "failed",
"err": f"uncaught: {e}",
"rows": 0
}
results.append(result)
# 即時更新 checkpoint
mask = (ckpt["ticker"] == result["ticker"])
ckpt.loc[mask, ["status", "last_error"]] = [result["status"], result["err"]]
ckpt.to_csv(CKPT_FILE, index=False, encoding='utf-8-sig')
pbar.set_postfix_str(f"最新: {result['name']} {result['status']}", refresh=False)
pbar.update(1)
pbar.close()
# ========== 統計與輸出 ==========
end_time = time.time()
duration = end_time - start_time
success = [r for r in results if r["status"] == "success"]
skipped = [r for r in results if r["status"] == "skipped"]
failed = [r for r in results if r["status"] == "failed"]
total_rows = sum(r["rows"] for r in success)
avg_rows = total_rows / len(success) if success else 0
# 📊 Console 輸出統計
print("\n" + "="*60)
print("📊 下載統計報告")
print("="*60)
print(f"📁 資料存放路徑: {DATA_DIR}")
print(f"📝 Log 路徑: {LOG_DIR}")
print(f"⏰ 執行時間: {duration:.2f} 秒")
print(f"📈 總計處理: {total_count} 支")
print(f"✅ 成功: {len(success)} 支 | 共 {total_rows:,} 筆日K資料 (平均 {avg_rows:.0f} 筆/支)")
print(f"🟡 跳過: {len(skipped)} 支 (已存在)")
print(f"❌ 失敗: {len(failed)} 支")
if failed:
print("📋 失敗清單:")
for r in failed:
print(f" - {r['name']}({r['ticker']})")
print("\n📌 提示:若仍有失敗項目,**直接重新執行本程式**即可續傳補抓。")
print("✅ 所有資料與日誌已儲存。")
print("="*60)
# Log 最終統計
log(f"📊 最終統計:成功={len(success)}, 跳過={len(skipped)}, 失敗={len(failed)}, 耗時={duration:.2f}s")
# ✅ 輸出失敗與成功清單
if failed:
fail_df = pd.DataFrame(failed)[["ticker", "name", "err"]]
fail_df.to_csv(f"{LOG_DIR}/failed_list.csv", index=False, encoding='utf-8-sig')
log(f"📁 已儲存失敗清單:{LOG_DIR}/failed_list.csv")
if success:
succ_df = pd.DataFrame(success)[["ticker", "name", "rows"]]
succ_df.to_csv(f"{LOG_DIR}/success_list.csv", index=False, encoding='utf-8-sig')
log(f"📁 已儲存成功清單:{LOG_DIR}/success_list.csv")
print(f"\n🧾 已輸出報告至:{LOG_DIR}/")
print(" - success_list.csv(成功清單)")
print(" - failed_list.csv(失敗清單)")
print(" - checkpoint_tw.csv(續傳點)")
print(" - download_tw_YYYYMMDD.txt(日誌)")
執行完後結果畫面會是如下

colab免費版執行時間是551秒,將近10分鐘

-----------------------------------------------------------------------------------------------------
將上方程式碼逐個貼上colab cell執行即可。預設會在goole driver建立資料夾存放日K檔案。

如果複製程式碼貼到colab上方會出現如下空白,導致執行後發生錯誤訊息
File "<tokenize>", line 205 IndentationError: unindent does not match any outer indentation level
請選擇該處空白選取候用取代方式全部取代,再次執行即可

-----------------------------------------------------------------------------------------------------
🧑🔬 作者身份與非專業聲明|AUTHOR'S STATUS AND INTENT 本報告的作者為獨立的、業餘數據研究愛好者,非專業量化分析師,亦不具備任何持牌金融顧問資格。本專題報告是作者利用全職工作外的個人時間完成。 The author of this report is an independent, amateur data researcher and NOT a professional quantitative analyst or a licensed financial advisor. This work is completed in the author's personal free time for statistical research purposes.
📊 數據來源與品質限制|DATA SOURCE LIMITATION 本報告所有歷史價格數據均來自免費公共資源(如 Yahoo Finance)。雖然作者已通過 V4.0 QA 系統盡力檢查並排除明顯錯誤,但由於數據源限制,作者不保證數據 100% 無誤。 All data is sourced from free public providers (e.g., Yahoo Finance). While the author uses the V4.0 QA System to minimize errors, the author offers NO WARRANTY of 100% accuracy. Data integrity is constrained by the free source.
🚫 無投資建議聲明|NO INVESTMENT ADVICE 本文內容、圖表及 AI 分析結果僅供研究參考與教學啟發之用,不構成任何投資買賣建議、諮詢或招攬。所有分析僅描述歷史統計規律。 This content is for statistical research and educational inspiration only. It does NOT constitute personalized financial advice, investment recommendations, or a solicitation to buy or sell securities.
⚠️ 風險與責任劃分|RISK & LIABILITY 股票市場投資涉及重大風險。您應自行判斷並承擔所有投資風險。作者(和平台)對您基於本報告所做出的任何投資決策和潛在損失,不承擔任何責任。 Stock market investing involves significant risk. The reader must exercise their own judgment. The author (and the platform) assumes NO LIABILITY for any financial losses incurred based on the information provided herein.




