00919 高股息 ETF 復刻與優化:玩轉精選高息策略
February 16, 2026
封面圖片
前言
市面上的高股息 ETF 百花齊放,這次要來復刻 00919 **群益台灣精選高息 ETF,**如果 0056 是台股存股族的啟蒙,00919 就像下一代改裝版。它主打「精準高息、精準卡位、精準領息」三大賣點,看似簡單,其實暗藏許多量化細節。本文帶你:
- 完整拆解官方邏輯,一步步復刻 00919 的選股流程。
- 用量化工具驗證:驗證相關性和重疊率。
- 再往前優化:刪掉雜訊因子、加入高效指標,打造報酬更高、回撤更低的 00919 優化版。
一、00919 三大「精準」拆解
查看基金介紹發現有幾個特色
- 精準高息:用「實際宣告」取代「預估數字」
00919 選擇鎖定「已公告現金股利」的企業,從確定的金額計算真實殖利率,進一步提升股息來源的可靠度與穩定性。
- 精準卡位:提前布局的策略優勢
00919 採雙階段審核機制,五月、十二月雙審核,透過這種快與早並重的選股機制,達到真正「買在除息前」與「走在市場前」的投資節奏。
- 精準領息:每一分股息都不浪費
選股時機對應企業除息時程,投資人持有期間能真正參與除息、獲取現金配息,讓每一分錢都落袋為安。
名詞解釋 – 殖利率:每股現金股利 ÷ 股價。殖利率越高,代表用相對便宜的股價就能拿到較高現金回報。
二、復刻 00919:研究流程
- 資料擷取
- 00919 公開說明書
- 邏輯拆解
- 採樣母體 → 流動性 / 財務健全性 → 股利資訊 → 排序 → 替換規則。
三、復刻 00919 的關鍵步驟
-
初始採樣母體:
-
臺灣上市與上櫃普通股股票為基礎。
-
選取發行市值前 300 大股票。
-
-
基本條件篩選:
- 日平均成交金額需高於 8,000 萬元。
- 近四季稅後股東權益報酬率 (ROE) 皆為正數。
-
股利資訊篩選(五月定審限定):
- 排除董事會尚未決定股利金額的公司。
- 排除已除息且於審核生效日前已完成發放的股票。
-
排序並選取成分股:
- 五月定審:依近四季股利率排序,選出前 30 檔。
- 十二月定審:依據預估股利率排序,選出前 30 檔。
顯示程式碼
def calculate_ranks(date, is_may_review=True):
"""計算特定審核日期的股票排名"""
nearest_date = get_nearest_past_trading_date(date, all_trading_dates)
if nearest_date is None:
return pd.Series(dtype=float) # 空序列
if is_may_review: # 5月定審
# 計算排名 - 5月定審使用股利率
score = (市值.rank(axis=1, pct=True) + 股利率.rank(axis=1, pct=True) + yield_ratio.rank(axis=1, pct=True))[conds & (board_cash_dividend > 0)]
else: # 12月定審
# 計算排名 - 12月定審使用預估股利率
score = (市值.rank(axis=1, pct=True) + 預估股利率.rank(axis=1, pct=True) + yield_ratio.rank(axis=1, pct=True))[conds]
if nearest_date not in score.index:
return pd.Series(dtype=float) # 空序列
return score.loc[nearest_date].dropna().rank(ascending=False, method='min')- 成分股替換規則:
- 排名前 15 名直接納入成分股。
- 既有成分股若跌出 46 名以外則剔除。
- 排名 16 至 45 名的股票列為候補,優先保留既有成分股。
- 十二月定審單次最多更替 8 檔股票。
顯示程式碼
# 建立空的結果DataFrame
position = pd.DataFrame(False, index=effective_dates, columns=close.columns)
# 處理所有調倉日期和排名數據
all_ranks = {}
valid_dates = []
for date in effective_dates:
try:
is_may_review = (date.month == 5)
ranks = calculate_ranks(date, is_may_review)
if not ranks.empty:
all_ranks[date] = ranks
valid_dates.append(date)
except Exception as e:
print(f"處理 {date} 時發生錯誤: {e}")
valid_dates = sorted(valid_dates) # 確保日期順序
if not valid_dates:
print("沒有有效的調倉日期,無法進行模擬")
else:
# 使用 Pandas 向量化處理成分股替換
prev_components = None
target_component_count = 30 # 設定目標成分股數量
for i, date in enumerate(valid_dates):
ranks = all_ranks[date]
# 1. 排名在第15名以內者納入成分股
top_15 = set(ranks[ranks <= 15].index)
# 2. 排名16至45名為候補名單
candidates = set(ranks[(ranks > 15) & (ranks <= 45)].index)
# 暫定的成分股 (先加入前15名)
current_components_tentative = set(top_15)
# 如果不是第一次調倉
if prev_components is not None:
# 加入排名16-45之間的既有成分股
existing_in_candidates = prev_components.intersection(candidates)
current_components_tentative.update(existing_in_candidates)
# 對於12月定審,額外限制最多替換8檔
if date.month == 12:
# 計算基於 Top15 + 既有候補 所得的新增股票
added = current_components_tentative - prev_components
# 如果新增超過8檔,需要減少替換數量
if len(added) > 8:
# 取出新增的股票並按排名排序 (rank越小越好)
added_with_rank = pd.Series({stock: ranks.get(stock, float('inf')) for stock in added})
# 只保留排名最好的前8名新增的股票
to_keep = set(added_with_rank.sort_values().index[:8])
to_remove_due_to_limit = added - to_keep
# 從暫定名單中移除因超過8檔限制而被剔除的股票
current_components_tentative = current_components_tentative - to_remove_due_to_limit
# --- 補滿至目標數量 ---
num_needed = target_component_count - len(current_components_tentative)
if num_needed > 0:
# 找出所有排名16-45,但尚未被選入的股票
remaining_candidates = candidates - current_components_tentative
if remaining_candidates: # 確保還有候選股可補
# 依排名排序這些候選股
remaining_candidates_with_rank = pd.Series({stock: ranks.get(stock, float('inf')) for stock in remaining_candidates})
sorted_remaining_candidates = remaining_candidates_with_rank.sort_values().index
# 選取排名最好的 num_needed 檔來補滿
stocks_to_add = set(sorted_remaining_candidates[:num_needed])
current_components_tentative.update(stocks_to_add)
# --- 補滿邏輯結束 ---
# 最終確認的成分股
current_components = current_components_tentative
# 設定成分股
# 確保只設定存在的欄位
valid_cols = [col for col in current_components if col in position.columns]
position.loc[date, valid_cols] = True
# 更新前一期成分股
prev_components = current_components
# 5. 前向填充空值,確保非調倉日也有持股
position = position.loc[valid_dates]- 生效日期計算:
- 審核基準日後第 5 個交易日正式生效。
顯示程式碼
def calculate_review_dates(trading_dates: pd.DatetimeIndex):
"""
計算台灣高股息指數的審核與調倉日期
台灣高股息指數規則:
- 5月: 第17個交易日為審核基準日,審核資料截至5月第10個交易日
- 12月: 第7個交易日為審核基準日,審核資料截至11月最後交易日
- 生效日: 審核基準日後第5個交易日
"""
review_dates_info = []
start_year = trading_dates[0].year
end_year = trading_dates[-1].year
for year in range(start_year, end_year + 1):
# 5月定審
may_review_basis_day = get_trading_day_of_month(year, 5, 17, trading_dates)
may_data_cutoff_day = get_trading_day_of_month(year, 5, 10, trading_dates)
if may_review_basis_day and may_data_cutoff_day:
target_effective_day_index = trading_dates.searchsorted(may_review_basis_day) + 5
if target_effective_day_index < len(trading_dates):
may_effective_day_target = trading_dates[target_effective_day_index]
may_effective_day = get_nearest_future_trading_date(may_effective_day_target, trading_dates)
if may_effective_day:
review_dates_info.append({
'year': year,
'month': 5,
'cutoff_date': may_data_cutoff_day,
'basis_date': may_review_basis_day,
'effective_date': may_effective_day
})
# 12月定審
dec_review_basis_day = get_trading_day_of_month(year, 12, 7, trading_dates)
nov_data_cutoff_day = get_last_trading_day_of_month(year, 11, trading_dates)
if dec_review_basis_day and nov_data_cutoff_day:
target_effective_day_index = trading_dates.searchsorted(dec_review_basis_day) + 5
if target_effective_day_index < len(trading_dates):
dec_effective_day_target = trading_dates[target_effective_day_index]
dec_effective_day = get_nearest_future_trading_date(dec_effective_day_target, trading_dates)
if dec_effective_day:
review_dates_info.append({
'year': year,
'month': 12,
'cutoff_date': nov_data_cutoff_day,
'basis_date': dec_review_basis_day,
'effective_date': dec_effective_day
})
# 按生效日期排序
review_dates_info = sorted(review_dates_info, key=lambda x: x['effective_date'])
# 移除無效日期
review_dates_info = [info for info in review_dates_info if info['effective_date'] is not None]
if not review_dates_info:
raise ValueError("無法計算出任何有效的審核與生效日期,請檢查日期計算邏輯或資料範圍。")
return review_dates_info四、復刻結果
在經過一連串比對後,復刻而得的策略股池與原版 00919 報酬率曲線有高度相關。這意味著,我們的 復刻版 00919 確實能有效重現 00919 的選股結果。
相關性分析:
相關性分析
復刻 00919 報酬 :
復刻 00919 報酬
長期持有 00919 報酬 :
長期持有 00919 報酬
我們的「復刻版 00919」與官方版本高相關,證明邏輯拆解合理。