我用 30 行 Python 寫了一個台股篩選器 — — 而且每個數字都能溯源
找「營收在成長、股價剛站上季線、又買得進」的股票,不該是一件要先跟資料奮鬥三小時的事。
我用 30 行 Python 寫了一個台股篩選器 — — 而且每個數字都能溯源
找「營收在成長、股價剛站上季線、又買得進」的股票,不該是一件要先跟資料奮鬥三小時的事。
先講一個我自己卡很久的問題。
我想做的事很單純:每個月花十分鐘,掃一遍我關注的股票,挑出「月營收還在成長、股價剛站上季線、成交量又夠大到我進得去出得來」的那幾檔,剩下的先不看。這不是什麼高深策略,是很多人用 Excel 手動在做的事。
真正麻煩的從來不是邏輯,是資料。免費來源東缺一塊西缺一塊;有的把缺漏用前一筆硬補上去,你根本不知道哪些是真的;最陰險的是,你抓月營收的時候很容易「偷看到未來」 — — 用了一個當時市場還沒公布的數字,回測跑出來漂亮得不像話,上線就打回原形。
後來我改用 TW Market Data(下面簡稱 TWMD)的 API 重寫,篩選器本體大概 30 行。這篇就把整個過程走一遍 — — 你不用先註冊也能跟著打第一筆,程式碼可以直接複製貼上跑。
如果你不寫程式,前三段看完你就知道這東西在幹嘛、以及為什麼它給的數字可以信;真的想自己跑再往下看程式。
我們要做什麼
一個小篩選器。輸入:一串你關注的股票代碼。輸出:通過下面三關的清單。
- 成長:最近一個月的營收年增率(YoY)大於 20%。
- 動能:目前收盤價站上 60 日均線(俗稱季線)。
- 流動性:近 20 個交易日的日均成交金額夠大,別挑到一天成交幾十萬、你一買就漲停的冷門股。
三個條件都很土,但合在一起意外地能過濾掉一大堆雜訊。重點是,做完這件事你需要兩種資料:日線價格和月營收。TWMD 這兩個資料集都在免費方案裡,所以整篇你不用付錢也能跑完。
三分鐘暖身:先打一筆,不用註冊
TWMD 有五檔股票可以完全不帶金鑰直接打 — — 台積電(2330)、鴻海(2317)、聯發科(2454)、0050、長榮(2603)。打開終端機貼這行:
curl "https://api.twmarketdata.com/v2/datasets/twse-daily-price?symbol=2330&limit=1"
你會拿到類似這樣的東西(我把它縮短了):
{
"dataset": "twse_daily_price",
"rows": [
{
"symbol": "2330",
"date": "2026-08-05",
"open": 80,
"high": 81,
"low": 79.1,
"close": 80.1,
"volume_shares": 549452,
"turnover_value": 43901948,
"price_method": "official",
"price_confidence": null
}
],
"count": 1,
"source_role": "official_twse",
"lineage": {
"provider": "TWSE",
"table": "normalized_twse_daily_prices"
}
}
注意三件事,這是我後來決定用它的原因:
- 資料放在
rows陣列裡,每一列就是一個交易日,欄位名很直白(close、turnover_value)。 - 每筆回應都帶
lineage——這個數字來自哪個官方來源、哪張表,寫得清清楚楚。你要的話可以一路追回交易所的原始發布。 price_confidence是null,不是隨便塞個數字。TWMD 的原則是資料有缺就如實留著,不拿推估值把洞補起來。 對做量化的人來說,這一條比多幾個欄位重要得多——你至少知道哪裡有洞。
不想開終端機的話,官網有個 Playground,在瀏覽器裡就能打,一樣免金鑰。
拿一把金鑰
要查這五檔以外的股票,就得註冊拿金鑰。到 儀表板 開一個免費帳號、建一把 API key(長得像 sk_live_...)。金鑰放在請求標頭 X-API-Key,不要放進網址。
我習慣塞進環境變數,程式碼裡就不會出現金鑰:
export TWMD_API_KEY="sk_live_你的金鑰"
一個小 helper
所有端點長得一樣:GET /v2/datasets/{資料集},帶 symbol 之類的參數,金鑰放標頭,資料回在 rows。先包一個函式,後面就不用重複:
import os
import requests
BASE = "https://api.twmarketdata.com/v2/datasets"
KEY = os.environ["TWMD_API_KEY"]
def get_rows(dataset: str, **params) -> list[dict]:
"""打一個 TWMD 資料端點,回傳 rows 陣列。"""
r = requests.get(
f"{BASE}/{dataset}",
params=params,
headers={"X-API-Key": KEY},
timeout=15,
)
r.raise_for_status()
return r.json().get("rows", [])
symbol 是必填的——這幾個資料集沒有「一次給我全市場」的用法,你要自己準備一串候選股、一檔一檔查。這其實是好事:你會很清楚自己在掃哪些標的,額度也花在刀口上。
條件一:動能(站上季線)
季線就是 60 個交易日的收盤均價。抓夠長的一段日線,算最後 60 天的平均,再看最新收盤價有沒有站上去:
def passes_momentum(symbol: str) -> bool:
rows = get_rows(
"twse-daily-price",
symbol=symbol,
start_date="2026-04-01", # 抓大約五個月,確保有 60 個交易日
end_date="2026-08-25",
limit=200,
)
closes = [r["close"] for r in rows if r.get("close") is not None]
if len(closes) < 60:
return False # 資料不夠就別猜,直接淘汰
ma60 = sum(closes[-60:]) / 60
latest_close = closes[-1]
return latest_close > ma60
closes[-1] 是不是真的「最新」,取決於 API 回來的排序。第一次跑務必自己 print(rows[0]["date"], rows[-1]["date"]) 確認一下順序,別預設。這是我踩過的雷,講一次省你半小時。
條件二:成長(月營收年增)
台股散戶最愛看的數字,大概就是月營收年增率了。TWMD 的 monthly-revenue 直接給你 revenue_yoy,而且是小數——0.18 就是 18%:
def passes_growth(symbol: str, threshold: float = 0.20) -> bool:
rows = get_rows("monthly-revenue", symbol=symbol, limit=1)
if not rows:
return False
yoy = rows[0].get("revenue_yoy")
return yoy is not None and yoy >= threshold
revenue_yoy 可能是 null(這檔那個月沒有年增資料),所以先擋掉 None 再比。不要看到 null 就自己補個 0——那會把「不知道」偷偷變成「沒成長」,是兩件不同的事。
條件三:流動性(買得進賣得出)
用近 20 個交易日的日均成交金額(turnover_value,單位是元)當門檻。這裡設一億,你可以照自己的胃口調:
def passes_liquidity(symbol: str, min_avg_turnover: float = 1e8) -> bool:
rows = get_rows(
"twse-daily-price",
symbol=symbol,
start_date="2026-07-20",
end_date="2026-08-25",
limit=40,
)
turnovers = [r["turnover_value"] for r in rows if r.get("turnover_value")]
if len(turnovers) < 20:
return False
avg = sum(turnovers[-20:]) / 20
return avg >= min_avg_turnover
串起來
import time
watchlist = ["2330", "2317", "2454", "2603", "3008",
"2412", "1301", "2882", "3034", "2379"]
passed = []
for symbol in watchlist:
try:
if passes_growth(symbol) and passes_momentum(symbol) and passes_liquidity(symbol):
passed.append(symbol)
except requests.HTTPError as e:
print(f"{symbol} 查詢失敗: {e}")
time.sleep(0.3) # 對 API 客氣一點,別狂打
print("通過篩選:", passed)
我把成長條件放第一個判斷,是因為它只花一次呼叫;and 短路後,不成長的股票根本不會去打那兩個比較貴的價格查詢,額度省下來。各方案的額度和速率限制在 定價頁,量大的話記得看一下。
就這樣。換掉 watchlist、調一下三個門檻,你就有一個自己的篩選器了。
一個一定要知道的雷:別偷看到未來
上面這個篩選器是拿來看「現在」的,沒問題。但只要你哪天想把它變成回測 — — 「如果我 2022 年就這樣選股,績效如何?」 — — 就會踩到量化裡最經典的陷阱:前視偏誤(lookahead bias)。
月營收不是月底就公布的,公司有法定申報期限,通常隔月才揭露。所以 2022 年 3 月的營收,市場可能到 4 月中才看得到。如果你回測時用「3 月」這個月份去對齊 3 月的股價,等於讓你的策略提前知道了還沒公布的數字 — — 回測會漂亮得離譜,實盤直接破功。
TWMD 在這件事上處理得很老實:每一列揭露類資料都帶一個 knowledge_date(知識日期),而且保守地設成法定申報截止日——寧可偏晚也不偏早。回測時你用 knowledge_date 過濾,而不是用營收月份,數字就會在它「真正進入市場的那天」才進入你的策略。官方在 資料集規格書 裡把這個取捨寫得很白,值得一讀。
順帶一提,日線價格資料集含已下市股票(從 2004 年起、約 525 萬列)。這聽起來不起眼,但如果你回測時只用「今天還活著」的股票,你的績效會被系統性高估 — — 那些下市、倒掉的公司被你悄悄地從歷史裡刪掉了,這叫倖存者偏差。想避開它,前提就是你的資料裡留著那些已經消失的名字。
這兩點 — — 知識日期、含下市股 — — 就是我願意在這上面花錢的原因。它們不會讓你的 demo 更炫,但會讓你的回測不騙自己。
想更進一步
- 換更長的候選清單:與其手打,可以用 指數成分股 把某個指數的成分股一次拉出來當 universe。
- 加更多條件:估值(本益比)、三大法人買超、財報三表 — — 資料集總覽 有全部清單,每個都同一套
rows結構,helper 不用改。 - 讓 AI 幫你查:TWMD 有 MCP server,接上 Claude 或 ChatGPT 之後,你可以直接用中文問「幫我看台積電近 12 個月營收年增」,它會去打同一個 API、把帶來源的數字拿回來。不想寫 Python 的話,這是最短的路。
最後
一個能用的台股篩選器,核心就是這 30 行。難的部分從來不是邏輯,是背後那份「不缺、不亂補、不偷看未來」的資料 — — 這也是我寫這篇的原因:把資料選對,後面的程式碼會簡單到不可思議。
先去打那筆免金鑰的 2330 看看回應長怎樣,你大概十分鐘就有感覺了。
免費開始:twmarketdata.com · 文件:快速開始
本文的資料與統計僅供研究與教學,非投資建議;投資決策與風險請自行判斷。文中程式碼與欄位以 TWMD 官方文件為準,API 若有更新請以官網為主。
메타데이터
- post_id
- e8fc84d169ee
- slug
- 我用-30-行-python-寫了一個台股篩選器-而且每個數字都能溯源-e8fc84d169ee
- url
- https://medium.com/@anthonyiaaan/%E6%88%91%E7%94%A8-30-%E8%A1%8C-python-%E5%AF%AB%E4%BA%86%E4%B8%80%E5%80%8B%E5%8F%B0%E8%82%A1%E7%AF%A9%E9%81%B8%E5%99%A8-%E8%80%8C%E4%B8%94%E6%AF%8F%E5%80%8B%E6%95%B8%E5%AD%97%E9%83%BD%E8%83%BD%E6%BA%AF%E6%BA%90-e8fc84d169ee
- canonical_url
- https://medium.com/@anthonyiaaan/%E6%88%91%E7%94%A8-30-%E8%A1%8C-python-%E5%AF%AB%E4%BA%86%E4%B8%80%E5%80%8B%E5%8F%B0%E8%82%A1%E7%AF%A9%E9%81%B8%E5%99%A8-%E8%80%8C%E4%B8%94%E6%AF%8F%E5%80%8B%E6%95%B8%E5%AD%97%E9%83%BD%E8%83%BD%E6%BA%AF%E6%BA%90-e8fc84d169ee
- author_url
- https://medium.com/@anthonyiaaan
- status
- ok
- fetched_at
- 2026-09-08 15:49:05