Files
stocks-query/scripts/twse-margin-trading.py
Timmy 7d72aed984 restore and rebuild all stock query scripts
Recover 7 existing scripts from remote repo and rebuild 5 missing ones:
- twse-margin-trading.py (融資融券)
- twse-day-trading.py (當沖交易)
- twse-foreign-holdings.py (外資持股)
- yahoo-dividend-browserless.py (股利資訊)
- yahoo-broker-trading-browserless.py (主力券商進出)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 10:07:31 +08:00

121 lines
4.0 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""
查詢 TWSE 個股融資融券餘額(信用交易統計)
用法:
python3 skills/stocks-query/scripts/twse-margin-trading.py 2330
python3 skills/stocks-query/scripts/twse-margin-trading.py 3481
資料來源:證交所 TWSE 公開 API
"""
from __future__ import annotations
import json
import sys
import urllib.request
from datetime import datetime, timedelta
def fetch_margin(stock_no: str, date_str: str | None = None) -> dict:
"""從 TWSE 抓融資融券資料,自動往前回溯最多 10 天找到有資料的日期。"""
if date_str:
dates_to_try = [date_str]
else:
today = datetime.now()
dates_to_try = [(today - timedelta(days=i)).strftime("%Y%m%d") for i in range(10)]
for d in dates_to_try:
url = (
f"https://www.twse.com.tw/rwd/zh/marginTrading/MI_MARGN"
f"?date={d}&selectType=ALL&response=json"
)
req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"})
try:
with urllib.request.urlopen(req, timeout=30) as resp:
data = json.loads(resp.read().decode("utf-8", errors="replace"))
except Exception:
continue
if data.get("stat") != "OK":
continue
tables = data.get("tables", [])
if not tables:
continue
# 找到個股那張表title 含「融資融券」或欄位含「股票代號」)
target_table = None
for tbl in tables:
fields = tbl.get("fields", [])
if "股票代號" in fields:
target_table = tbl
break
if not target_table:
continue
fields = target_table["fields"]
for row in target_table.get("data", []):
code = row[fields.index("股票代號")].strip()
if code == stock_no:
result = {"date": d}
for i, field in enumerate(fields):
result[field.strip()] = row[i].strip().replace(",", "")
return result
raise RuntimeError(f"找不到 {stock_no} 的融資融券資料(已回溯 10 天)")
def print_summary(stock_no: str, d: dict) -> None:
date_fmt = f"{d['date'][:4]}/{d['date'][4:6]}/{d['date'][6:]}"
name = d.get("股票名稱", stock_no)
print(f"{name}{stock_no})|融資融券摘要")
print(f"• 日期:{date_fmt}")
# 融資
margin_buy = d.get("融資買進", "-")
margin_sell = d.get("融資賣出", "-")
margin_cash = d.get("融資現償", "-")
margin_balance = d.get("融資餘額", d.get("融資今日餘額", "-"))
margin_limit = d.get("融資限額", "-")
print(f"• 融資買進:{margin_buy}")
print(f"• 融資賣出:{margin_sell}")
print(f"• 融資現償:{margin_cash}")
print(f"• 融資餘額:{margin_balance}")
if margin_limit != "-":
print(f"• 融資限額:{margin_limit}")
# 融券
short_sell = d.get("融券賣出", "-")
short_buy = d.get("融券買進", "-")
short_cash = d.get("融券現償", "-")
short_balance = d.get("融券餘額", d.get("融券今日餘額", "-"))
short_limit = d.get("融券限額", "-")
print(f"• 融券賣出:{short_sell}")
print(f"• 融券買進:{short_buy}")
print(f"• 融券現償:{short_cash}")
print(f"• 融券餘額:{short_balance}")
if short_limit != "-":
print(f"• 融券限額:{short_limit}")
# 券資比
try:
mb = float(margin_balance) if margin_balance != "-" else 0
sb = float(short_balance) if short_balance != "-" else 0
if mb > 0:
ratio = (sb / mb) * 100
print(f"• 券資比:{ratio:.2f}%")
except (ValueError, ZeroDivisionError):
pass
if __name__ == "__main__":
stock_no = sys.argv[1] if len(sys.argv) > 1 else "2330"
try:
data = fetch_margin(stock_no)
print_summary(stock_no, data)
except Exception as e:
print(f"取得失敗:{e}", file=sys.stderr)
sys.exit(1)