請更新您的瀏覽器

您使用的瀏覽器版本較舊,已不再受支援。建議您更新瀏覽器版本,以獲得最佳使用體驗。

理財

德伍.卻斯的成長動能選股策略:價值與動能的交會點

TEJ 台灣經濟新報

更新於 06月17日09:33 • 發布於 06月17日06:00
Photo by Yeshi Kangrang on Unsplash

前言

在成長型投資領域中,德伍.卻斯(Derwood S. Chase Jr.)無疑是一位極具代表性的傳奇人物。投資生涯橫跨近半世紀的他,擁有紮實的學術背景,畢業於維吉尼亞大學,並於1954年取得哈佛大學企業管理碩士學位。1958年創立大通投資顧問公司(Chase Investment Counsel Corporation),專注於管理公司退休基金、信託基金與個人退休計畫,並不涉足與銀行相關業務。在他的領導下,公司至2001年已管理超過14億美元資產,其旗下機構大型成長股組合於1990至2001年間創下年化報酬率16.38%的亮眼成績,顯著超越同期S&P 500指數及Russell 1000 Growth指數。

1997年,德伍.卻斯進一步推出大通成長基金(Chase Growth Fund),同樣繳出優異表現。截至2001年底,該基金4年平均年報酬率達9.88%,遠高於S&P 500的5.65%,並在743檔同類型基金中名列第六,獲得晨星(Morningstar)五顆星的最高評等。

德伍.卻斯自許為一位風險意識強烈的GARP(Growth at a Reasonable Price)投資者,他的選股哲學融合數量研究、技術分析與基本面分析,並強調分散投資的重要性,認為理想的投資組合應包含35至45檔個股。透過兼具成長與穩健的策略,德伍.卻斯為投資人建立了穿越市場周期的長期回報典範。

為何德伍.卻斯如此設定選股與買進基準?

德伍.卻斯採用的選股標準,結合了企業的規模、成長性、獲利能力、估值與動能表現。他偏好總市值高於平均的大型企業,以降低流動性與營運風險;選擇過去五年盈餘成長率高、ROE 高的公司,代表企業具備穩健成長與經營效率。他也設下估值上限(預估本益比不得超過成長率兩倍),確保不會為成長支付過高溢價。同時納入股價相對強弱度,反映市場資金動向。這些條件搭配低於平均的負債比率,共同構築出一套注重「合理成長、穩健財務、順勢操作」的選股策略。

德伍.卻斯的選股邏輯

根據德伍.卻斯的投資方法,他所採用的選股邏輯結合了企業的基本面強度、成長潛力、市場表現與財務穩健性。以下為其具體條件:

  • 總市值≧市場平均值  → 聚焦於具有穩定性與資金流動性的中大型企業,降低流動性風險。

  • 過去五年盈餘複合成長率(EPS CAGR)≧10%  → 確保公司在中長期具備穩定的獲利成長性。

  • 近四季股東權益報酬率(ROE)≧15%  → 評估企業運用資本創造價值的效率,高 ROE 代表企業經營效能良好。

  • 近一季負債比率<市場平均值  → 篩選財務結構穩健的公司,提升整體投資組合的抗壓能力。

  • 預估本益比(Forward P/E)≦EPS CAGR × 2  → 以成長性合理估值原則(GARP)篩選價格未被高估的標的。

近六個月股價相對強弱度(6M RS)>1  → 動能強勁,近期獲市場資金青睞。

回測基本設定

回測期間:2016-01-01 至2025-05-27

再平衡天數:30 個交易日

比較基準Benchmark:IR0001 簡稱大盤

以下程式碼抓取財務資料時使用的是資料庫代碼,以下展示代碼對應的財務欄位

coid roi mktcap r505 r104 per close_d r316 股票代碼 日報酬率 公司市值 流動比率 常續ROE 本益比 收盤價 EPS 資料庫欄位代碼對應財務資料

資料整理程式碼展示

import pandas as pd import numpy as np import tejapi import os import json import matplotlib.pyplot as plt plt.rcParams['font.family'] = 'Arial' tej_key = 'your key' tejapi.ApiConfig.api_key = tej_key os.environ['TEJAPI_BASE'] = "https://api.tej.com.tw" os.environ['TEJAPI_KEY'] = tej_key from zipline.sources.TEJ_Api_Data import get_universe import TejToolAPI from zipline.data.run_ingest import simple_ingest from zipline.api import set_slippage, set_commission, set_benchmark, symbol, record, order_target_percent from zipline.finance import commission, slippage from zipline import run_algorithm start_date = '2010-01-01'; end_date = '2025-05-27' pool = get_universe(start = start_date, end = end_date, mkt_bd_e = ['TSE', 'OTC'], stktp_e = ['Common Stock-Foreign','Common Stock']) columns = ['coid', 'roi', 'mktcap', 'r505', 'r104', 'per', 'close_d', 'r316'] start_dt = pd.Timestamp(start_date, tz = 'UTC') end_dt = pd.Timestamp(end_date, tz = "UTC") data_use = TejToolAPI.get_history_data(start = start_dt, end = end_dt, ticker = pool + ['IR0001'], fin_type = ['Q', 'TTM'], columns = columns, transfer_to_chinese = False) data_use['mdate'] = pd.to_datetime(data_use['mdate']) data_use = data_use.sort_values('mdate') data_use['avg_market_cap'] = data_use.groupby('mdate')['Market_Cap_Dollars'].transform('mean') data_use['avg_debt_ratio'] = data_use.groupby('mdate')['Liabilities_Ratio_Q'].transform('mean') data_use['EPS_5y_ago'] = data_use.groupby('coid')['Net_Income_Per_Share_Q'].shift(252 * 5) data_use['EPS_CAGR_5Y'] = ((data_use['Net_Income_Per_Share_Q'] / data_use['EPS_5y_ago']) ** (1/5)) - 1 # 以 126 個交易日作為近 6 個月(可依台股調整為 120~130 天) data_use['ret_6m'] = data_use.groupby('coid')['Close'].transform(lambda x: x / x.shift(126) - 1) # 從 df 中抽出大盤(IR0001)的每日報酬率 market_ret = data_use[data_use['coid'] == 'IR0001'][['mdate', 'ret_6m']].rename(columns={'ret_6m': 'market_ret_6m'}) # 把 market_ret 併回所有資料(用日期對齊) data_use = data_use.merge(market_ret, on='mdate', how='left') data_use['RS_ratio_6m'] = data_use['ret_6m'] / data_use['market_ret_6m'] data_use['mdate_shifted'] = data_use.groupby('coid')['mdate'].shift(1) def compute_stock(date, data): df = data[data['mdate_shifted'] == pd.to_datetime(date)].reset_index(drop = True) set_1 = set(df[df['Market_Cap_Dollars'] >= df['avg_market_cap']]['coid']) set_2 = set(df[df['EPS_CAGR_5Y'] >= .1]['coid']) set_3 = set(df[df['Net_Income_Per_Share_TTM'] >= .15]['coid']) set_4 = set(df[df['Liabilities_Ratio_Q'] < df['avg_debt_ratio']]['coid']) set_5 = set(df[(df['PER_TWSE'] <= df['EPS_CAGR_5Y']*100 * 2.0)]['coid']) set_6 = set(df[(df['RS_ratio_6m'] > 1)]['coid']) tickers = list(set_1 & set_2 & set_3 & set_4 & set_6 & set_5) print(f'set1:{len(set_1)}, set2:{len(set_2)},set3:{len(set_3)},set4:{len(set_4)},set5:{len(set_5)},set6:{len(set_6)}') sets = [len(set_1), len(set_2),len(set_3), len(set_4),len(set_5), len(set_6)] return tickers, sets

回測程式碼展示

pools = pool + ['IR0001'] start_ingest = start_date.replace('-', '') end_ingest = end_date.replace('-', '') print(f'開始匯入回測資料') simple_ingest(name = 'tquant' , tickers = pools , start_date = start_ingest , end_date = end_ingest) print(f'結束匯入回測資料') def initialize(context, re = 30): set_slippage(slippage.VolumeShareSlippage(volume_limit=1, price_impact=0.01)) set_commission(commission.Custom_TW_Commission()) set_benchmark(symbol('IR0001')) context.i = 0 context.state = False context.order_tickers = [] context.last_tickers = [] context.rebalance = re context.set1 = 0 context.set2 = 0 context.set3 = 0 context.set4 = 0 context.set5 = 0 context.set = 0 context.dic = {} def handle_data_1(context, data): # 避免前視偏誤,在篩選股票下一交易日下單 if context.state == True: for i in context.last_tickers: if i not in context.order_tickers: order_target_percent(symbol(i), 0) for i in context.order_tickers: order_target_percent(symbol(i), 1.0 / len(context.order_tickers)) context.dic[i] = data.current(symbol(i), 'price') record(p = context.dic) context.dic = {} print(f"下單日期:{data.current_dt.date()}, 擇股股票數量:{len(context.order_tickers)}, Leverage: {context.account.leverage}") context.last_tickers = context.order_tickers.copy() context.state = False backtest_date = data.current_dt.date() if context.i % context.rebalance == 0: context.state = True context.order_tickers = compute_stock(date = backtest_date, data = data_use)[0] context.set = compute_stock(date = backtest_date, data = data_use)[1] record(tickers = context.order_tickers) record(Leverage = context.account.leverage) if context.account.leverage > 1.2: print(f'{data.current_dt.date()}: Over Leverage, Leverage: {context.account.leverage}') for i in context.order_tickers: order_target_percent(symbol(i), 1 / len(context.order_tickers)) context.i += 1 lengths = [s for s in context.set] record( ticker_num = len(context.order_tickers), set1_len = lengths[0], set2_len = lengths[1], set3_len = lengths[2], set4_len = lengths[3], set5_len = lengths[4], set6_len = lengths[5] ) def analyze(context, perf): plt.style.use('ggplot') # 第一張圖:策略績效與報酬 fig1, axes1 = plt.subplots(nrows=2, ncols=1, figsize=(18, 10), sharex=False) axes1[0].plot(perf.index, perf['algorithm_period_return'], label='Strategy') axes1[0].plot(perf.index, perf['benchmark_period_return'], label='Benchmark') axes1[0].bar(perf.index, perf['algorithm_period_return'] - perf['benchmark_period_return'], label='Excess return', color='g', alpha=0.4) axes1[0].set_title("Backtest Results") axes1[0].legend() axes1[1].plot(perf.index, perf['returns'], label='Strategy') axes1[1].plot(perf.index, perf['benchmark_return'], label='Benchmark') axes1[1].set_title("Daily Returns") axes1[1].legend() plt.tight_layout() plt.show() # 第二張圖:選股結構與篩選條件 fig2, axes2 = plt.subplots(nrows=2, ncols=1, figsize=(18, 10), sharex=False) axes2[0].plot(perf.index, perf['ticker_num'], label='Ticker Number') axes2[0].set_title("Ticker Number") axes2[0].legend() axes2[1].plot(perf.index, perf['set1_len'], label='Set1: mktcap >= ave') axes2[1].plot(perf.index, perf['set2_len'], label='Set2: EPS CAGR >= 10%') axes2[1].plot(perf.index, perf['set3_len'], label='Set3: ROE >= 15%') axes2[1].plot(perf.index, perf['set4_len'], label='Set4: debt ratio < ave') axes2[1].plot(perf.index, perf['set5_len'], label='Set5: PER <= CAGR x2') axes2[1].plot(perf.index, perf['set6_len'], label='Set6: RS ratio > 1') axes2[1].set_title("Six Conditions Filtered Count") axes2[1].legend(loc='upper right') plt.tight_layout() plt.show() results = run_algorithm( start = pd.Timestamp('2016-01-01', tz = 'utc'), end = pd.Timestamp(end_date, tz = 'utc'), initialize = initialize, handle_data = handle_data_1, analyze = analyze, bundle = 'tquant', capital_base = 1e5)

績效圖表&分析

從上圖的第一張圖「累積報酬率圖表中」,我們可以看得出來策略在表現上是贏過大盤的,策略累積報酬率為 457.54% 相比於大盤只有 241.5%,展現出了良好的獲利性。我們可以從第一張圖中看出,每當大盤處於上升格局,策略報酬率容易表現得更好,也就是創造報酬綠差距的期間。但是當大盤在進行盤整或進入下跌期時,策略往往產生更大的跌幅,同時對比第二張圖「日報酬率的圖表中」,可以得出策略的波動率大部分期間是大於大盤的。這也可以從策略的 Beta 值得出,策略的 Beta 值為 1.27 但是 alpha 值僅僅只有 0.03,顯示出策略的良好獲利性其實來源於放大市場風險賺取的市場溢酬。顯示出策略的實用性應該根據市場狀態而有所調整,而不是一個可以在全期間使用的策略邏輯。

從上述這張圖來看,第一張圖顯示了策略在不同時期持有的股票個數,第二張圖顯示 了不同時期通過篩選標準的股票個數,也就是說若一隻股票同時滿足這六個篩選條件才會被選入被策略邏輯買進持有。可以看得出來,主導選股的條件集中在 set 6,也就是「近六個月股價相對強弱度(6M RS)>1」這個條件,而這個條件就是這個策略之所以被描述為動能的理由,我認為此條件也是使得策略展現高市場風險的主要原因。

另一方面,從持股數量來看,策略期間持股的數量至多不超過20支股票,這可能導致投資組合持股數量不足難以分散個股分險,造成整體績效容易受到單一個股影響,而承受較高的獨有風險。

從回測結果來看,策略在 2018、2022 與 2025 年皆經歷明顯回撤,表示市場處於下跌行情時策略會放大市場的跌幅。其中,2022 年報酬為負且最大回撤接近 -35%,為典型的系統性下跌;2025 年雖未結束年度,但已出現超過 -40% 的深度回撤,顯示市場波動劇烈,仍處於修正階段。而 2018 年則為中度回檔,跌幅約 -20%,屬於邊緣型熊市或大型修正。整體而言,策略雖表現亮眼,但在面對極端市場狀況時仍存在顯著回撤風險。

項目 德伍.卻斯投資策略 大盤 IR0001 累積報酬率 457.54% 241.54% 年化報酬率 20.84% 14.47% 年化波動度 27.79% 16.22% 夏普比率 0.82 0.92 卡瑪比率 0.49 0.53 最大回撤 - 42.61% - 27.37% Beta 1.27 0.99 Alpha 0.03 0 註:卡瑪比率計算方式為年化波動度除以期間最大回撤,用以衡量「報酬率對虧損」的比值,概念類似於風暴比,此指標的數值越高越好。

完整程式碼連結

歡迎投資朋友參考,之後也會持續介紹使用 TEJ 資料庫來建構各式指標,並回測指標績效,所以歡迎對各種交易回測有興趣的讀者,選購 TQuant Lab 的相關方案,用高品質的資料庫,建構出適合自己的交易策略。
溫馨提醒,本次分析僅供參考,不代表任何商品或投資上的建議。

【TQuant Lab 回測系統】解決你的量化金融痛點

全方位提供交易回測所需工具

點我註冊會員,開始試用

延伸閱讀

從景氣燈號到資產輪動:一套避開熊市的量化策略

麥克.墨菲高科技股投資風險評估法則

查爾士.布蘭帝 價值型選股法則:打造安全邊際的投資組合

相關連結

查看原始文章

更多理財相關文章

01

ATM新制今上路!8大銀行「宣布新規」 有錢也領不出來

TVBS
02

特斯拉AI晶片選三星非台積?分析師曝關鍵!

NOWnews 今日新聞
03

股市觀察》兩周內決戰!台股要崩了?分析師揭「假利空真洗盤」真相

新頭殼
04

海角36億元拿不回外傳要賣街口電支股票 泰山反應曝光

鏡週刊
05

AI股太貴、非龍頭股不敢買?億級投資人:現在這種產品是好選擇

商周.com
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
查看更多

留言 0

沒有留言。

最新消息

台股「嚇」跌!千金股穎崴短時間填息機率高 股價還在低位階

理財周刊

中華電信全數完成Google憑證換發 拚2026年3月前重返信任名單

anue鉅亨網

AWS 高峰會登台!廣達與世芯-KY強勢卡位 AWS 超級訂單

理財周刊

外資中止連10買 賣超164億元 三大法人聯手加碼欣興1.55萬張

anue鉅亨網

台積電蠟燭兩頭燒 利空接連襲來 能HOLD得住嗎?

理財周刊

科技新貴不當盤子?上半年交易冷基隆、新竹最慘 外擴買氣令苗栗逆勢突圍

信傳媒

緯創打出蘋果鏈以外的第二回合!砸380億回印度蓋廠,聚焦「高毛利」伺服器、車載電子業務

數位時代

臻鼎-KY暴漲8.97%!市場押寶ABF報價上調受惠股

理財周刊

〈致伸法說〉車用AIOT年增高雙位數 新竹廠9月投產攻美國市場

anue鉅亨網

晶片關稅來襲 產業署:台灣直接輸美僅4%

NOWnews 今日新聞

盤中速報 - Bonk大跌8.19%,報0美元

anue鉅亨網

《今日總經速讀》美歐貿易協議達成!帶動殖利率反彈,美債市場氣氛轉趨樂觀

優分析

營收觸底反彈?中台(6923)押注雙廢市場商機,再生設備回收布局成轉型催化劑

優分析

經濟部AI新秀計畫聯合開訓記者會(3)(圖)

中央通訊社

經濟部AI新秀計畫聯合開訓記者會(2)(圖)

中央通訊社

經濟部AI新秀計畫聯合開訓記者會(1)(圖)

中央通訊社

〈致伸法說〉Q2毛利率創新高、匯率避險奏效 EPS 1.6元與Q1持平

anue鉅亨網

鉅亨買幣速報 - 以太幣(ETH)24小時成交量超過22.6億美元,LTO Network(LTO)24小時漲幅達54.8%

anue鉅亨網

經濟部第2波AI新秀計畫啟動(圖)

中央通訊社

經濟部AI新秀計畫開訓 無人機課程實作展示(圖)

中央通訊社

輝達H20晶片重啟對中國出口 傳向台積電追加代工訂單

商傳媒

開放申設數位保險公司 金管會曝5團隊探詢

NOWnews 今日新聞

矽谷最神秘公司 Palantir 現身台灣,自揭市場差異化的關鍵優勢

TechOrange 科技報橘

〈國巨法說〉Q2獲利年減8% 每股純益9.74元 上半年仍賺逾2股本

anue鉅亨網

關稅對景氣影響 世界先進董座:已經鈍化

NOWnews 今日新聞

盤後速報 - 大台北(9908)次交易(30)日除息1.2元,參考價30.05元

anue鉅亨網

盤後速報 - 大汽電(8931)次交易(30)日除息2元,參考價50.8元

anue鉅亨網

盤後速報 - 上洋(6728)次交易(30)日除息8元,參考價153.0元

anue鉅亨網

盤後速報 - 伊雲谷(6689)次交易(30)日除息2元,參考價79.1元

anue鉅亨網

盤後速報 - 動力-KY(6591)次交易(30)日除息2.35元,參考價64.75元

anue鉅亨網

盤後速報 - 瑞祺電通(6416)次交易(30)日除息2.9元,參考價84.4元

anue鉅亨網

盤後速報 - 大城地產(6171)次交易(30)日除息2.5元,參考價33.1元

anue鉅亨網

盤後速報 - 久威(6114)次交易(30)日除息0.5元,參考價35.75元

anue鉅亨網

盤後速報 - 中租-KY甲特(5871A)次交易(30)日除息3.8元,參考價98.2元

anue鉅亨網

盤後速報 - 中租-KY(5871)次交易(30)日除權息6.3元,參考價118.04元

anue鉅亨網

盤後速報 - 遠雄港(5607)次交易(30)日除權息1.3元,參考價43.24元

anue鉅亨網

盤後速報 - 宣德(5457)次交易(30)日除息1.3元,參考價42.0元

anue鉅亨網

盤後速報 - 國眾(5410)次交易(30)日除息1.5元,參考價27.25元

anue鉅亨網

盤後速報 - 強信-KY(4560)次交易(30)日除息1.2元,參考價34.0元

anue鉅亨網

盤後速報 - 冠星-KY(4439)次交易(30)日除息2元,參考價90.0元

anue鉅亨網