跳至主要內容
台股研究 約 22 分鐘閱讀

kd 指標怎麼用才對?台股 11 年回測:拆成選股因子+大盤風控的純技術策略

kd 指標怎麼用?台股回測:超賣抄底研究段年化僅 1.8%,黃金交叉也輸 0050;動能池內挑漲最多、K−D 上升最快的 10 檔,配 KD 廣度風控,全期年化 29.4% 勝 0050 的 21.3%,但 2022 年起落後,換股日挪一個月只剩 7.7%。

免安裝,用 AI 重跑這篇的策略

在瀏覽器開啟 FinLab Studio,AI 依本文的條件重跑回測,接著可以改條件再測。

在 Studio 重跑
kd 真正用法 CAGR 29.4% Sharpe 1.14

kd 指標在台股怎麼用:拆成選股因子加大盤風控

kd 指標台股回測的關鍵數字:

你想知道的 本文用真實台股回測的回答
kd 指標是什麼 隨機指標(Stochastic),由 K 值與 D 值組成,衡量收盤價在近 N 日高低區間的相對位置,0 到 100 有界擺盪
黃金交叉、超賣抄底有效嗎 每月買慢 K 最低的 30 檔,研究段(2015–2021)年化 1.8%;只買月底當天剛金叉的股票 7.4%;持有所有 K 在 D 之上的股票 10.5%。同期含息 0050 是 15.2%,2022 年起七種裸用法也全部落後 0050 的 30.5%
那 K−D 該怎麼用 拆成兩個零件:K−D 的 5 日變化當選股加分項(在 120 日動能前 80 名裡,挑漲最多、K−D 上升最快的 10 檔);可交易宇宙(流動性前 300 名)內 K−D > 0 的比例(KD 廣度)配 0050 年線,當季底的減碼開關
效果如何 資料延長到 2026-09-30,全期年化 29.4%、月夏普 1.14、最大回撤 -25.4%,三項都優於含息 0050(21.3%、1.07、-34.0%);但 2022 年起年化 22.0% 落後 0050 的 30.5%
最大的保留 這套規則在 3、6、9、12 月底換股。換股日挪到 1、4、7、10 月底,全期年化只剩 7.7%;挪到 2、5、8、11 月底是 11.7%;改成每月換股 10.6%。漂亮的數字有很大一部分來自「剛好在季底換股」
最近發生什麼事 6 月 30 日換股那天,KD 廣度剛好落在門檻下方,第三季只持有 35% 部位。持股 10 檔 7 月全數虧損,策略當月 -12.7%;同一籃不減碼是 -34.5%
依本文重跑

執行這個策略

複製提示詞,讓 AI 依照上方證據的資料、條件與風險檢查重跑一次。

告訴你的AI:

幫我設定 FinLab,重現這篇文章,請讀:https://finlab.finance/setup?relatedUrl=/blog/kd-stochastic-indicator-taiwan

免安裝,開啟 FinLab Studio

KD 動能選股加 KD 廣度風控策略與含息 0050 的台股淨值對照圖,2015 年 3 月 31 日同日起算到 2026 年 9 月

本文所有數字皆出自 finlab 套件(2.2.17)抓取的上市櫃真實價量資料(price 與 etl:adj_close 含息還原股價),回測從 2015-03-31 起算、資料截至 2026-09-30,基準為同日起算的含息 0050。2026-10 用延長後的資料重跑,參數一個都沒改:2022 年起月夏普由首發版(資料截至 2026-06-09)的 1.32 降到 1.16,仍在我們設的 1.0 門檻之上。這次重跑另外補做了換股日、檢查頻率與參數鄰格的檢查,結論比首發版保守很多,首發版的幾個錯誤也在對應段落更正。文末附可下載的 strategy.py 與 data.csv,可自行重現。對回測的觀念還不熟,可先看回測是什麼。

kd 指標是什麼

kd 指標(Stochastic Oscillator,隨機指標)由 George Lane 在 1950 年代提出,用來衡量「收盤價落在近期高低區間的哪個位置」。計算分三步:先算 RSV(未成熟隨機值),再把 RSV 平滑成 K 值,最後把 K 值再平滑成 D 值。

RSVt=Ct−LN,tHN,t−LN,t×100\mathrm{RSV}_t = \frac{C_t - L_{N,t}}{H_{N,t} - L_{N,t}} \times 100

其中 CtC_t 是今日收盤價,HN,tH_{N,t} 是近 NN 日最高價,LN,tL_{N,t} 是近 NN 日最低價。接著:

Kt=MA⁡slowk(RSVt),Dt=MA⁡slowd(Kt)K_t = \operatorname{MA}_{\mathrm{slowk}}(\mathrm{RSV}_t), \qquad D_t = \operatorname{MA}_{\mathrm{slowd}}(K_t)

所以 D 比 K 鈍、K 比 D 敏感。常用設定是:

N=9,slowk=3,slowd=3N = 9,\qquad \mathrm{slowk}=3,\qquad \mathrm{slowd}=3

kd 是「有界擺盪指標」,值永遠落在 0 到 100 之間。實務上衍生出三組教科書訊號:

黃金交叉 / 死亡交叉:K 值由下往上穿過 D 值(K−D 由負轉正)稱黃金交叉,視為轉強;反向穿越(K−D 由正轉負)稱死亡交叉,視為轉弱。

超買 / 超賣:K 值 > 80 視為超買(股價強勢但有過熱風險)、K 值 < 20 視為超賣(弱勢但可能跌深反彈)。

kd 鈍化:強趨勢中 K、D 會貼在高檔(或低檔)一段時間,交叉與超買超賣訊號暫時失效,這個現象稱鈍化。

KD 指標訊號圖解 這張圖用三個區塊說明 KD 黃金交叉與死亡交叉、超買與超賣,以及 K 值與 D 值在強趨勢中長時間貼近高檔的鈍化現象。 1. 黃金交叉 / 死亡交叉 K 線(藍)先低於 D 線,往上穿越時 K−D 由負轉正 黃金交叉 死亡交叉 K 值,較敏感 D 值,較平滑 2. 超買 / 超賣 100 80 20 0 K > 80:超買 K < 20:超賣 超買代表強勢但過熱,超賣代表弱勢但可能跌深反彈 3. kd 鈍化 100 80 0 強趨勢中 K、D 長時間貼近高檔或低檔,交叉與超買超賣訊號會短暫失效 高檔鈍化:不要只因 K > 80 就賣
這張 SVG 把三個常見讀法放在同一張圖:交叉看的是 K−D 的正負翻轉,超買超賣看的是 K 值落在 80 / 20 之外,鈍化則提醒你在強趨勢裡不要只靠單一交叉訊號做進出場。

技術指標的基礎名詞可參考名詞詞彙表。這些訊號聽起來都有道理,但「聽起來有道理」和「在台股回測賺得到錢」是兩回事,後面把每一種用法都跑了真實回測。

kd 指標的兩種算法:TA-Lib 與台灣券商軟體不一樣

上一段的式子是 TA-Lib 的算法,也是 finlab data.indicator("STOCH") 的預設:慢 K 是 RSV 的 3 日簡單平均,慢 D 再取慢 K 的 3 日簡單平均。台灣多數券商看盤軟體用的是另一種「遞迴平滑」,用前一日的 K、D 各帶三分之二的權重:

RSV=C−LnHn−Ln×100,K=23Kt−1+13RSV,D=23Dt−1+13KRSV = \frac{C - L_n}{H_n - L_n}\times 100,\quad K = \tfrac{2}{3}K_{t-1} + \tfrac{1}{3}RSV,\quad D = \tfrac{2}{3}D_{t-1} + \tfrac{1}{3}K

逐個符號拆開來看:

  • CC 是當日收盤價,HnH_n、LnL_n 是近 nn 日(常用 n=9n=9)的最高價與最低價。RSVRSV(未成熟隨機值)把收盤價放進近期高低區間做標準化,數值落在 0 到 100 之間:貼近區間頂端代表近期偏強、貼近底端代表偏弱。
  • Kt=23Kt−1+13RSVK_t = \tfrac{2}{3}K_{t-1} + \tfrac{1}{3}RSV 是把當日 RSVRSV 對前一日 K 值做指數平滑,可削掉 RSVRSV 逐日跳動的雜訊。
  • Dt=23Dt−1+13KD_t = \tfrac{2}{3}D_{t-1} + \tfrac{1}{3}K 再把 K 值平滑一次,所以 D 比 K 鈍。兩者的差 K−DK - D 就是後面拿來選股與算廣度的快慢線差。

兩種算法的數值不同。用台積電(2330)2023-07 到 2024-06 這一年實算,兩種 K 值最多差 22 點;下一段的四種訊號,TA-Lib 版是金叉 31、死叉 31、K 跌破 20 共 9 次、K 突破 80 共 15 次,遞迴版是 26、26、5、13。本文所有回測都用 TA-Lib 版,拿券商軟體的 KD 值來對照本文數字會對不上。

不論哪一種算法,K 與 D 都是同一檔股票的價格各自平滑後相減,長期趨勢在相減時被抵掉,留下的是最近幾天的相對快慢。把 K−D 拿去做個股排名,回測裡全部輸給 0050;把它加總成市場廣度,在季底當減碼開關時能壓低回撤,但效果和換股日綁得很緊。

把訊號畫在個股上:兩派買賣點與甩巴掌問題

公式歸公式,把它畫在真實股價上最直觀。下面兩張圖取台積電(2330)與聯發科(2454)2023-07 到 2024-06 這一年,上方面板是收盤價並標出兩個教科書派別的進出場點,下方面板是 kd(9-3-3)的慢 K 與慢 D 兩條線與超買 80 / 超賣 20 帶。標的訊號用兩派:金叉死叉派(K 上穿 D 買、下穿 D 賣,三角形)與超買超賣派(K 跌破 20 買、突破 80 賣,圓點與叉號)。KD 用完整歷史計算後才取這一年,窗內第一天的交叉也用真實的前一日判斷。

台積電 2330 的 kd 指標金叉死叉派與超買超賣派買賣點示意圖,2023-07 到 2024-06 一年內 86 個訊號

聯發科 2454 的 kd 指標金叉死叉派與超買超賣派買賣點示意圖,一年內 78 個訊號,盤整段反覆來回

把點數一數就會看到第一個問題:訊號太多、太密。 光是金叉死叉派,台積電這一年就有 31 次金叉、31 次死叉;聯發科 27 次金叉、28 次死叉。加上超買超賣派的 9 個買點、15 個賣點(聯發科 8 個、15 個),兩派合計台積電 86 個、聯發科 78 個,平均每 2.8 到 3.1 個交易日就出現一個訊號。2023 下半年的橫盤段,K、D 兩條線反覆纏繞,金叉買進後沒幾天就死叉賣出,這就是技術分析常講的「甩巴掌」(whipsaw)。每一次來回都是一次買賣,都要付手續費與證交稅。

第二個問題是強趨勢裡的鈍化。這一年台積電上漲 66.8%、聯發科上漲 102.6%,K 值在 80 以上的日子分別占 28.0% 與 35.4%,最長連續 13 個交易日。照超買超賣派「K 突破 80 就賣」的規則,台積電一年賣了 15 次,多半賣在上漲途中,後面的漲幅就讓掉了。

這兩張圖的訊號可以自己重算。下面這段程式碼用 data.indicator("STOCH") 取 kd,再用交叉與門檻邏輯數出各派訊號,跑出來就是上面的 31、31、9、15:

顯示程式碼
from finlab import data
 
# 套件會在需要資料時自動引導登入
import finlab
finlab.login()
 
# 用完整歷史計算 KD(9-3-3),再取觀察窗
slowk, slowd = data.indicator("STOCH", fastk_period=9, slowk_period=3, slowd_period=3)
sid = "2330"
k = slowk[sid]
d = slowd[sid]
 
# 金叉死叉派:K 上穿 D 買、K 下穿 D 賣
golden = (k > d) & (k.shift(1) <= d.shift(1))
death = (k < d) & (k.shift(1) >= d.shift(1))
 
# 超買超賣派:K 跌破 20(進入超賣)買、K 突破 80(進入超買)賣
oversold_buy = (k < 20) & (k.shift(1) >= 20)
overbought_sell = (k > 80) & (k.shift(1) <= 80)
 
window = slice("2023-07-01", "2024-06-30")
signals = {"金叉": golden, "死叉": death, "超賣買": oversold_buy, "超買賣": overbought_sell}
for name, sig in signals.items():
    print(name, int(sig.loc[window].sum()))

把 sid 換成 "2454" 就是聯發科。不論換哪一檔,一年內訊號都是數十次的量級,訊號頻繁是 kd 當個股進出場依據的主要問題。

訊號頻繁不是中性的:周轉率怎麼吃掉報酬

在台股,每一筆賣出要付 0.3% 證交稅,加上手續費(0.1425%,多數券商有折扣),一買一賣的固定摩擦在 0.3% 到 0.5% 之間。上面那種每三天一個訊號的頻率,換算成全年就是把本金反覆繳很多次過路費。

回測的周轉率看得很清楚。下一段的「持有所有 K 在 D 之上的股票」(金叉後抱著、死叉才賣,月底換股)年換手 7.9 次,整個組合一年換掉近八輪;本文後面的完整策略一季換一次股,年換手 2.0 次。同樣用 kd 的資訊,頻率差了近四倍,差出來的周轉全是要從報酬裡扣掉的成本。

頻率的問題也出現在風控端。KD 廣度(可交易宇宙內 K−D > 0 的比例)一年穿越 0.5 門檻 58 次,如果每天照它調整部位,年換手會到 16.3 次(見後面的「拆開風控」段)。kd 的訊號跳得越勤,越要先問其中有多少是雜訊。

散戶最常見的 kd 用法,在台股 11 年都輸給 0050

我們在可交易宇宙(60 日均成交金額前 300 大、收盤 > 10 元,排除 ETF、金融保險股、處置與全額交割股)裡,把七種「kd 選股」用法各跑一次月底換股、等權、不擇時的回測,KD 參數都用 9-3-3。研究段從 2015-03-31 起算,與後面完整策略同一天:

kd 選股用法 研究段年化 研究段月夏普 2022 年起年化 全期最大回撤
超賣接刀(慢 K 最低 30 檔) 1.8% 0.19 12.0% -49.2%
K−D 最低 30 檔(買最弱等反彈) 2.3% 0.21 17.7% -47.5%
月底當天剛金叉的股票 7.4% 0.44 14.8% -40.2%
持有所有 K 在 D 之上的股票(金叉後抱著) 10.5% 0.60 15.8% -39.1%
K−D 的 60 日 z-score 最高 30 檔 10.3% 0.58 14.8% -46.6%
K−D 最高 30 檔(買最強) 9.1% 0.52 12.2% -47.7%
K−D 5 日變化最高 30 檔 9.5% 0.54 13.9% -45.6%
含息 0050(對照) 15.2% 1.00 30.5% -34.0%

kd 指標四種裸用法在研究段與 2022 年起的年化報酬長條圖,全部低於含息 0050

最多人用的「超賣抄底」在台股研究段年化只有 1.8%、全期最大回撤 -49.2%,是最差的一種用法。 一種解讀是,慢 K 最低的股票常處在下跌途中,用 kd 低檔去接,等於在下跌裡逆勢接刀。兩種「黃金交叉」版本也都輸給 0050:只買月底當天剛金叉的股票,每個月底選到的檔數中位數是 27 檔,研究段 7.4%;金叉後一直抱著的版本每個月約持有宇宙裡 45.7% 的股票,研究段 10.5%,本質上接近「買半個市場」,最大回撤卻比 0050 深 5 個百分點。首發版把後者標成「K−D 由負轉正選股」,實際程式是持有所有 K 在 D 之上的股票,這次把兩種都列出來。

把 K−D 拿來做個股排名同樣站不住:買最強、買最弱、看 z-score、看 5 日變化,研究段都在 2.3% 到 10.3% 之間,2022 年起全部低於 18%。kd 是 0 到 100 的有界值,個股排名只是把「近幾天相對強弱」重新貼一次標籤;低 kd 可能是健康回檔,也可能是轉空的起點,兩種互斥的情況混在同一個排名裡。同一套回測方法用在 MACD、RSI 上也得到「教科書裸用選股贏不了 0050」的結論,三大指標並列的差異可參考RSI vs MACD vs KD 三大指標台股實測。

KD 廣度:把個股 K−D 加總成市場溫度計

單看一檔個股的 kd 有沒有黃金交叉,雜訊太大。改看「可交易宇宙裡有多少比例的股票,K 值站在 D 值之上」,得到的是一個橫截面的多空讀數,本文稱為 KD 廣度。每檔股票的 K−D 都在 0 上下擺盪,所以廣度本身也在一半上下來回:2015 年 3 月起有 46.6% 的交易日高於 0.5,一年穿越 0.5 門檻 58 次。它反映的是最近幾天多數股票轉強還是轉弱,屬於很短期的讀數。

2025 年 10 月到 2026 年 9 月每日 KD 廣度與策略淨值的雙軸圖,標出四個季底換股日,廣度頻繁穿越 0.5 門檻

上圖是最近一年的每日 KD 廣度,虛線是四個季底換股日。廣度幾乎每週都在穿越 0.5,策略卻只在這四天讀一次:6 月 30 日那天廣度是 0.456,落在 0.5 下方(6 月 23 日到 30 日剛好是一段短暫轉弱),第三季就以 35% 部位持有;隔天 7 月 1 日廣度就回到 0.745。圖中的策略淨值在 6 月 22 日創高後回落,7 月底的低點之後只收復一部分。換股日不同,讀到的廣度與選到的股票都不同,後面「換股日挪一個月」那段把兩者一起挪動,看結果差多少。

完整策略:動能池裡挑漲最多、K−D 上升最快的 10 檔,季底看廣度與年線決定部位

把 K−D 的兩個用法組成一套完全不含基本面的純技術策略:

  1. 動能池:可交易宇宙中,120 日報酬前 80 名。
  2. 選股(綜合分):池內每檔算兩個分位,120 日報酬分位與 K−D 的 5 日變化分位,兩個都是「值越大、分位越接近 0」。綜合分 = 0.6 × 動能分位 + 0.4 × K−D 變化分位,取綜合分最低的 10 檔等權持有,也就是池內漲最多、K−D 上升最快的股票。K、D 用 STOCH(6, 3, 3)。
  3. 風控:換股日 KD 廣度 > 0.5,且 0050 還原價站上 200 日均線,才滿倉;否則整籃部位縮到 35%,其餘放現金,不加槓桿。
  4. 換股頻率:每季最後一個交易日換股,選股與風控都只在這天讀取,季中不檢查。

首發版把第 2 步寫成「挑強勢族群裡相對落後、正在補漲的股票」,方向寫反了。實際檢查 47 個換股日的入選股,它們在 80 檔動能池裡的 120 日報酬名次中位數是第 10 名、K−D 5 日變化名次中位數是第 14.5 名:選到的是池內最強、K−D 還在加速往上的股票。首發版也寫「廣度與趨勢訊號跑日頻」,回測其實是季底才讀一次,這次一併更正。

核心程式碼分三段,完整版在文末的 strategy.py。第一段建立可交易宇宙:

顯示程式碼
import numpy as np
import pandas as pd
from finlab import data
 
# 套件會在需要資料時自動引導登入
import finlab
finlab.login()
 
close = data.get("price:收盤價")
volume = data.get("price:成交股數")
amount = data.get("price:成交金額")
 
# 可交易宇宙:60 日均量 > 100 萬股、收盤 > 10 元、60 日均成交金額前 300 大
liquid = ((volume.rolling(60, min_periods=20).mean() > 1_000_000)
          & (close > 10)
          & amount.rolling(60, min_periods=20).mean().is_largest(300))
 
# 只留代號剛好 4 碼數字的股票,排除 ETF(00 開頭)、金融保險股、處置與全額交割股
cat = data.get("security_categories")
financial = set(cat.loc[cat["category"].astype(str)
                        .str.contains("金融|保險|銀行", na=False), "stock_id"].astype(str))
common_cols = [
    sid for sid in close.columns
    if len(str(sid)) == 4 and str(sid).isdigit()
    and not str(sid).startswith("00") and str(sid) not in financial
]
flagged = (data.get("etl:is_flagged_stock")
           .reindex(index=close.index, columns=common_cols).fillna(False).astype(bool))
universe = liquid[common_cols] & ~flagged
close = close[common_cols]

第二段在動能池裡算綜合分、挑出 10 檔:

顯示程式碼
# 動能池:宇宙內 120 日報酬前 80 名
momentum = close / close.shift(120) - 1
pool = momentum.where(universe).rank(axis=1, ascending=False) <= 80
 
# K−D 與它的 5 日變化(STOCH 6-3-3;指標輸出的欄位比收盤價少,先對齊欄位)
slowk, slowd = data.indicator("STOCH", fastk_period=6, slowk_period=3, slowd_period=3)
kd_diff = (slowk - slowd).reindex(columns=common_cols)
kd_slope = kd_diff - kd_diff.shift(5)
 
# 兩個分位都是「值越大越接近 0」,綜合分最低的 10 檔 = 漲最多、K−D 上升最快
mom_score = momentum.where(pool).rank(axis=1, ascending=False, pct=True)
slope_score = kd_slope.where(pool).rank(axis=1, ascending=False, pct=True)
combo = 0.6 * mom_score + 0.4 * slope_score
basket = combo.where(pool).rank(axis=1, ascending=True) <= 10

第三段算 KD 廣度與 0050 年線,決定部位,季底換股回測:

顯示程式碼
from finlab.backtest import sim
 
# KD 廣度:宇宙內 K−D > 0 的比例
breadth = (kd_diff > 0).where(universe).sum(axis=1) / universe.sum(axis=1).replace(0, np.nan)
 
# 風控:廣度 > 0.5 且 0050 還原價站上 200 日均線才滿倉,否則整籃縮到 35%
adj_close = data.get("etl:adj_close")
idx = adj_close["0050"].dropna()
trend_on = (idx > idx.rolling(200).mean()).reindex(close.index).ffill().fillna(False)
risk_on = (breadth > 0.5).fillna(False) & trend_on.astype(bool)
 
weights = basket.astype(float)
weights = weights.div(weights.sum(axis=1).replace(0, np.nan), axis=0).fillna(0.0)
scale = risk_on.map({True: 1.0, False: 0.35})
position = weights.mul(scale, axis=0)
 
# 每季最後一個交易日換股(風控也只在這天讀取),從 2015 年起回測
report = sim(position.loc["2015":], resample="Q", fee_ratio=1.425 / 1000 * 0.3, upload=False)

這三段在乾淨環境實跑,2025-12-31、2026-03-31、2026-06-30、2026-09-30 四個換股日選出的股票與部位,和完整引擎逐檔相同。

這套策略全期(2015-03-31 到 2026-09-30)年化 29.4%、月夏普 1.14、月索提諾 2.65、最大回撤 -25.4%。 同期含息 0050 年化 21.3%、月夏普 1.07、月索提諾 1.85、最大回撤 -34.0%,四項都是策略較好。分段看就不一樣了:

區間 策略 年化 / 月夏普 / 月索提諾 / 最大回撤 含息 0050 年化 / 月夏普 / 月索提諾 / 最大回撤
研究段 2015-03-31 至 2021 34.9% / 1.20 / 3.28 / -25.4% 15.2% / 1.00 / 1.46 / -28.2%
2022 年起至 2026-09-30 22.0% / 1.16 / 1.96 / -21.0% 30.5% / 1.21 / 2.44 / -34.0%
全期 29.4% / 1.14 / 2.65 / -25.4% 21.3% / 1.07 / 1.85 / -34.0%

研究段大幅領先 0050;2022 年起年化落後 8.5 個百分點,月夏普與月索提諾也低於 0050,勝出的只剩最大回撤較淺(-21.0% 對 -34.0%)。年化波動 23.2%,0050 是 19.9%。年換手 2.0 次。

下面是完整互動式回測,月報酬熱力圖、權益曲線、年度績效都可以直接在文章裡操作:

拆開風控:KD 廣度與 0050 年線各自的作用

選股不動,只換風控,看每一層的作用:

風控版本 研究段 年化 / 月夏普 2022 年起 年化 / 月夏普 全期最大回撤
完整(廣度 > 0.5 且 0050 站上年線) 34.9% / 1.20 22.0% / 1.16 -25.4%
只留 0050 年線 32.7% / 0.97 25.7% / 0.95 -56.9%
只留 KD 廣度 33.6% / 1.12 20.9% / 1.05 -34.6%
不減碼(全程滿倉) 40.6% / 1.04 29.8% / 1.00 -59.7%
risk-off 改全現金 29.3% / 1.10 15.7% / 0.96 -23.4%
對照:同一籃改成每天依風控調整部位 10.4% / 0.56 12.4% / 0.67 -41.8%

完整策略、只留 0050 年線、只留 KD 廣度、不減碼四種風控版本的研究段與 2022 年起月夏普,以及全期最大回撤長條圖

三件事看得出來。第一,減碼是用報酬換回撤:不減碼的同一籃年化最高(研究段 40.6%、2022 年起 29.8%),最大回撤卻是 -59.7%;完整風控把回撤壓到 -25.4%,月夏普兩段都比不減碼高。第二,壓回撤主要靠 KD 廣度:只留 0050 年線,最大回撤仍有 -56.9%,只留廣度就降到 -34.6%,兩者合用最淺。第三,改成每天依風控調整部位,月夏普和年化都掉到比不減碼還低(見下段)。

逐季拆開看,46 個完整季度裡有 30 季在季底判定為 risk-off,比例近三分之二。判定為 risk-off 的季度,不減碼那一籃接下來一季平均賺 6.1%、30 季中 13 季虧損;判定為 risk-on 的季度平均賺 18.4%、16 季中 4 季虧損。減碼少賠最多的幾季是 2018 年第三季(不減碼 -26.1%,策略 -7.5%)、2022 年第二季(-26.2%,-9.2%)、2018 年第四季(-21.1%,-7.4%)與 2025 年第一季(-20.2%,-7.2%)。

可是把同一籃改成每天依廣度與年線調整部位,研究段年化掉到 10.4%、年換手升到 16.3 次。廣度每天在 0.5 上下來回,每天跟著它調整,部位會頻繁在 100% 與 35% 之間切換,周轉大增,結果也差很多。首發版寫「kd 廣度風控必須跑日頻、不能降頻」,回測其實從頭到尾都是季底才讀,日頻的版本反而差很多,這段說法已刪除。

拆開選股:120 日動能與 K−D 斜率各自的貢獻

風控不動,只換選股:

選股版本 研究段 年化 / 月夏普 2022 年起 年化 / 月夏普 全期最大回撤
完整綜合分(0.6 動能 + 0.4 K−D 變化) 34.9% / 1.20 22.0% / 1.16 -25.4%
只用 120 日動能(動能前 10 名) 22.5% / 1.00 11.4% / 0.77 -26.7%
只用 K−D 變化(動能池內最快 10 檔) 19.9% / 0.91 18.0% / 0.97 -27.5%
含息 0050 15.2% / 1.00 30.5% / 1.21 -34.0%

完整綜合分、只用 120 日動能、只用 K−D 斜率與含息 0050 的研究段與 2022 年起年化報酬及月夏普長條圖

兩個零件單獨用都普通:只用動能,研究段 22.5%、2022 年起只剩 11.4%;只用 K−D 變化,研究段 19.9%。兩者合成綜合分後研究段 34.9%、月夏普 1.20,比任一邊都高。在動能池裡,K−D 變化和動能合用,研究段比只用動能高 12.4 個百分點。前面教科書表最後一列(全宇宙 K−D 5 日變化最高 30 檔)研究段只有 9.5%,但它和主策略在 KD 參數、持股檔數、換股頻率、風控都不同,不能單獨歸因於有沒有先篩動能。選股端「過去半年漲最多的股票傾向繼續漲」對應 Jegadeesh & Titman (1993) 的動能效應;K−D 變化在這裡的角色,是在強勢股裡再挑「最近幾天還在加速」的那幾檔。

換股日挪一個月,結果就差很多

前面所有數字都用 3、6、9、12 月的最後一個交易日換股。我們保持一季一換、規則與參數都不變,只把換股日挪到其他月份:

換股日 研究段 年化 / 月夏普 2022 年起 年化 / 月夏普 全期年化 全期最大回撤
3、6、9、12 月底(本文) 34.9% / 1.20 22.0% / 1.16 29.4% -25.4%
1、4、7、10 月底 4.0% / 0.31 13.2% / 0.65 7.7% -40.7%
2、5、8、11 月底 6.6% / 0.39 19.7% / 0.87 11.7% -44.7%
每個月底 6.9% / 0.44 16.5% / 0.80 10.6% -32.8%
含息 0050 15.2% / 1.00 30.5% / 1.21 21.3% -34.0%

只挪一個月,全期年化從 29.4% 掉到 7.7%,比 0050 還低;換到另一個相位是 11.7%,改成每月換股是 10.6%。為了確認不是做法造成的差異,同一套程式把換股日設回 3、6、9、12 月底,跑出來與原回測逐位相同。

這叫換股時點運氣(rebalance timing luck)。Hoffstein, Faber & Braun (2020) 分析 smart beta 指數時指出,規則完全相同、只差換股日的組合,績效可以有明顯落差,落差大小和換股頻率、每次換股的周轉幅度有關。本文的策略只持有 10 檔、一季才換一次,這類集中又低頻的策略特別需要先檢查換股日。我們沒有找到季底換股特別有效的原因,在找到之前,29.4% 應該看成「3、6、9、12 月底換股這一組」的結果,不能當成這套 kd 用法的穩定報酬。這個檢查是首發版沒有做的。

參數最佳化:fastk 與廣度門檻

再看兩個 kd 參數:STOCH 的 fastk(同時影響 K−D 變化與 KD 廣度)× 廣度門檻,其餘設定不動,16 格各跑一次,研究段與 2022 年起月夏普分開畫,兩張圖用同一組色階:

STOCH fastk 週期乘以 KD 廣度門檻的研究段月夏普色溫圖

同一張 fastk 乘以廣度門檻網格的 2022 年起月夏普色溫圖

研究段 16 格的月夏普在 0.90 到 1.20 之間,本文採用的 fastk 6、門檻 0.50 是最高的一格。2022 年起 16 格落在 0.72 到 1.18,8 格高於 1.0;首發版同一張網格的 2022 年起數字有 13 格在 1.2 以上,新資料讓整片往下移。

單一參數換一格(其餘不變)的結果:

鄰格 研究段月夏普 2022 年起 年化 / 月夏普 全期最大回撤
本文(fastk 6、池 80、10 檔、留倉 0.35) 1.20 22.0% / 1.16 -25.4%
risk-off 留倉 0.30 / 0.40 1.20 / 1.20 21.2% / 1.16、22.8% / 1.16 -24.2% / -28.6%
動能池 60 / 100 檔 1.10 / 1.18 19.0% / 1.10、24.7% / 1.32 -28.8% / -24.3%
持股 9 / 11 檔 1.16 / 1.17 21.6% / 1.13、20.4% / 1.11 -27.3% / -26.8%
fastk 7 / 8 0.81 / 0.87 22.2% / 1.23、16.1% / 1.25 -37.1% / -38.9%

留倉係數、動能池大小、持股檔數換一格,研究段月夏普在 1.10 到 1.20、2022 年起在 1.10 到 1.32 之間。fastk 不是:fastk 5、6、9 的研究段月夏普是 1.18、1.20、1.10,夾在中間的 7 與 8 卻只有 0.81、0.87,最大回撤加深到 -37% 以上。首發版寫「fastk 在 5 到 9 之間掃,fastk 6 是高原中心」,當時沒有跑 7 與 8;補跑之後,fastk 6 是一個窄峰。

2026-06-09 之後:首發後的新資料

首發版的資料截至 2026-06-09。延長到 2026-09-30 的這段,策略累計 -5.0%,同期含息 0050 +8.9%;其中 6 月 9 日到 30 日策略 +0.4%、0050 +4.2%,之後逐月如下:

期間 策略 不減碼的同一籃 每天調整的對照組 含息 0050
2026 年 7 月 -12.7% -34.5% -35.2% -4.0%
2026 年 8 月 +5.3% +19.7% +13.2% +3.3%
2026 年 9 月 +2.9% +9.6% +10.3% +5.5%
06-09 至 09-30 累計(含 6 月 9 日到 30 日) -5.0% -13.3% -19.8% +8.9%

首發資料截止日 2026-06-09 之後,KD 策略、每天依風控調整的對照組與含息 0050 的淨值走勢

經過是這樣:0050 這段期間一直站在 200 日均線上,風控的變化全部來自 KD 廣度。6 月 30 日換股時,廣度剛好處在 6 月 23 日開始的短暫轉弱裡,低於 0.5,所以第三季整籃只放 35%。選出的 10 檔從 7 月 1 日進場抱到 8 月 3 日全數虧損,中位數 -23.0%,最差的國巨(2327)-51.6%,表現最好的聯發科(2454)也跌了 9.3%;抱滿整季到 9 月 30 日,10 檔中位數 -10.3%。策略從 6 月 22 日的高點到 7 月 30 日回落 18.1%,7 月的 -12.7% 是整個回測最差的單月,研究段最差的是 2016 年 4 月的 -11.4%。

這一季沒有跟著重摔,靠的是 6 月 30 日那一天的讀數。如果那天廣度在 0.5 之上,同一籃滿倉持有,7 月會是 -34.5%;每天照風控調整的對照組,因為 7 月初與月中幾度轉回 risk-on,7 月 -35.2%。同一批技術指標文在 2026-09 重跑時多數在 7 月重挫,經過可對照 威廉指標怎麼用才對。

依季度刷新的規則,判斷看 2022 年起的段落:同一次執行把淨值截到首發日 2026-06-09,2022 年起年化 25.2%、月夏普 1.32;延長到 2026-09-30 後是 22.0%、1.16,仍高於年化 15% 與月夏普 1.0 的門檻,所以參數不動、照一般刷新處理。研究段由首發的 35.3% 變成 34.9%、0050 由 15.3% 變成 15.2%,原因是 finlab 版本差異與 0050 起算日對齊(新版回測的首個淨值日是 2015-03-31),用新版在首發同一截止日重跑就是 34.9%,和新資料無關;研究段月夏普嚴格說是 1.198,比首發選參數用的 1.2 門檻低一點。

回測方法與口徑

量化結論的可信度取決於方法,以下逐項交代:

  • 交易成本:策略使用 finlab sim(),台股預設已內扣手續費(本文以 0.1425% 打三折計)與賣出證交稅 0.3%。
  • 滑價:未額外假設滑價,實際成交衝擊取決於資金規模與流動性,本文未估算容量。
  • 股票池:上市櫃股票,60 日均量大於 100 萬股、收盤大於 10 元、60 日均成交金額前 300 名。
  • 排除類別:只留代號剛好 4 碼數字的股票(排除權證、特別股、6 碼代號),再排除 ETF(代號 00 開頭)、金融保險股、處置與全額交割股。KY 股代號是 4 碼數字,沒有排除,7 月持股裡的 4958 就是 KY 股;首發版寫「排除 KY」與程式不符,已更正。首發版只檢查代號前 4 碼,用首發的規則重跑,換股日持股完全相同。
  • 生存者偏差:宇宙每天依當時資料篩選,2015-03-31 有收盤價、到 2026-09-30 已無收盤價的 4 碼普通股共 143 檔,在當時都可能被選入。
  • 前視偏差:120 日動能、K−D、K−D 的 5 日變化、KD 廣度與 0050 均線都只使用當日(含)以前的價格;換股日收盤算出的訊號從下一個交易日起生效。主策略不使用任何財報或月營收。
  • 檢查頻率:選股與風控都只在每季最後一個交易日讀取,季中不減碼、不加碼。2015 年起日頻的 risk-off 區段有 269 段,其中 242 段在下一個季底前就轉回、從未執行;換股日判定為 risk-off 的比例是 66.0%(47 個換股日中 31 個)。
  • 權重:入選 10 檔等權,risk-off 時整籃乘上 0.35,其餘現金,不持有防禦標的、不用槓桿。
  • 周轉率:年換手約 2.0 次。
  • 基準與起算日:含息 0050 用 etl:adj_close 純算術買進持有,與策略同日 2015-03-31 起算。首發版的 0050 從 2015-01-05 起算,已對齊。
  • 樣本內外:研究段 2015-2021 是選參數用的;2022 年起在首發時已被看過,屬於選參數時參考過的驗證段,真正沒看過的資料只有 2026-06-09 之後這四個月。換股相位、每日檢查與 fastk 7、8 的檢查都是這次才補做。
  • 下載檔:首發版的 strategy.py 在新版 finlab 會在 KD 廣度的分母除以 0 時中斷,data.csv 則是 6 月定案前另一套低波策略的淨值,都已換成本文策略;新的 strategy.py 在乾淨目錄實跑,印出的主表、零件拆解與逐年報酬與本文逐格相同。

不同年度的表現:12 年贏 6 年,2024 年起連三年落後

年度 策略 含息 0050 不減碼的同一籃
2015(3/31 起) 13.6% -9.3% 15.6%
2016 9.2% 19.6% 25.4%
2017 33.6% 18.1% 49.0%
2018 65.5% -4.9% 12.7%
2019 15.2% 33.5% 52.2%
2020 28.9% 31.1% 58.0%
2021 85.5% 21.9% 70.3%
2022 -16.4% -21.4% -43.1%
2023 67.9% 27.5% 96.7%
2024 38.4% 48.6% 53.5%
2025 7.0% 36.8% 17.8%
2026(至 9/30) 23.2% 74.3% 68.6%

策略贏 0050 的 6 年集中在 2017、2018、2021、2023 這幾個強勢股表現突出的年份,以及 2015、2022 兩個空頭年。2024 年起連續三年落後,2026 年前三季 0050 漲 74.3%,策略只有 23.2%:2024-09-30 起連續 9 個季底都判定為 risk-off(0050 只有 2025-03-31 那次在年線下,其餘都是廣度低於 0.5),兩年來策略一直只持有 35% 部位。和不減碼的同一籃比,風控在 2022 年少賠 26.7 個百分點,但在 2019、2020、2026 這些多頭年少賺 29 到 45 個百分點;2018 年策略 65.5% 遠高於不減碼的 12.7%,那年的兩次大跌(第三、四季)剛好都在季底判定為 risk-off。

加上月營收 YoY 會更好嗎:實跑結果比主策略好,但不換

本文主策略完全不含基本面,選股只用 120 日動能與 K−D 變化,風控只用 KD 廣度與 0050 年線。純技術訊號每日可得,沒有財報公布延遲與對齊問題,也才與本文「kd 用法」的主題一致。

首發版寫「加營收過濾會大幅縮小可選池、稀釋 K−D 挑股的爆發力」,當時沒有實測。這次實跑:月營收以公布日對齊、只用已公布的數字,YoY > 0 的股票才能進動能池,風控與其他設定不變。結果研究段年化 40.7%、月夏普 1.21,2022 年起 23.9%、1.29,全期最大回撤 -23.9%,兩段都比主策略好。首發版的說法不成立,已改寫。

我們沒有因此改用營收版:這個變體是看過全部資料後才測的,換過去就等於用新資料挑規則,依季度刷新的原則不替換。它同樣是季底換股,換股日的影響本文沒有另外檢查。想自己試,可在下載的程式碼裡於動能池之前加一層 monthly_revenue 的過濾。技術面與基本面怎麼分工,可延伸看技術分析 vs 基本面分析台股回測。

三項穩健性驗證:重跑到 2026 年 10 月

這裡驗證本文的完整策略:120 日動能前 80 名的池子裡,依 120 日報酬與 K−D 5 日變化的綜合分取 10 檔等權,KD 廣度不到 0.5 或 0050 跌破年線時部位縮到 35%,每季換股。我們在 2026-10-27 用本文附的 strategy.py(把 DATA_END 改成 None,finlab 2.2.20,資料截至 2026-10-27)重跑三項檢查,統一採 sim() 預設手續費 0.1425% 與證交稅 0.3%;原程式手續費三折的結果另列於成本對照表。「贏」指日夏普高於同期 0050 含息買進持有(etl:adj_close 還原價、純算術計算、不扣成本)。兩邊日夏普皆為日報酬平均除以標準差、乘上 √252,不扣無風險利率。

檢查 標準 結果
鄰近參數 KD 週期(fastk)(5、6、7)乘上持股檔數(8、10、12),9 組至少 7 組贏 4/9 組贏,不過
前後兩段 2015-03-31 到 2021-01-12、2021-01-12 到 2026-10-27 兩段都贏 前段 1.17 對 0.99,後段 1.31 對 1.17,過
交易成本 原參數、sim() 預設成本 日夏普 1.24 對 1.08,過

KD 斜率動能策略鄰近參數 9 組日夏普熱圖:4 組高於同期 0050 含息,原參數日夏普 1.24

9 組參數的日夏普(括號內是年化報酬,粗體是原文參數):

持股檔數 \ KD 週期(fastk) 5 6 7
12 1.07(年化 24.9%) 1.12(年化 24.7%) 0.99(年化 21.9%)
10 1.13(年化 27.5%) 1.24(年化 28.6%) 0.91(年化 19.8%)
8 1.06(年化 26.5%) 1.24(年化 29.8%) 0.92(年化 20.9%)

前後兩段與全期:

期間 策略年化 策略日夏普 0050 含息年化 0050 日夏普
前段 2015-03-31 到 2021-01-12 26.5% 1.17 15.5% 0.99
後段 2021-01-12 到 2026-10-27 30.8% 1.31 26.0% 1.17
全期 2015-03-31 到 2026-10-27 28.6% 1.24 20.7% 1.08

前後兩段是把整段期間從中點對半切。上文的研究段與樣本外以 2022 年初為界,照這個切法,2015 到 2021 年日夏普 1.39 對 0050 的 0.96,2022 年起 1.01 對 1.22。上文多用月夏普,跟這裡的日夏普算法不同,數字不能直接比。

交易成本對照(原參數):

成本設定 年化 日夏普 最大回撤
不扣成本 30.2% 1.29 -25.1%
sim() 預設:手續費 0.1425%、證交稅 0.3% 28.6% 1.24 -25.6%
原文設定:手續費打三折 29.1% 1.26 -25.4%
0050 含息買進持有(同期) 20.7% 1.08 -34.0%

重跑到 2026-10-27,原參數年化 28.6%、日夏普 1.24、最大回撤 -25.6%;同期 0050 含息年化 20.7%、日夏普 1.08、最大回撤 -34.0%。前後兩段與交易成本都過:前段日夏普 1.17 對 0.99、後段 1.31 對 1.17(後段從 2021 年 1 月起算;照上文以 2022 年切開,2022 年起是 1.01 對 1.22,跟上文「2022 年起落後」一致);季換股的換手低,扣掉 sim() 預設成本只比不扣少 0.05(1.29 降到 1.24)。卡在鄰近參數:9 組只有 4 組贏。fastk 6 的三組都贏(1.12 到 1.24),fastk 改成 5 只剩持股 10 檔那組贏,改成 7 三組全輸(0.91 到 0.99)。三項沒有全過,所以不放進策略庫。跟上文「換股日挪一個月,結果就差很多」一樣,績效集中在特定的參數與換股日上。上文的年化 29.4% 是資料到 2026-09-30、手續費打三折的結果;照三折跑到 2026-10-27 是 29.1%,換成 sim() 預設成本是 28.6%。

策略的限制與取捨

  • 換股日決定了大部分結果:同一套規則換到其他月份換股,全期年化只剩 7.7% 到 11.7%,低於 0050。實盤若照季底換股,等於押注這個時點效果會延續,而本文找不到它會延續的理由。
  • 2022 年起落後 0050:年化 22.0% 對 30.5%,月夏普 1.16 對 1.21,勝出的只有回撤較淺。2024 年起連三年落後。
  • 大部分時間只有 35% 部位:47 個換股日中 31 個判定為 risk-off,2024-09-30 起更是連續 9 個季底。減碼讓回撤變淺,也讓多頭年少賺很多;判定只看季底一天的廣度,讀數受短期波動影響大。
  • 集中與季頻:只持有 10 檔,選到的是動能池裡最強、K−D 還在加速的股票,選錯就要扛一整季(2026 年第三季 10 檔中位數 -10.3%)。
  • fastk 是窄峰:fastk 7、8 的研究段月夏普只有 0.81、0.87,參數稍微一動結果就差很多。
  • 容量有限:每檔權重 10%,大資金部署需重估流動性衝擊,本文未逐筆估算容量與滑價。

想把「市場廣度」或「大盤濾網」當風控,這幾點同樣適用;用 0050 均線當濾網的另一種做法,可參考用大盤濾網降低最大回撤。

自己跑一次

FinLab Skill 可以讓你的 AI 助理安裝環境、抓台股資料,並重現這篇回測。

裝好之後,下載本文的 strategy.py 與 data.csv,即可在自己的環境重跑、改參數、換選股因子。strategy.py 把資料截止日釘在 2026-09-30,執行後會印出本文的主表、風控與選股的零件拆解、每天調整的對照組、逐年報酬與 2026-06-09 之後的逐月報酬;把 DATA_END 改成 None 就用最新資料。data.csv 是策略與含息 0050 同日起算的每日淨值,外加每日的 KD 廣度與 risk-on 狀態。想測換股日,可只在你指定的月底取部位、往後填滿到下一個換股日,再用 resample=None 回測。登入只需在程式裡寫 finlab.login(),套件會在需要資料時引導你完成登入。想延伸成更完整的量化流程,可參考量化交易總覽與程式交易是什麼。其他技術指標用對方法後的成績,匯整在常用技術指標的台股回測總覽。

常見問題

Q:kd 黃金交叉買進在台股有用嗎? 在本文設定、2015-03-31 到 2026-09-30 這段期間,兩種黃金交叉用法都輸給含息 0050:只買月底當天剛金叉的股票,研究段年化 7.4%;金叉後一直抱到死叉,研究段 10.5%,同期 0050 是 15.2%。後者每個月約持有宇宙裡四成五的股票,年換手 7.9 次,最大回撤 -39.1% 也比 0050 深。

Q:kd 超賣(K<20)抄底會反彈嗎? 本文測的是每月買慢 K 最低的 30 檔(不限定 K<20),研究段年化只有 1.8%、全期最大回撤 -49.2%,是七種用法裡最差的,2022 年起 12.0% 也遠低於 0050 的 30.5%。回測沒有另外統計單次反彈的機率;一種解讀是,慢 K 最低的股票常處在下跌途中,等反彈的過程要先承受更深的回撤。

Q:kd 參數要設多少?9、3、3 是最好的嗎? 9-3-3 是最常見的教科書設定,拿來看圖沒有問題。本文策略用 fastk 6,但補跑鄰格後發現 fastk 5、6、9 的研究段月夏普是 1.18、1.20、1.10,7 與 8 只有 0.81、0.87,沒有一個「越調越好」的方向。參數微調的效果小於用法本身,也小於換股日的影響。

Q:kd 鈍化是什麼?要怎麼處理? 強趨勢中 K、D 會貼在高檔或低檔一段時間,交叉與超買超賣訊號暫時失效,這就是鈍化。2023-07 到 2024-06 台積電上漲 66.8%,K 值在 80 以上的日子占 28.0%,照「K 突破 80 就賣」一年賣了 15 次。鈍化的定義與參數實驗見如何定義 kd 鈍化。

Q:KD 廣度是什麼?可以每天看嗎? KD 廣度是可交易宇宙裡 K 在 D 之上的股票比例,一年穿越 0.5 門檻約 58 次。本文策略只在季底讀一次;同一籃改成每天依廣度與 0050 年線調整部位,研究段年化從 34.9% 掉到 10.4%、年換手升到 16.3 次。每天跟著它進出,結果反而差很多。

Q:為什麼換股日差一個月,結果差這麼多? 策略只持有 10 檔、一季才換一次,換股那天選到哪 10 檔、廣度讀數是高是低,會決定接下來三個月的持股與部位。3、6、9、12 月底換股全期年化 29.4%,挪到 1、4、7、10 月底只有 7.7%。本文沒有找到季底特別有效的原因,這個差距應該視為換股時點運氣,不能當成可以期待的報酬。

Q:這套策略適合誰? 適合想研究「指標怎麼拆成選股與風控零件」、並願意自己檢查換股日與參數敏感度的人。不適合想直接照抄進場的人:它在 2022 年起落後 0050,換股日一動結果就差很多。只想參與台股長期報酬,直接買進持有含息 0050 更省事。


最後更新:2026-10|回測區間:2015-03-31 到 2026-09-30|作者:FinLab 量化研究團隊(經量化研究員審閱)

本文僅供量化研究與教學用途,所有回測數字均由 finlab 套件在真實台股資料上產出,過去績效不代表未來表現,不構成投資建議。策略全期最大回撤 -25.4%,換股日改變時全期年化只剩 7.7% 到 11.7%,實際交易前請自行評估風險、滑價與交易容量。

FinLab AI

想建立自己的策略?

用自然語言描述你的選股想法,AI 自動驗證、回測、給你答案

免費開始

更多技術指標研究

查看全部