請更新您的瀏覽器

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

理財

揭開投資大師的選股密碼:麥克.喜偉收益型投資四大準則解析

TEJ 台灣經濟新報

更新於 2025年03月19日10:30 • 發布於 2025年02月21日08:30
收益型投資

Photo from Unflash by Markus Spiske

前言

在投資的世界裡,準確的預測和系統性的分析是取得長期成功的關鍵,而能夠洞悉市場趨勢的策略家,更是投資者們爭相學習的對象。麥克.喜偉(Michael Sivy)無疑是其中的佼佼者。他以精準的市場預測和獨到的投資見解,在美國投資界樹立了卓越的聲譽。

從1987年預警美國股市大崩盤,到1995年預言股市轉機,再到1999年洞察科技股泡沫的破滅,麥克.喜偉以數十年的市場研究和實戰經驗,證明了其卓越的投資洞察力。他的作品《投資金律》更成為眾多投資者必讀的經典,透過系統性的分析和清晰的準則,為不同類型的投資者提供了實用的選股指南。

本篇文章將聚焦於麥克.喜偉的投資理念,特別是他為收益型投資者制定的四大準則:高收益率、穩健成長率、健康財務狀況和合理估值。我們將結合其經典案例,探索這些原則如何在現實投資中實現收益最大化,並為當前的投資環境提供指引。

麥克.喜偉(Michael Sivy)的擇股條件

麥克.喜偉重視公司在各種收益率的表現,因此使用了較多的收益相關資料進行股票的篩選。同時也關心公司的營運方的表現,我們將採用舉債情況以及資產的流動程度來作為首要依據,去評估公司營運方是否有將公司為實在良好的體質當中。

  • 股利收益率加上獲利(營收)成長率大於10%
  • 股利收益率大於市場平均值
  • 本益比小於市場平均值
  • 流動比例大於市場平均值
  • 負債佔股東權益小於20%

策略簡介

通過上述的五個條件進行選股,於每季最後一天交易日更換部位,最後查看麥克.喜偉(Michael Sivy)的擇股策略是否可以應用於台灣股市。

程式範例

基本的模組輸入以及apikey 的設定

import pandas as pd import numpy as np import tejapi import os import matplotlib.pyplot as plt plt.rcParams['font.family'] = 'Arial' tej_key ='your tejapi key' tejapi.ApiConfig.api_key = tej_key os.environ['TEJAPI_BASE'] = "your tej base" os.environ['TEJAPI_KEY'] = tej_key

利用TEJToolAPI 抓取股票的財務以及交易資料

選擇回測時間之前就存在的股票(2019年就存在的股票),避免出現前視偏誤問題,並選擇上市股票作為研究目標

from zipline.sources.TEJ_Api_Data import get_universe pool = get_universe(start = pd.Timestamp('2019-01-01', tz = 'UTC'), end = pd.Timestamp('2019-12-31', tz = "UTC"), mkt_bd_e = 'TSE', stktp_e = 'Common Stock')

關於 get_universe 用法可以參考 TQuant Lab Github 官網:https://github.com/tejtw/TQuant-Lab/blob/main/lecture/get_universe說明.ipynb

利用TEJToolAPI的get_history函數抓取需要的財務會計資料,這裡抓取麥克.喜偉(Michael Sivy)的擇股條件所需要的會計資料。

例如:本益比、流動比率、營收成長率等等。

  • TEJToolAPI 說明內容參考: lecture/TejToolAPI.ipynbtejtw/TQuant-Labimport TejToolAPI columns = ['Industry', '本益比', '收盤價', '流動比率', '股東權益總計', '負債總額', '營收成長率','eps','mt_div','現金股利率'] start_dt = pd.Timestamp('2015-12-29', tz = 'UTC') end_dt = pd.Timestamp('2023-12-31', tz = "UTC") data__ = TejToolAPI.get_history_data(start = start_dt, end = end_dt, ticker = pool, fin_type = 'A', # 為累計資料 columns = columns, transfer_to_chinese = True)

計算出每期換部位的時間(每季的最一天交易日):

通過選取台積電的資料當作樣本,來篩選出每季的最後一天交易日日期,之後會根據這個日期進行新一輪的換股以及選股。

# 找尋每年當中的12月以及6月的最後一天交易日日期 sample = data[data['股票代碼'] == '2330'] last_day = list(sample.groupby(sample['日期'].dt.year)['日期'].max()) june_data = sample[sample['日期'].dt.month == 6] last_june_day = list(june_data.groupby(june_data['日期'].dt.year)['日期'].max()) march_data = sample[sample['日期'].dt.month == 3] last_march_day = list(march_data.groupby(march_data['日期'].dt.year)['日期'].max()) sep_data = sample[sample['日期'].dt.month == 9] last_sep_day = list(sep_data.groupby(sep_data['日期'].dt.year)['日期'].max()) last_day = last_day + last_june_day + last_march_day + last_sep_day modified_day = [] for i in last_day: modified_day.append(i.date()) modified_day 日期輸出格式: [datetime.date(2015, 12, 31), datetime.date(2016, 12, 30), datetime.date(2017, 12, 29), datetime.date(2018, 12, 28), datetime.date(2019, 12, 31), datetime.date(2020, 12, 31), datetime.date(2021, 12, 30), datetime.date(2022, 12, 30), datetime.date(2023, 12, 29), datetime.date(2016, 6, 30), datetime.date(2017, 6, 30), datetime.date(2018, 6, 29), datetime.date(2019, 6, 28), datetime.date(2020, 6, 30), datetime.date(2021, 6, 30), datetime.date(2022, 6, 30), datetime.date(2023, 6, 30), datetime.date(2016, 3, 31), datetime.date(2017, 3, 31), datetime.date(2018, 3, 31), datetime.date(2019, 3, 29), datetime.date(2020, 3, 31), datetime.date(2021, 3, 31),

設定回測架構

先將需要的股票資料匯入浸會側系統當中,這裡除了剛剛獲取的股票池之外,會加入台股大盤(IR0001)資料當作選股策略的比較對象。回測的時間設定為2019/12/30至2024/11/31。

pools = pool + ['IR0001'] from zipline.data.run_ingest import simple_ingest # 價量資料 simple_ingest(name = 'tquant' , tickers = pools , start_date = '20191231' , end_date = '20241131')

匯入回測需要的模組及函數

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

設定benchmark為台灣大盤指數(IR0001)來進行最終策略的比較,同時設定手續費以及滑價模型。

def initialize(context): set_slippage(slippage.TW_Slippage(spread = 1 , volume_limit = 1)) # 設定為台灣股票手續費計算方法 set_commission(commission.Custom_TW_Commission(min_trade_cost=20, discount=1.0, tax = 0.003)) # 設定台灣大盤為比較基準 set_benchmark(symbol('IR0001')) context.i = 0 context.state = False context.order_tickers = [] context.last_tickers = []

交易策略設定為在每一年的最後一天交易日進行上一期股票的賣出,同時對下一期的股票進行買入。買入的部位比例為等權重的比例買進。

def compute_stock(date, data): # 創建一個函數,在指定的日期進行選股,輸出篩選出的股票列表。 # 提取出調整部位當日的股票資訊 df = data[data['日期'] == pd.Timestamp(date)].reset_index(drop = True) # 本益比小於市場平均值 df['產業平均本益比'] = df.groupby('主產業別_中文')['本益比'].transform('mean') set_1 = set(df[df['本益比'] < df['產業平均本益比']]['股票代碼']) # 流動比例大於市場平均值 df['產業平均流動比率'] = df.groupby('主產業別_中文')['流動比率_A'].transform('mean') set_2 = set(df[df['流動比率_A'] > df['產業平均流動比率']]['股票代碼']) # 負債佔股東權益小於20% df['負債佔股東權益'] = df['負債總額_A'] / df['股東權益總計_A'] set_3 = set(df[df['負債佔股東權益'] < 0.2]['股票代碼']) # 現金股利率大於市場平均值 df['產業平均現金股利率'] = df.groupby('主產業別_中文')['現金股利率'].transform('mean') set_4 = set(df[df['現金股利率'] > df['產業平均現金股利率']]['股票代碼']) # 股利收益率加上獲利成長率大於10% set_5 = set(df[df['營收成長率_A']*0.01 + df['現金股利率']*0.01 > 0.1]['股票代碼']) # 因為單位問題進行調整,將%調整為正確單位 tickers = list(set_1 & set_2 & set_3 & set_4 & set_5) return tickers

def handle_data(context, data): # 避免前視偏誤,在篩選股票下一交易日下單 if context.state == True: print(f"下單日期:{data.current_dt.date()}, 擇股股票數量:{len(context.order_tickers)}") 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 / len(context.order_tickers)) curr = data.current(symbol(i), 'price') record(price = curr, days = context.i) context.last_tickers = context.order_tickers context.state = False backtest_date = data.current_dt.date() # 查看回測時間是否符合指定日期 for idx, j in enumerate(modified_day): if backtest_date == j: # 調整狀態,在下一個交易下單 context.state = True context.order_tickers = compute_stock(date = backtest_date, data = data__) context.i += 1

def analyze(context, perf): # plt.style.use('dark_background') # 重置為預設樣式 plt.style.use('default') plt.title(f'Portfolio Value') plt.plot(perf['portfolio_value'], label='Portfolio Value') plt.legend() plt.show() cumulative_returns = (1 + perf['returns']).cumprod() - 1 plt.title(f'Period Return Portfolio & Benchmark') plt.plot(perf.index, cumulative_returns, label = 'Portfolio') plt.plot(perf.index, perf['benchmark_period_return'], label = 'Benchmark') plt.legend() plt.show()

capital_base = 1e7 results = run_algorithm( start = pd.Timestamp('20191231', tz = 'utc'), end = pd.Timestamp('20231130', tz = 'utc'), initialize = initialize, handle_data = handle_data, analyze = analyze, bundle = 'tquant', capital_base = capital_base) results

策略績效圖表分析

收益型投資

回測期間設定為2019年底~2023年底,從累積報酬圖中看出選股策略的績效是略微優於大盤,表示這個策略有挑選出股市中上漲潛力較高的標的。但是在市場下行時,策略的下行幅度更加劇烈,在應對市場的系統性風險上相對缺乏韌性。從槓桿使用圖來看,只有在換股的幾天會出現槓桿的波動,其餘情況是維持在無槓桿的情況,使得策略在週轉資金上不會有過多的問題。策略年化報酬率為18.74%,略高於台灣大盤的年化報酬12-15%。

收益型投資

從移動波動度以及移動夏普值圖表中,可以刊到在2021年的下半年出現比較大的波動性。那段時間正好處於牛市結尾的回檔,台灣股票市場出現明顯的下跌段並且波動性較高,但策略的波動度卻明顯大於市場波動,我認為是這個選股方法是選出市場中上漲潛力較大的股票,這種股票市場處於熊市時,因為投資人的資金入場,因此叫沒有動能推升這類型股價。所以在該時間對中策略的下跌情形以及波動度都會較大,算是這個策略的市場風險。因此如果可以在整體股票市場基期較高時(牛市尾段)減少這個策略的操作,可以避開較大的波動時段。

以不同的換股頻率去進行相同邏輯的策略,我們採用每年、每半年以及每季為單位進行回測。從圖中可以看到換股頻率愈頻繁所帶來的整題績效會於高。經過篩選出的股票會在一個季度的內,股票價格反應公司的價值,因此進行高頻率的換股會對整體績效有顯著的提升。

在平衡天數的績效表格

統計量 3month 6month yearly Benchmark 年化報酬 18.74% 16.058% 17.635% 14.92% 累積報酬 94.499% 78.025% 87.581% 71.362% 夏普值 0.95 0.89 0.94 0.86 年化波動度 20.318% 18.805% 19.311% 18.02% 最大回撤 -29.274% -28.394% -28.394% -28.553%

完整程式碼連結:程式碼連結

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

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

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

點我註冊會員,開始試用

延伸閱讀

程式交易是什麼?程式交易教學、優缺點及常見策略懶人包 - TEJ台灣經濟新報
用 Alphalens 剖析因子表現,價量因子篇

相關連結

查看原始文章

更多理財相關文章

01

她退休11年總花費只有314萬!居無定所走到哪、玩到哪、住到哪,壯遊世界把旅行當生活

幸福熟齡 X 今周刊
02

年薪破300萬!黃仁勳點未來搶手「3職業」成金飯碗:寫程式不是唯一出路

三立新聞網
03

破天荒!台北101「15.1%股權」標售 最低5.7億當「國家門面」房東

太報
04

賣藍莓先看台灣!外媒曝市場的秘密

自由電子報
05

政府打房建商慘1/新舊青安累計撥款2.55兆 兩顆未爆彈全民挫咧等

鏡週刊
06

「陶朱隱園」沒有蓋牌! 17樓見實登「單價飆364萬」

ETtoday新聞雲
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...