跳至主要內容

低波動優化版(風險調整報酬+品質+動能)

回測視窗 2013-09-30 〜 2026-06-18 · 資料截至 2026-06-20

純低波動選股在台股長期輸給 0050,改用風險調整報酬因子並疊加 ROE、營收成長與複合動能後,季換前 15 檔。2013 年 9 月至 2026 年 6 月台股回測年化報酬 33.3%、日夏普 1.13、最大回撤 -38.2%;同視窗(2013 年 9 月起)0050 含息年化 20.9%、最大回撤 -34.0%。

投資警語:本文僅供教學參考,不構成投資建議。過去績效不代表未來表現,投資有風險。

回測績效

年化報酬率
33.3%
日夏普
1.13
最大回撤
-38.2%
總報酬率
3765%
平均持股
14.9 檔

回測含手續費 0.1425% 與賣出證交稅 0.3%(finlab 套件 sim() 預設成本),日夏普未扣無風險利率。策略淨值自資料可得日(2013-09-30)起算。

淨值曲線(對數座標)

低波動優化版(風險調整報酬+品質+動能) vs 0050 買進持有(含息)
1x2x3x5x8x12x20x30x2013-09-302015-11-102017-12-202020-02-132022-03-302024-05-212026-06-18
低波動優化版(風險調整報酬+品質+動能)0050 買進持有(含息)

與 0050 含息買進持有對照

對象年化報酬率日夏普最大回撤統計視窗
低波動優化版(風險調整報酬+品質+動能)33.3%1.13-38.2%2013-09-30 〜 2026-06-18
0050 買進持有(含息)20.9%1.14-34.0%2013-09-30 〜 2026-06-18

註:0050 含息若從更早的 2008-01-01 起算(含 2008 年金融海嘯),至 2026-06-20 為年化 14.8%、日夏普 0.74、最大回撤 -52.4%;上表對照採與策略淨值相同的統計視窗。

完整策略程式碼

安裝 finlab 套件後即可直接執行重現(finlab.login() 會自動引導登入,不需手動填 token)。想用 AI 輔助安裝與跑第一次回測,可以走 安裝教學

展開程式碼(strategy.py)
"""低波動選股策略 — 與 /blog/low-volatility-strategy 同口徑的完整可重現程式。

回測資料自 2008-01-01 取得;本頁策略(風險調整優化版)用到的 ROE 與營收資料較晚才齊備,淨值曲線實際自 2013-09-30 起算、統計至 2026-06-18。執行前安裝 finlab 並登入(finlab 會自動引導):

    pip install finlab
    python strategy.py

包含:低波動 vs 高波動異象實測,以及風險調整報酬優化版
(風險調整報酬 + ROE + 營收成長 + 複合動能、季換股、前 15)。
"""
import finlab
from finlab import data
from finlab.backtest import sim

finlab.login()  # finlab 會自動引導登入

adj = data.get("etl:adj_close")
volume = data.get("price:成交股數")
roe = data.get("fundamental_features:ROE稅後")
yoy = data.get("monthly_revenue:去年同月增減(%)")

ret = adj.pct_change()
vol60 = ret.rolling(60).std()            # 60 日波動率
liquid = volume.average(20) > 100 * 1000  # 平均日成交 > 100 張
universe = liquid & adj.notna()
vol_u = vol60[universe]

# 低波動異象:60 日波動率最低 30 檔 vs 最高 30 檔,月換股
pos_low = vol_u.rank(axis=1, ascending=True) <= 30
pos_high = vol_u.rank(axis=1, ascending=False) <= 30
sim(pos_low, resample="M", fee_ratio=0.001425, tax_ratio=0.003).display()
sim(pos_high, resample="M", fee_ratio=0.001425, tax_ratio=0.003).display()

# 風險調整報酬優化版:個股 6M 報酬/波動最高,加品質與複合動能,前 15、季換股
ret6m = adj.pct_change(120)
vol120 = ret.rolling(120).std()
mom_3m = adj.pct_change(60)
risk_adj = ret6m / vol120
pool = universe & (roe > 5) & (yoy > 0) & (mom_3m > 0) & (ret6m > 0)
position = risk_adj[pool].is_largest(15)
report = sim(position, resample="Q", fee_ratio=0.001425, tax_ratio=0.003)
report.display()
print(report.get_stats())
到 FinLab Studio 重現這支策略

在 Studio 裡用同一套真實資料調整條件、重跑回測。

本頁策略與回測結果僅供教學與研究用途,詳見服務條款