![]()
自己用收盤價算的報酬率,常常跟 FinLab 量化平台回測出來的數字對不上,差異多半卡在三件事:價格序列用錯(原始價與還原價)、成交時點誤會(訊號日當天與 T+1)、費稅沒有對齊(引擎預設已內扣)。這篇文章把回測程式 sim 的報酬率計算規則逐條拆開,最後從真實回測取出一筆交易手算還原:手算淨報酬與引擎輸出相差 0.00 個基點。
關鍵規則速查表
| 規則 | 內容 | 對應參數 |
|---|---|---|
| 成交時點 | 盤後訊號,下一個交易日(T+1)收盤價成交 | trade_at_price(預設 'close') |
| 手續費 | 0.1425%(台股牌價,未打折),買進、賣出各收一次 | fee_ratio(預設 0.001425) |
| 證交稅 | 0.3%,賣出時收;ETF 為 0.1% | tax_ratio(預設 0.003) |
| 價格序列 | 向後還原股價(除權息、增減資、分割),歷史值不變動 | 資料表 etl:adj_close |
| 報酬計算 | 複利連乘、含未實現損益,獲利全數再投入 | 權益曲線 report.creturn |
| 滑價 | 未假設(可自行調高 fee_ratio 做壓力測試) |
未內建 |
文中每個績效數字都出自同一支腳本(strategy.py,pip install finlab 後可重跑),資料區間 2018-01-02 至 2026-06-09。還在比較回測環境的讀者,台股回測工具比較整理了六種選項的成本處理差異;回測的定義與常見誤區,見回測是什麼。
還原股價:為什麼你抓的歷史價格跟 FinLab 不一樣
還原股價:把除權息、增減資、股票分割造成的價格跳空補回去的序列,讓跨越這些事件的報酬率可以相除計算。更多名詞定義見量化名詞解釋。
原始收盤價在除權息日會跳空下跌,但這個下跌不是虧損,股東拿到了股息。0050 在 2018-01-02 到 2026-06-09 之間有 17 個這類調整事件,兩種序列算出的「報酬」天差地遠:原始收盤價累積 +25.3%,向後還原價累積 +558.5%(來源:finlab price:收盤價 與 etl:adj_close,2026-06-09 快照)。原始價連方向都不可信,0050 在 2025-06-18 還有一次一股拆四股。兩個真實事件的對照:
| 事件 | 日期 | 前一交易日原始價 | 當日原始價 | 原始價漲跌 | 還原價漲跌 |
|---|---|---|---|---|---|
| 一股拆四股 | 2025-06-18 | 188.65 元 | 47.57 元 | -74.78% | +0.86% |
| 除息(隱含配息約 1.01 元) | 2026-01-22 | 71.85 元 | 71.80 元 | -0.07% | +1.34% |
拆股當天原始價看似崩盤 74.78%,除息當天市場其實上漲 1.34%、原始價卻顯示 -0.07%;用原始價回測,這些假跳空都會被當成真實損益。

向後還原的數學
FinLab 的還原價是「向後還原」:以掛牌日為基準往後推,遇到調整事件就把落差補回,歷史值一旦算出就永遠不變。遞迴定義是
其中 是當日原始收盤價, 是當日每股現金股利(拆股、增減資用對應的調整比例),沒有事件的日子 。式子只用到 與之前的資訊,昨天算出的還原價,明天重算仍是同一個數字。
向前還原為什麼不能拿來回測
另一派做法是「向前還原」(yfinance 的 Adj Close 屬於此類):錨定在最新收盤價,把整段歷史等比例縮放,
其中 是你下載資料的那一天。問題就在這個 :時點 的向前還原值,取決於 之後發生的所有配息與分割,歷史數值滲入了未來資訊,而且每次下載都會變。用真實的 0050 資料示範:同樣是 2018-01-02 這一天,在 2025-06-10 下載算出的向前還原價是 64.19 元,在 2026-06-09 下載變成 15.72 元,相差 4.08 倍(主因是中間那次拆股)。

影響有兩層:一是可重現性,今天跑的回測,下個月重跑連輸入價格都不一樣;二是凡把「價格水準」當訊號的策略(如股價低於 50 元),向前還原的歷史價位已被未來資訊改寫,訊號本身就洩漏了未來。用線性代數理解回測框架也據此強調價格矩陣要固定。
訊號日與進出場日:T+1 收盤成交
FinLab 的資料是日週期盤後資料,訊號盤後算出,最快下一個交易日才買得到,所以引擎規定:訊號日的下一個交易日,以收盤還原價成交。一筆真實交易的時間線(取自下文的示範策略):
| 事件 | 日期 | 說明 |
|---|---|---|
| 進場訊號日 | 2026-04-30(週四) | 盤後資料產生買進訊號 |
| 實際進場日 | 2026-05-04(週一) | 下一個交易日(5/1 連假),以收盤價成交 |
| 出場訊號日 | 2026-05-29(週五) | 月底再平衡,個股被換出 |
| 實際出場日 | 2026-06-01(週一) | 下一交易日,以收盤價成交 |
成交價可用 trade_at_price 改成開盤價:
顯示程式碼
from finlab import data
from finlab.backtest import sim
# 市值資料,首次執行會自動引導登入
mv = data.get('etl:market_value')
# 每月選市值前 5 大,等權持有(僅為示範計算規則,不是推薦策略)
position = mv.is_largest(5)
# 預設以 T+1 收盤還原價成交;改用 T+1 開盤價成交:
report = sim(position, resample='M', trade_at_price='open')開盤價與收盤價不能混用,所以「今日尾盤買進、明日開盤賣出」這種回測目前做不到;兩種價格序列對進出場的影響,客製化回測價格序列的比較有實測。當沖、隔日沖等依賴盤中資料的策略也不在支援範圍;訊號到自動下單的完整流程,見程式交易入門。
交易成本的數學:費稅怎麼進到報酬率
一筆完整的買進賣出,引擎的淨報酬計算是
其中 是手續費率(預設 0.001425,買進端收一次、賣出端收一次), 是證交稅率(預設 0.003,只在賣出端收)。舉個示例(示意性數字):假設股價不漲不跌,,淨報酬是 。名目上三筆費用相加是 0.585%,乘法精算約 0.584%,差異來自賣出端費用作用在已扣買進手續費的基數上。
一個常被誤解的細節:月頻再平衡不等於整個部位砍掉重買。我們檢視引擎原始碼後確認,費稅只對「實際買賣的差額」計收,連續留在持股名單內的股票只有權重微調的部分被收費,所以低換手策略的實際成本遠低於「每月來回 0.585%」的粗估。
成本侵蝕實算:同一套規則,三檔費率
兩個換手率不同的策略各跑三次,只改 fee_ratio,其餘設定相同(證交稅固定 0.3%,2018-01 至 2026-06 每月再平衡,數據出自文末可下載腳本的實跑結果):
| 年化報酬 CAGR | 手續費打 6 折(0.0855%) | 牌價(0.1425%) | 雙倍(0.285%) |
|---|---|---|---|
| 市值前 5 等權(年單邊換手約 85.5%) | 22.48% | 22.31% | 21.91% |
| 60 日動能前 5 等權(年單邊換手約 786.5%) | 22.37% | 21.24% | 18.45% |
低換手策略從打折到雙倍費率,年化只差 0.57 個百分點;高換手的動能策略同樣變化,年化掉了 3.92 個百分點。高換手策略不扣成本的回測,結論可能整個翻盤。

學術研究對這件事有大樣本證據:Novy-Marx & Velikov (2016) 檢驗 23 種知名異常報酬策略,發現扣掉交易成本後,高換手策略的超額報酬大多明顯縮水甚至消失;Frazzini, Israel & Moskowitz (2018) 則用近一兆美元的機構實際成交資料說明,真實交易成本得用真實成交紀錄校準,且隨下單規模上升。落到個人操作上:回測費率設成自己的券商方案,並把換手率當成本槓桿來管理。
權益曲線:複利連乘、含未實現損益
回測儀表板上的權益曲線,是把每天的組合報酬連乘起來:
是組合第 天的報酬,已含當天發生的費稅。兩個關鍵性質:
- 複利:獲利全數再投入,部位隨權益放大,年化報酬(CAGR)由此而來。
- 含未實現損益:持股每天用收盤還原價評價,漲跌即時反映在曲線上,不等賣出才認列,所以權益曲線和「已實現損益」的加總是兩回事。
示範策略(每月持有市值前 5 大、等權)2018-01 至 2026-06 的權益曲線換算成指標:CAGR 22.31%、日夏普 1.05、最大回撤 -32.59%(來源:finlab sim() 預設費稅實跑,統計取 2018-01-02 至 2026-06-09 的權益曲線計算)。再次強調,這個策略只是規則驗證的教具,集中持股 5 檔的回撤與個股風險都偏高;以績效為目標的策略設計,見多因子選股實測與量化交易 Python 教學。
完整互動式回測報告(月報酬熱力圖、交易明細)可在下面操作:
手算驗證:一筆真實交易,從還原價到淨報酬
規則講完,驗證它。從示範策略的回測報告取出最後一筆已平倉交易:3711 日月光投控,訊號日 2026-04-30 進場、2026-05-29 出場,單獨放進 sim() 跑一筆完整買賣,手算對帳:
顯示程式碼
import pandas as pd
from finlab import data
from finlab.backtest import sim
close = data.get('price:收盤價')
adj = data.get('etl:adj_close')
# 單獨持有 3711:訊號區間 2026-04-30 到 2026-05-28,5/29 訊號出場
single = pd.DataFrame(False, index=close.index, columns=['3711'])
single.loc['2026-04-30':'2026-05-28', '3711'] = True
report_single = sim(single)
engine_net = report_single.creturn.iloc[-1] - 1 # 引擎淨報酬
# 手算:T+1 收盤還原價的價差,乘上買進費率與賣出費稅
gross = adj['3711'].loc['2026-06-01'] / adj['3711'].loc['2026-05-04']
hand_net = gross * (1 - 0.001425) * (1 - 0.001425 - 0.003) - 1逐步攤開每個數字(皆為文末可下載腳本的輸出):
| 步驟 | 計算 | 累積報酬 |
|---|---|---|
| 進場價 | 2026-05-04 收盤還原價 748.7333(原始價 525.0 元) | — |
| 出場價 | 2026-06-01 收盤還原價 857.1213(原始價 601.0 元) | — |
| 價差報酬 | 857.1213 ÷ 748.7333 − 1 | +14.4762% |
| 扣買進手續費 | × (1 − 0.001425) | +14.3131% |
| 再扣賣出手續費 | 賣出端費用作用在同一基數 | +14.1502% |
| 再扣證交稅 0.3% | × (1 − 0.001425 − 0.003) 完成賣出端 | +13.8072% |
引擎權益曲線的終值是 1.138072,淨報酬 +13.8072%,與手算相差 0.00 個基點。這段期間 3711 沒有除權息,所以原始價比值(601 ÷ 525)與還原價比值同為 +14.4762%;若持有期間跨過除息日,就只有還原價算得出正確價差。

這次對帳同時驗證了 T+1 進出場日與收盤還原價成交兩條規則。
回測方法與限制
本文所有回測的口徑:
| 項目 | 本文設定 |
|---|---|
| 交易成本 | 手續費 0.1425%(敏感度測試另跑 0.0855% 與 0.285%)、賣出證交稅 0.3%,sim() 預設內扣 |
| 滑價 | 未假設 |
| 股票池 | 全上市櫃;示範策略依市值取前 5 大 |
| 流動性過濾 | 未做(示範策略選出的本來就是市值最大的權值股,非刻意過濾) |
| 排除類別 | 未排除金融股、KY 股;ETF 不在 etl:market_value 個股池內 |
| 前視偏差 | 市值與價格皆為當日盤後可得資料,訊號一律 T+1 成交 |
| 權重 | 等權,未設單檔上限(5 檔集中持股,個股風險高) |
| 周轉率 | 市值前 5 約 85.5%/年(單邊估算)、動能前 5 約 786.5%/年 |
| 樣本內外 | 全段 in-sample,未做樣本外測試;本文驗證計算規則而非策略效度,過擬合議題見量化投資的過擬合挑戰 |
常見問題 FAQ
回測報酬率跟實際下單為什麼不一樣?
三個主因:回測以收盤價整筆成交,實單會有滑價與部分成交;費率沒改成自己的券商折扣,成本就失準;資金大到影響市場價格時,回測假設失真。上線前用小額或模擬單比對成交價差。
回測有算滑價嗎?
沒有,sim() 未內建滑價假設。實務上可把 fee_ratio 調高當壓力測試,如本文的雙倍費率情境。
可以用開盤價回測嗎?
可以,sim(position, trade_at_price='open') 改為 T+1 開盤價成交。開盤價與收盤價不能在同一次回測中混用。
支援當沖或隔日沖回測嗎?
不支援。資料為日週期盤後資料,盤中進出的策略需要 tick 或分 K 資料。
ETF 的證交稅怎麼設?
台股 ETF 證交稅是 0.1%,與個股的 0.3% 不同。策略標的全是 ETF 時設 sim(position, tax_ratio=0.001),否則報酬會被多扣。
除權息的股息有算進報酬嗎?
有。還原價已把配息配股折算回價格序列,權益曲線等同「配息全數再投入」口徑,不需另外加回股息。
回測獲利有再投入嗎?
有,權益曲線以複利連乘計算,已實現與未實現損益都即時反映、全數滾入下一期部位。
手續費可以依券商折扣調整嗎?
可以,例如 6 折設 sim(position, fee_ratio=0.001425 * 0.6)。本文實測顯示低換手策略對折扣不敏感,高換手策略差距明顯。
下載與驗證
| 檔案 | 說明 |
|---|---|
| strategy.py | 本文全部數字的重現腳本:還原價對照、費率敏感度、單筆交易手算驗證 |
| report_strategy.html | 示範策略完整互動式回測報告 |
pip install finlab 免費註冊就能重跑這些數字;回測在量化流程中的整體位置,見量化交易完整指南。對計算規則還有疑問,歡迎到 Discord 社群討論,平台進階功能見方案說明。
延伸閱讀
投資警語:本文僅供教學參考,不構成投資建議。文中示範策略僅用於驗證回測計算規則,集中持股 5 檔、最大回撤達 -32.59%,並非推薦組合;所有數據為歷史回測模擬,過去績效不代表未來表現,投資有風險,請審慎評估自身風險承受能力。
最後更新:2026-06|回測區間:2018-01-02 ~ 2026-06-09|作者:FinLab 量化研究團隊(經量化研究員審閱)
FinLab AI
想建立自己的策略?
用自然語言描述你的選股想法,AI 自動驗證、回測、給你答案
免費開始


