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 套件(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 值。
其中 是今日收盤價, 是近 日最高價, 是近 日最低價。接著:
所以 D 比 K 鈍、K 比 D 敏感。常用設定是:
kd 是「有界擺盪指標」,值永遠落在 0 到 100 之間。實務上衍生出三組教科書訊號:
黃金交叉 / 死亡交叉:K 值由下往上穿過 D 值(K−D 由負轉正)稱黃金交叉,視為轉強;反向穿越(K−D 由正轉負)稱死亡交叉,視為轉弱。
超買 / 超賣:K 值 > 80 視為超買(股價強勢但有過熱風險)、K 值 < 20 視為超賣(弱勢但可能跌深反彈)。
kd 鈍化:強趨勢中 K、D 會貼在高檔(或低檔)一段時間,交叉與超買超賣訊號暫時失效,這個現象稱鈍化。
技術指標的基礎名詞可參考名詞詞彙表。這些訊號聽起來都有道理,但「聽起來有道理」和「在台股回測賺得到錢」是兩回事,後面把每一種用法都跑了真實回測。
kd 指標的兩種算法:TA-Lib 與台灣券商軟體不一樣
上一段的式子是 TA-Lib 的算法,也是 finlab data.indicator("STOCH") 的預設:慢 K 是 RSV 的 3 日簡單平均,慢 D 再取慢 K 的 3 日簡單平均。台灣多數券商看盤軟體用的是另一種「遞迴平滑」,用前一日的 K、D 各帶三分之二的權重:
逐個符號拆開來看:
- 是當日收盤價,、 是近 日(常用 )的最高價與最低價。(未成熟隨機值)把收盤價放進近期高低區間做標準化,數值落在 0 到 100 之間:貼近區間頂端代表近期偏強、貼近底端代表偏弱。
- 是把當日 對前一日 K 值做指數平滑,可削掉 逐日跳動的雜訊。
- 再把 K 值平滑一次,所以 D 比 K 鈍。兩者的差 就是後面拿來選股與算廣度的快慢線差。
兩種算法的數值不同。用台積電(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 用完整歷史計算後才取這一年,窗內第一天的交叉也用真實的前一日判斷。


把點數一數就會看到第一個問題:訊號太多、太密。 光是金叉死叉派,台積電這一年就有 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% |

最多人用的「超賣抄底」在台股研究段年化只有 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 次。它反映的是最近幾天多數股票轉強還是轉弱,屬於很短期的讀數。

上圖是最近一年的每日 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 的兩個用法組成一套完全不含基本面的純技術策略:
- 動能池:可交易宇宙中,120 日報酬前 80 名。
- 選股(綜合分):池內每檔算兩個分位,120 日報酬分位與 K−D 的 5 日變化分位,兩個都是「值越大、分位越接近 0」。綜合分 = 0.6 × 動能分位 + 0.4 × K−D 變化分位,取綜合分最低的 10 檔等權持有,也就是池內漲最多、K−D 上升最快的股票。K、D 用 STOCH(6, 3, 3)。
- 風控:換股日 KD 廣度 > 0.5,且 0050 還原價站上 200 日均線,才滿倉;否則整籃部位縮到 35%,其餘放現金,不加槓桿。
- 換股頻率:每季最後一個交易日換股,選股與風控都只在這天讀取,季中不檢查。
首發版把第 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% |

三件事看得出來。第一,減碼是用報酬換回撤:不減碼的同一籃年化最高(研究段 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% |

兩個零件單獨用都普通:只用動能,研究段 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 年起月夏普分開畫,兩張圖用同一組色階:


研究段 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% |

經過是這樣: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,過 |

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 自動驗證、回測、給你答案
免費開始

