
量化交易 Python 入門最常見的卡點是順序錯了:多數人還沒跑出任何一個回測結果,就先報名了數十小時的課程,然後在 pandas 語法和爬蟲除錯裡耗盡耐心。這篇教學走相反的順序:pip install finlab 之後,把一段不到 30 行的程式碼貼進去執行,今天就得到一個完整的台股回測,2020-2026 實測 Sharpe 1.48、最大回撤 -13.3%,理論留到跑通之後再補。
示範策略可下載、可重現:營收動能(月營收年增率)、價格動能(120 日報酬)、低波動(120 日報酬標準差,負向)三因子複合,每月選 30 檔、反波動加權、加上大盤站上 120 日均線才進場的濾網。月營收是台股每月公布的基本面資料,拿它當教學主角,也順便把「財報類資料如何對齊公布日」這個新手最常踩的坑示範一次。量化交易的觀念、策略類型與平台全景,整理在 量化交易完整指南;本文只做一件事:讓你的第一個回測動起來。
關鍵數字
下表是這個策略的真實回測結果。招牌期間取 2020-01 至 2026-06,同時附上全期 2007-2026 數字(兩者差異在下方「回測方法」段落會說清楚)。
| 指標 | 本策略 2020-2026 | 本策略 全期 2007-2026 | 含息 0050(同期 2020-2026) |
|---|---|---|---|
| 年化報酬 CAGR | 20.9% | 14.5% | 29.1% |
| 夏普比率 Sharpe(日頻年化) | 1.48 | 0.99 | 1.21 |
| Sortino | 2.25 | 1.49 | 2.05 |
| Calmar | 1.57 | 0.74 | 0.86 |
| 最大回撤 | -13.3% | -19.7% | -34.0% |
| 年化波動 | 12.5% | 12.9% | 22.4% |
| alpha / beta(對 0050 日報酬) | 0.118 / 0.288 | — | — |
| 月勝率 | 59.7% | 54.0% | 62.3% |
兩件事要先講白:第一,raw 報酬上含息 0050 同期 CAGR 29.1%,高過本策略的 20.9%,那是台積電權值股超級多頭的結果,而且 0050 連月勝率都略高(62.3% 對 59.7%)。第二,本策略的價值在風險調整後:Sharpe 1.48 高於含息 0050 同期的 1.21(兩者用同一個 sim() 引擎、同一窗口實跑,0050 以還原權息價格、fee_ratio=0 計算),最大回撤 -13.3% 對上 0050 的 -34.0%。所以本文一律用「以較低回撤、較高 Sharpe 的風險調整後勝出」描述,不會宣稱「報酬贏過大盤」。關於這條口徑,你可以對照我們在 多因子選股能否打敗 0050 裡的同一基準討論。
策略版的完整互動式回測報告直接嵌在下面,逐年、逐月、持股明細都可以點開檢視:
對照組(含息 0050 買進持有,同窗、零成本)的互動報告也公開:含息 0050 買進持有(2020-2026)回測報告。
量化交易 Python 四條入門路線:自學、上課、K 線平台,還是 pip install finlab
量化交易的第一道門檻不是策略,是資料。台股的調整收盤價、財報公布日對齊、成交金額、全額交割註記、產業分類,這些散落在公開觀測站、交易所、櫃買的資料,自己用 requests 爬、用 pandas 清,光是把前視偏差處理乾淨就能耗掉好幾週。線上課程與系列教學文則是把這段苦工拍成影片教你做一遍,苦工本身並沒有消失。
四條常見路線攤開比較:
| 路線 | 台股資料 | 回測引擎 | 投入成本 | 距離第一個回測 |
|---|---|---|---|---|
| 純自學(requests + pandas 自己爬) | 自己爬、自己清、自己對齊財報公布日 | 自己寫,容易埋前視偏差 | 金錢免費,時間以週計 | 數週起跳,且難驗證對錯 |
| 線上課程 / 系列教學文 | 多半教你接 API 或爬蟲,資料清理與公布日對齊仍要自己做 | 跟著課程手刻,或再學一套回測框架 | 課程費用 + 數十小時跟課 | 跟完課之後才開始 |
| MultiCharts / TradingView | 偏單一商品價量,台股全市場財報資料不齊 | 內建,但難做橫斷面選股 | 軟體或平台費用 | 做不出多因子排名選股 |
pip install finlab |
台股價量、財報、月營收、籌碼一套到位 | sim() 已內扣手續費與證交稅 |
套件免費,資料有免費試用 | 十分鐘 |
課程不是沒有價值:想系統性打好 pandas 與資料工程底子的人,跟一門課是合理投資。問題在順序。對多數新手,「先跑出一個真實結果,再回頭補理論」遠比「先學完才動手」有效,因為你會帶著具體問題去學:為什麼月營收要對齊公布期限?為什麼權重要正規化?這些問題在你跑通第一個回測之前,課程講了你也記不住。
MultiCharts 與 TradingView 擅長的是單一商品的進出場訊號,但量化選股的核心是「每月在全市場做橫斷面排名,挑出前段班」,這類運算在 K 線型平台上很難自然表達。finlab 的差異在於它是一個 pip 套件:資料、橫斷面排名、月頻換股、成本內扣都在同一個 DataFrame 流程裡。想更完整地比較工具,可參考 FinLab vs QuantPass 與 FinLab vs TEJ 兩篇評估;若你的最終目標是讓訊號自動下單而非手動換股,整條路的全景在 程式交易是什麼。
如果你還不確定「回測」到底在做什麼,讀 什麼是回測 補概念;「量化分析」這個詞背後的方法論,則見 什麼是量化分析。
環境安裝:pip install finlab 與登入
整個量化交易程式環境只需要兩步。先安裝套件:
顯示程式碼
pip install finlab接著登入。第一次使用時,finlab 會自動引導你完成登入(照套件指示在瀏覽器授權即可),不需要手動貼上任何 token:
顯示程式碼
import finlab
finlab.login() # 第一次執行會自動開啟登入流程,照 finlab 套件的指示完成即可登入成功後,所有台股資料都用 data.get('資料表名稱') 取得,回傳的是一個增強版的 pandas DataFrame。取單一張價量表的最小範例如下(這也是 用 Python 取得台股資料 的起點):
顯示程式碼
from finlab import data
close = data.get('price:收盤價') # 全市場每日收盤價,index 是日期、欄位是股票代號
close.tail()到這裡你的量化交易 python 環境就完成了。接下來把三個因子一個一個拼起來。
三因子複合策略逐步講解
這個策略的設計目標放在風險調整後表現,用三個方向互補的因子換取更平滑的權益曲線與更淺的回撤,而非單純衝高 CAGR。三個因子各自做全市場橫斷面百分位排名,再取平均當綜合分數。順帶說明:我們在 台股選股回測 用的是品質(ROE)為核心的另一組複合因子,回答「哪類選股因子長期有效」;本篇的因子組合與回測窗口刻意與它區隔,主軸是「最快跑通第一個回測」,兩篇可互相對照但不重複。
因子一:營收動能(月營收年增率)
台股是少數要求上市櫃公司每月申報營收的市場(次月 10 日前公布),這讓月營收成為更新頻率最高的基本面資料。基本面好消息公布後,股價平均不會一次反應完,而是延遲漂移一段時間,Bernard & Thomas (1989) 對盈餘公布後漂移(PEAD)的實證是這類「基本面動能」的源頭。關鍵是必須用 index_str_to_date() 把月營收對齊到實際公布期限,否則會用到當下還拿不到的資料,產生前視偏差:
顯示程式碼
rev_yoy = data.get('monthly_revenue:去年同月增減(%)').index_str_to_date().reindex(close.index, method='ffill')因子二:動能(120 日報酬)
動能因子捕捉「近半年漲得相對強的股票傾向繼續強」這個橫斷面現象,實證源頭是 Jegadeesh & Titman (1993):過去 3 到 12 個月相對強勢的股票,在接下來數月平均仍跑贏弱勢股。本文用 120 個交易日(約半年)的價格比值,落在這個學術上驗證過的形成期區間內:
顯示程式碼
momentum = close / close.shift(120)因子三:低波動(120 日報酬標準差,負向)
低波動因子是這個策略壓低回撤的關鍵。Ang, Hodrick, Xing & Zhang (2006) 記錄了所謂「低波動異常」:特異波動高的股票,後續風險調整後報酬反而偏低。本文用日報酬的 120 日滾動標準差,並加上負號,讓「波動低」的股票排名靠前:
顯示程式碼
low_vol = -close.pct_change().rolling(120).std()三因子合成、選股與加權
三個因子各自做橫斷面百分位排名(rank(pct=True))後平均,得到綜合分數;每月選分數最高的 30 檔;權重用 60 日波動的倒數(反波動加權,波動低者權重高);最後乘上大盤濾網,加權報酬指數站上 120 日均線才進場,否則整體空手抱現金。
顯示程式碼
from finlab import data
from finlab.backtest import sim
# ── 1. 載入價量資料(登入已在上一步用 finlab.login() 自動完成)──
close = data.get('price:收盤價')
volume = data.get('price:成交股數')
# ── 2. 定義可交易股票池:流動性、低價股、全額交割、特殊類別過濾 ──
amount = (close * volume).average(60) # 60 日均成交金額
flagged = data.get('etl:is_flagged_stock').reindex(close.index, method='ffill').fillna(False)
categories = data.get('security_categories').set_index('stock_id')['category']
excluded = ['金融保險', 'ETF', 'ETN', '存託憑證', '受益證券', '創新板']
bad_ids = set(categories[categories.isin(excluded)].index)
keep = [c for c in close.columns if c not in bad_ids]
universe = (amount > 50_000_000) & (close > 10) & (~flagged)
# ── 3. 計算三個因子 ──
# 營收動能:月營收年增率,用 index_str_to_date() 對齊實際公布日,避免前視偏差
rev_yoy = (data.get('monthly_revenue:去年同月增減(%)')
.index_str_to_date()
.reindex(close.index, method='ffill'))
momentum = close / close.shift(120) # 價格動能:120 日報酬
low_vol = -close.pct_change().rolling(120).std() # 低波動:120 日報酬標準差(負向)
# ── 4. 三因子各自橫斷面百分位排名後平均,得到綜合分數 ──
cols = sorted(set(keep) & set(rev_yoy.columns))
def cross_rank(df):
return df[cols].rank(axis=1, pct=True) # 橫斷面百分位排名
score = (cross_rank(rev_yoy) + cross_rank(momentum) + cross_rank(low_vol)) / 3
score = score.where(universe[cols])
# ── 5. 每月選分數最高的 30 檔,反波動加權(波動低者權重高)──
selected = score.rank(axis=1, ascending=False) <= 30
inv_vol = (1 / close.pct_change().rolling(60).std())[cols]
weight = selected * inv_vol
weight = weight.div(weight.sum(axis=1), axis=0).fillna(0)
# ── 6. 大盤濾網:加權報酬指數站上 120 日均線才進場,否則空手 ──
twii = data.get('benchmark_return:發行量加權股價報酬指數').iloc[:, 0]
market_up = (twii > twii.rolling(120).mean()).reindex(close.index, method='ffill').fillna(False)
weight = weight.mul(market_up.astype(int), axis=0)
# ── 7. 回測:月頻換股,手續費打三折(sim() 預設已內扣證交稅)──
report = sim(weight, resample='M', fee_ratio=1.425/1000/3, name='營收動能低波複合')
report.display()完整可執行腳本可直接下載:strategy.py。第一次執行時 finlab 會引導你登入,完成後跑完就會看到下面這張權益曲線。
回測結果
下圖是策略累積淨值對照含息 0050 的曲線。可以看到 2020-2026 期間策略走勢相對平滑,在 2022 年台股回檔時(大盤濾網把部位降到現金)避開了大部分跌幅:該年策略 -2.9%,含息 0050 是 -21.4%。

逐年報酬對照如下。策略並非每一年都領先,2024、2025 這種權值股大多頭年明顯落後 0050;它的強項在下跌年虧得少,這正是低波動因子加大盤濾網的設計目的。

招牌期間的完整指標:Sharpe 1.48、Sortino 2.25、Calmar 1.57、CAGR 20.9%、年化波動 12.5%、alpha 0.118、beta 0.288(對含息 0050 日報酬迴歸)、月勝率 59.7%。beta 只有 0.288,代表策略對大盤的敏感度低,這也是它在風險調整後能勝出的結構性原因。
風險與回撤
回撤(drawdown)比 CAGR 更能反映你實際抱得住抱不住。下圖是策略的回撤曲線,最大回撤 -13.3%,出現在波動最劇烈的區間。

-13.3% 的最大回撤,搭配 20.9% 的 CAGR,得到 Calmar 1.57(報酬除以最大回撤)。含息 0050 同期最大回撤 -34.0%,這是兩者風險輪廓最直觀的差距。需要提醒的是:過去回撤淺不保證未來不會更深,全期 2007-2026 的最大回撤就有 -19.7%,任何單一因子失效或市場結構改變,都可能讓回撤超出歷史區間。
回測方法
漂亮的數字若不交代怎麼算出來,就只是廣告。以下逐項說明這份回測的設定,有做的寫出數值,沒做的直接標明「未做」:
- 交易成本: finlab 的
sim()預設已內扣手續費與證交稅。程式裡fee_ratio=1.425/1000/3代表手續費以 0.1425% 打三折計算(約 0.0475%);賣出證交稅 0.3% 由sim()自動扣除。 - 滑價: 本文未額外假設滑價。月頻換股、單檔權重分散,衝擊相對可控,但實際衝擊取決於資金規模與當下成交量,本文未估算到滑價層級。
- 股票池: 全上市櫃個股,經下列流動性與類別過濾後的集合。
- 流動性過濾: 60 日均成交金額 > 5,000 萬元、股價 > 10 元,確保回測買得到的股票實單也買得到。
- 排除類別: 排除全額交割股(
etl:is_flagged_stock)、金融保險、ETF、ETN、存託憑證、受益證券、創新板。排除全額交割與特殊類別是為了降低生存者偏誤與不可交易的標的。 - 前視偏差: 月營收用
index_str_to_date()對齊實際公布期限(次月 10 日),再ffill到每日,確保每一天只用到當天真正已公布的營收;動能與低波動只用歷史價格,天然無前視。 - 基準口徑: 含息 0050 用同一個
sim()引擎、同一窗口實跑(權重恆為 1、fee_ratio=0),報酬以還原權息價格計算,不會因漏掉配息而低估基準。 - 權重: 反波動加權(60 日波動倒數),非等權;權重在 30 檔之間正規化加總為 1。本文未設單檔上限,反波動加權本身會壓低個股集中度。
- 周轉率: 月頻換股(
resample='M')。本文未逐期估算 turnover 數值,標明未估。 - 樣本內外: 本文為全段 in-sample(frontmatter 已標注
in_sample_only),未做嚴格的樣本外保留期測試;改以下一節的參數網格作為穩健性佐證,並會在季度刷新時補上發佈日之後的前瞻區間績效。 - 全期 vs 招牌期間對照: 招牌數字取 2020-2026(Sharpe 1.48、CAGR 20.9%);全期 2007-2026(資料可回溯的最早起點,含 2008 金融海嘯)為 Sharpe 0.99、Sortino 1.49、CAGR 14.5%、最大回撤 -19.7%。兩者差異主要來自 2020 後的台股多頭環境,讀者應以全期數字作為較保守的預期基準。
關於「回測看起來很好、實單卻失靈」的系統性風險,量化投資的挑戰與過擬合 有更完整的討論,建議搭配閱讀。
參數穩健性
單一組參數跑出 Sharpe 1.48 不能說明問題。Bailey, Borwein, López de Prado & Zhu (2014) 證明了一件對所有回測者都不舒服的事:嘗試的參數組合愈多,光憑運氣挑出漂亮 Sharpe 的機率就愈高,這就是回測過擬合。所以真正該問的是:換個持股數、換個大盤濾網長度,結論會不會崩掉?下圖是 2020-2026 的參數網格結果。

持股 15 / 20 / 25 / 30 檔,搭配大盤濾網 100MA 或 120MA,Sharpe 落在 1.31 到 1.51 之間,代表「策略風險調整後優於 0050(1.21)」這個結論對「選幾檔、用多長均線」並不敏感。明顯下滑的是把濾網拉長到 150MA,Sharpe 掉到 1.031.22,因為較長均線反應太慢、退場太晚。另外要照 Bailey 等人的警告說明:本文採用的 30 檔 × 120MA(1.48)接近網格中最好的一格,存在選擇效應,把鄰近組合的 1.311.42 當成合理預期區間,比盯著 1.48 務實。
我們在台股選股回測的單因子分測看到:低波動因子報酬墊底,但最大回撤只有 -13.6%,是所有因子裡最小的;營收動能與價格動能則提供報酬來源。三者合成的目的就是讓任一因子失效時,其他兩者仍能撐住整體。
風險調整後的對照
這一節必須講清楚,以免被當成又一篇浮誇的策略文。
含息 0050 在 2020-2026 累積約 5.2 倍、CAGR 29.1%,raw 報酬高過本策略的 20.9%(累積約 3.3 倍)。原因很單純:這段期間台積電等權值股走超級多頭,被動持有 0050 等於重壓贏家。所以就純報酬而言,本策略並沒有贏過含息 0050。
本策略的價值在另一個維度:以較低回撤、較高 Sharpe 的風險調整後勝出。同期 Sharpe 1.48 高於含息 0050 的 1.21,年化波動 12.5% 只有 0050(22.4%)的一半多一點,最大回撤 -13.3% 對上 -34.0%。換句話說,它用較低的長期報酬,換到更平穩的曲線與更小的最大虧損。對於抱不住大跌、會在低點停損的投資人,這個風險輪廓的實際意義可能比 raw CAGR 更重要。這個「每承擔一單位風險換到多少報酬」的衡量框架由 Sharpe (1994) 定式化,計算細節與判讀見 夏普比率。
新手下一步
如果這是你的第一個量化交易程式,建議照這個順序往下走:
- 先把上面的 strategy.py 跑通,確認自己的環境與 token 沒問題。
- 一次只改一個因子或一個參數,觀察 Sharpe 與回撤怎麼變,建立對因子的直覺。
- 讀 FinLab 量化平台新手指南 了解更多資料表與
sim()參數。 - 把選股邏輯換成你自己的想法,可參考 台股選股 5 步驟 與 如何在台股選股。
- 想理解因子有沒有效,讀 資訊係數 IC 是什麼;想了解 AI 在量化研究的角色,讀 AI 量化研究。
- 進階階段務必留意風險面:量化交易的缺點與風險、量化交易的職涯與薪資,以及若你考慮券商生態,可看 富途牛牛量化交易在台灣。
想直接動手?finlab 的資料與回測引擎有免費試用,免費註冊開始,把本文的 strategy.py 跑通、再換上你自己的因子,比看十篇文章更有感覺。
需要策略類型全景、優缺點與平台比較時,回到 量化交易完整指南;所有名詞若有不熟,都可以到 詞彙表 查核心定義後再回來。
FAQ
Q1:量化交易 Python 需要先上課或先學完 Python 嗎? 不需要。本文的策略整段不到 30 行,你只要能把程式碼貼到環境裡、把 token 換成自己的、按下執行,第一個回測今天就會跑出來。「跑得起來」之後再「看得懂」,接著一次改一個參數,是比先跟完數十小時課程更快的學習路徑;課程留給之後想系統性補 pandas 與資料工程底子時再說。
Q2:量化交易程式需要多少錢才能開始?
軟體層面,pip install finlab 本身免費,Python 也免費。資料下載需要 finlab 帳號,有免費試用可先跑回測驗證流程。真正要花錢的是實際投入市場的本金,以及換股產生的手續費與證交稅(本文回測已把這兩項內扣)。
Q3:量化交易 python 和一般技術分析程式有什麼不同?
技術分析程式多半針對單一商品判斷進出場訊號(例如均線交叉)。量化選股則是每月在全市場做橫斷面排名,挑出相對最好的一批股票。本文的三因子複合屬於後者,核心運算是 rank(axis=1) 的橫斷面排名,而非單檔的 K 線訊號。
Q4:回測 Sharpe 1.48 準嗎?實單能複製嗎? 回測是「在歷史資料上的模擬」,準的前提是方法乾淨:本文已處理前視偏差(月營收對齊公布期限)、流動性過濾、排除不可交易類別、內扣成本。即便如此,實單仍會遇到滑價、資金容量、策略隨時間衰退等回測難以完全反映的因素。把全期 2007-2026 的 Sharpe 0.99 當較保守的預期會更務實。
Q5:這個策略的容量(資金上限)大概多少? 假設每檔股票單日成交量參與率不超過 60 日均成交金額的 10%,對 2020-2026 共 61 次月再平衡逐次計算「各持股的 10% 均成交金額除以權重」的最小值,中位數約 NT0.8 億。也就是資金規模到億元等級,部分月份的換股就會開始頂到流動性,需要放寬持股檔數或調整流動性門檻;一般個人資金距離這個上限還很遠。
Q6:為什麼不直接買含息 0050 就好? 就 2020-2026 的純報酬而言,含息 0050 的 CAGR 29.1% 確實高過本策略的 20.9%。本策略的訴求是風險調整後:Sharpe 1.48 高於 0050 的 1.21、最大回撤 -13.3% 對上 -34.0%。適不適合你,取決於你更在意 raw 報酬,還是更在意曲線平穩與回撤控制。
Q7:多久要重新換股?換太頻繁會被成本吃掉嗎?
本策略月頻換股(resample='M')。月頻在台股是成本與訊號新鮮度之間相對平衡的選擇,回測已內扣手續費與證交稅。若改成週頻,turnover 與成本都會上升,需要重新驗證淨報酬是否還划算。
Q8:可以把這套策略套用到我自己挑的因子嗎?
可以,而且這正是建議的進階方向。把 rev_yoy、momentum、low_vol 任一個換成你自己的因子(例如 ROE、籌碼指標),其餘合成、選股、加權、濾網的框架不變。換因子後務必重跑回測與參數網格,確認新組合在風險調整後仍站得住。
投資有風險,過去績效不代表未來表現,本文僅供教學參考,不構成投資建議。回測數字會隨資料更新而變動。
最後更新:2026-06|回測區間:2020-01~2026-06(另列全期 2007-2026)|作者:FinLab 量化研究團隊(經量化研究員審閱)
參考文獻
- Bernard, V. & Thomas, J. (1989). Post-Earnings-Announcement Drift: Delayed Price Response or Risk Premium? Journal of Accounting Research. 基本面訊息公布後的延遲反應(PEAD):好消息公布後股價平均仍朝同方向漂移數月,支持本文以月營收年增率(台股月頻公布的基本面資料)作為營收動能因子。
- Jegadeesh, N. & Titman, S. (1993). Returns to Buying Winners and Selling Losers. Journal of Finance. 動能效應的開創性實證:過去 3 到 12 個月相對強勢的股票,在接下來數月傾向繼續跑贏,支持本文以 120 日報酬作為動能因子。
- Ang, A., Hodrick, R., Xing, Y. & Zhang, X. (2006). The Cross-Section of Volatility and Expected Returns. Journal of Finance. 低波動異常:波動較高的股票長期風險調整後報酬反而較差,對應本文低波動因子(120 日報酬標準差負向)與反波動加權的設計動機。
- Sharpe, W. F. (1994). The Sharpe Ratio. Journal of Portfolio Management. 夏普比率:用「每承擔一單位波動換到多少超額報酬」衡量策略,正是本文以 Sharpe 1.48 而非 raw CAGR 主張勝出的依據。
- Bailey, D., Borwein, J., López de Prado, M. & Zhu, Q. (2014). Pseudo-Mathematics and Financial Charlatanism. Notices of the AMS. 回測過擬合:在多組設定中挑出最漂亮的一組會高估真實績效,提醒讀者重視本文同時揭露全期 2007-2026(Sharpe 0.99)與參數網格穩健性,而非只看招牌期間的 1.48。
FinLab AI
想建立自己的策略?
用自然語言描述你的選股想法,AI 自動驗證、回測、給你答案
免費開始