建構出自己的 Smart ETF 00905 2.0 ! Part2 – 12 個獲利因子程式碼懶人包大公開

  • Post author:
  • Reading time:10 mins read

前情提要

上一篇文章中我們解構了Smart ETF 00905的公開說明書,從一百多頁中提取出特選Smart多因子指數的編纂方式,並與加權指數做對比,發現兩種指數走勢幾乎完全貼合。

Smart ETF追蹤指數與加權指數報酬率比較圖
特選Smart多因子與加權指數報酬比較圖

簡介

這次會使用finlab套件模擬出所有的Smart多因子,並考量發行機構與個體投資人的差別進行微調後,重新進行回測,目的是驗證特選Smart多因子是否有其意義,能否獲得超越大盤的報酬率。

ETF 00905 Smart多因子

00905公開說明書(24頁~28頁),詳細說明了00905 ETF挑選成分股的步驟如下:

  1. 流動性檢驗
  2. 指標篩選
  3. 排序方式
  4. 權重計算

接下來會一個個步驟講解計算的方法,那我們就開始吧!

流動性檢驗

首先由於ETF進出的金額遠大於一般投資人,因此要特別考慮流動性的問題,避免買賣的時候遇到嚴重的滑價。

00905採用以下的平均建倉天數公式,以AUM(資產管理規模)為60億元台幣估算,選取發行市值大於等於 25 分位數且平均建倉天數小於等於10天之股票。

image 6
平均建倉天數公式

指標篩選

指標篩選一共分成:價值因子指標、品質因子指標、動能因子指標、多因子分數。其中多因子分數由前三個指標分別計算完成後合成。由於前三個指標計算方式大同小異,以講解第一個價值因子作為示範。

價值因子指標

價值因子由以下四個單因子構成:

  • 營業現金流量對股價比 = 每股營業現金流量 / 收盤價
  • EBITDA 對企業價值比 = EBITDA /企業價值
  • 益本比 = 每股盈餘 / 收盤價
  • 股東收益率 = 股息殖利率 + 庫藏股回購率

計算完單因子之後,再根據股票的類別,以不同的方式即可算出價值因子:

  • 金融股 = 0.4*Z(營業現金流量對股價比)+0.4*Z(益本比)+0.2*Z(股東收益率)
  • 非金融股 = 0.3*Z(營業現金流量對股價比) + 0.3*Z(EBITDA 對企業價值比) + 0.3*Z(益本比) + 0.1*Z(股東收益率)

註:𝑍(𝑥) = (𝑥 − 𝜇𝑥) / 𝜎𝑥 ∈ [−3, 3] 為常態標準化函數

如法炮製,透過各種財報指標、收盤價、市值計算出所有單因子指標後,經過常態標準化函數,再根據是否為金融股採取不同的單因子加權方式,就可以得到品質因子和動能因子指標了!

多因子分數

假設我們已經有品質、價值、動能三種因子,接下來按照公式給予三種因子不同的權重即可算出多因子分數。

多因子分數 = 0.4*品質因子分數 + 0.3*價值因子分數 + 0.3*動能因子分數

多因子權重係數

因為多因子分數有正有負,無法作為持有股票的權重,因此要再經過以下公式轉換成多因子權重係數。

  • 1 + 𝑍 , 𝑍 ≥ 0
  • (1 − 𝑍) ^ −1 , 𝑍 < 0

註:Z為股票之多因子分數

排序方式

接著使用市值和多因子權重做排序,選出名列前茅的股票納入00905持有的清單。

候選名單

首先將通過流動性檢驗的股票中發行市值大於等於 25 分位數者作為候選名單,代表所有可能被納入ETF的股票。

成分股集合一

在母體之發行市值權重 ≥1%,無條件納入,稱作成分股集合一。

成分股集合二

候選名單中不屬於成分股集合一的股票中,多因子分數大於等於 75 分位數者納入,稱作成分股集合二。

權重計算

成分股集合一加上成分股集合二,就是00905納入的所有股票,也就是我們選股策略的選股清單!有了選股清單的最後一步,就是計算每支股票各自的權重,依據成分股集合一、二有不同的加權計算方法。

  • 集合一股票i之權重 = max{0, w𝑏𝑒𝑛𝑐ℎ𝑚𝑎𝑟𝑘,𝑖 ∗100 + 0.67 ∗ 𝑍𝑚𝑢𝑙𝑡,𝑖} %
  • 集合二股票 j 之權重 = (𝜔𝑟𝑒𝑚𝑎𝑖𝑛 ∗𝑍𝑚𝑢𝑙𝑡,𝑗 / ∑𝑗 𝑍𝑚𝑢𝑙𝑡,𝑗) %

簡單來說,成分股集合一內每支股票的權重為佔母體市值權重*100+多因子權重係數*0.67,而成分股集合一分完剩下來的權重,才按照多因子權重分配給成分股集合二的股票。

註:00905另有設持股上限,公式的細節就不贅述了,更多的細節可以到公開說明書查閱。

程式實作

實作微調部分

此次撰寫選股程式的目的是驗證Smart多因子的效度,並不受到流動性的限制,因此將略過所有和流動性相關之步驟,更貼近實驗目的的同時充分發揮身為一般投資人的優勢!

定義訓練集、測試集

在特選Smart多因子指數發布之前,投信發行商都有機會藉由overfitting優化歷史報酬率,因此本次驗證以2020/8/10作為分界點,以前為訓練集(training set)、以後則為測試集(testing set)。

引入套件、定義需要的函數

因為之後優化時會套用到不同的市場,因此先記錄不同市場的columns方便往後使用。同時定義常態標準函數、差集等等往後會用到的函式。

import finlab
from finlab import backtest
from finlab import data
import scipy.stats as stats
import numpy as np


with data.universe(market='TSE_OTC',category='金融'): # 上市+上櫃
    financialColumns =data.get('price:收盤價').columns
with data.universe(market='TSE_OTC'):
    allColumns =data.get('price:收盤價').columns

with data.universe(market='TSE',category='金融'): # 上市 (ETF母體)
    financialTseColumns =data.get('price:收盤價').columns
with data.universe(market='TSE'):
    allTseColumns =data.get('price:收盤價').columns

# 上櫃
with data.universe(market='OTC',category='金融'):
    financialOtcColumns =data.get('price:收盤價').columns
with data.universe(market='OTC'):
    allOtcColumns =data.get('price:收盤價').columns

data.set_universe(market='TSE_OTC') #設定母體為TSE+OTC,為下一章的策略優化作準備

def Z(df):
    df= df.apply(stats.zscore,axis=1,nan_policy='omit') # zscore, 忽略nan
    cond = (df>3) | (df<-3) # 只留下3個std以內的資料,離群值不採納
    df[cond] = np.nan
    return df

def diffList(a,b): #在A裡面不在B裡面的元素
    return [i for i in a if i not in b]

計算價值因子

  • 分別計算各個單因子
  • 通過常態標準化函數
  • 根據股票類別採取不同的單因子加權方式
# 營業現金流量對股價比
收盤價 = data.get("price:收盤價")
營業現金流量 = data.get('financial_statement:營業活動之淨現金流入_流出')
股本 = data.get('financial_statement:股本')
每股營業現金流量 = 營業現金流量 / (股本/10)
營業現金流量對股價比 = 每股營業現金流量 / 收盤價

# EBITDA對企業價值比
稅前息前折舊前淨利率 = data.get('fundamental_features:稅前息前折舊前淨利率')
營業收入淨額 = data.get("financial_statement:營業收入淨額")
EBITDA = 稅前息前折舊前淨利率*營業收入淨額/100
負債總額 = data.get('financial_statement:負債總額') 
現金及約當現金 = data.get('financial_statement:期末現金及約當現金餘額')
市值 = 股本/10*收盤價
企業價值 = 市值 + 負債總額 - 現金及約當現金
EBITDA對企業價值比 = EBITDA /企業價值

# 益本比
每股盈餘 = data.get('financial_statement:每股盈餘')
益本比 = 每股盈餘 / 收盤價

# 股東收益率
股息殖利率 = data.get('price_earning_ratio:殖利率(%)')
發行股數 = data.get('internal_equity_changes:發行股數') #改股本
前一年發行股數 = data.get('internal_equity_changes:發行股數').shift(12)  # 一年12個月
庫藏股回購率 = (發行股數 - 前一年發行股數) * (收盤價 / 市值)
股東收益率 = 股息殖利率 + 庫藏股回購率


# 計算價值因子分數

價值因子分數 = 0.4 * Z(營業現金流量對股價比) + 0.4* Z(益本比) + 0.2 * Z(股東收益率) #用非金融股算法計算全部
價值因子分數_金融 = 0.3 * Z(營業現金流量對股價比) +0.3 * Z(EBITDA對企業價值比) + 0.3 * Z(益本比) + 0.1 * Z(股東收益率) #用金融股算法計算全部
finCol = 價值因子分數_金融.columns.intersection(financialColumns) # 取交集過濾掉已經下市的股票
價值因子分數[finCol] = 價值因子分數_金融[finCol] # 再用金融股的值代入金融股的值
價值因子分數 # 所有TSE & OTC 的股票價值因子分數

計算多因子權重

  • 給予三種因子不同的權重算出多因子分數
  • 透過公式轉換成多因子權重
多因子分數 = 0.4 * 品質因子分數 + 0.3 * 價值因子分數 + 0.3 * 動能因子分數
多因子權重係數 = 多因子分數.applymap(lambda z: 1+z if(z>=0) else (1-z)**-1)

回測

  • 選取多因子權重係數大於等於75分位數者為成分股
  • resample頻率設定為每日
  • 不考慮交易稅和手續費
# 選取多因子權重係數大於等於75分位數者為成分股
col = 多因子權重係數.columns.intersection(allTseColumns)
多因子權重係數_TSE = 多因子權重係數[col]
cond = 多因子權重係數_TSE < 多因子權重係數_TSE.quantile_row(0.75) #計算多因子權重係數小於75分位數的股票
多因子權重係數_TSE[cond] = 0 # 設權重為0
position = 多因子權重係數_TSE.loc["2020-08-10":] #指數從此時編纂
report = backtest.sim(position, resample=None,fee_ratio=0,tax_ratio=0)

Smart多因子回測結果

訓練集(training set)表現

image 5
Smart多因子策略回測結果(訓練集)

大部分時間都勝過大盤,但在下跌的時候比大盤還要兇,品質因子並沒有發揮所宣稱的強健抗震效果,總結起來是個尚可的選股策略。

測試集(testing set)表現

image 4
Smart多因子策略回測結果(測試集)

測試集的結果令人驚豔!報酬率一路遙遙領先,MDD相對大盤來說也小上許多。即便在最近的熊市,最終的報酬率高達加權指數的兩倍之多,年化報酬高達24%!

單因子回測結果

特選Smart多因子在不同資料集中的表現時好時壞,沒有一致的績效表現通常代表組成的單因子好壞參半。

因此我們將特選Smart多因子進一步拆分成一個個單因子,來檢視那些單因子能產生超額報酬,哪些則只是拿來濫竽充數。

回測將使用訓練集資料,並分為報酬率和相關性。報酬率回測將使用單項因子取代原先的Smart多因子,其餘完全相同;相關性回測則會用單因子排序,將所有股票分為五份(每20%一個組別),依序計算其報酬率後做線性回歸,目的是觀察單因子與股票報酬率之間是否有明顯的正負相關性。

價值、品質、動能因子回測

首先回測三大主要因子,分別驗證各因子的報酬率與相關性表現。

報酬率回測

image

三大因子的報酬率相差不大,動能、品質、價值因子在測試集的總報酬分別為46%、43%、39%。

相關性回測

將股票按照單因子排序後切分成五等分,並回測每個部分的報酬率做成熱力圖,最後用線性回歸後的斜率作為判斷正負相關強度的依據。

註:詳細算法可以在PEG本益成長比中複習!

image 7

由熱力圖可以輕易地觀察到,使用品質、動能因子進行分類時,整體報酬分布皆明顯呈線性分布,從排名從低到高的報酬率變化具有相當的一致性,且動能因子的前20%的股票可以獲得最高的總報酬。

用線性回歸來看,也能獲得相似的結論,動能因子的前20%擁有最高的總報酬,線性程度也最高。品質因子和動能因子的總報酬,皆呈現全程遞增,相較之下價值因子就稍顯遜色了。

細項單因子回測

大致了解三大因子的效度之後,將進一步作更深入的解析,這次將目標轉移到了三大因子裡面的每一項單因子。

報酬率回測

image 11

整體來說報酬率呈現平滑的下降趨勢,彼此間的數值差異並不大,名列前三的單因子分別是益本比、股價動能、營利動能。令人驚奇的是,第一名竟然來自先前表現較差的價值因子類別,原因來自價值因子的好壞參差不齊。4種價值因子,分別位居報酬率的第1、4、8、11名。這也凸顯了逐一檢查因子的重要性,若是我們沒做細項的單因子檢驗,很有可能錯過益本比這樣優秀的feature!

相關性回測

緊接著對各細項單因子做熱力圖和線性回歸,查看相關性大小。

image 12
image 14

由於細項單因子的數目較多,額外做一張長條圖,用視覺化的方式呈現各因子的相關性。

image 15

可以發現很有趣的事實,第一名的益本比,其實就是我們所熟知的本益比做變形而已,第二、三名則都是動能指標。

動能指標往往都是很不錯的features,這跟finlab過去的許多文章不謀而合。像是創新高有多高?這篇文章就是在探討股價動能,而月營收這樣看!三種月營收選股法則屬於營利動能的範疇,歡迎大家同時搭配服用。

回測分析統整

三大因子中,動能因子的總報酬較高,但整體差異並不大,相關性則是動能、品質因子勝過價值因子。

若進一步比較細項的單因子,彼此間的報酬率差異依舊不算大,只是隨著排名緩慢的下滑;相關性的話,來自價值因子的益本比是其中最強力的單因子,第二三名則都是動能因子。

結論

整體而言,回測結果比預期要來的好!雖說00905本身因為流動性的考量和參考市值的設計導致績效與Smart特選多因子脫鉤,但多因子本身包含了許多優秀的features,也帶給我許多不同的思考方向和啟發。

以本益比為例,過往最常見選股策略就是設定本益比低於某數值時買入,但該策略也許多缺點:本益比會出現負數、不同本益比之間的持股權重難以定義等等,因此單純的本益比策略效果並不好。

00905則提出了更好的方法,首先將本益比改成益本比,並透過常規化、權重轉換的方式,同時解決了負數、權重分配的問題。讓我們以不同的角度去理解本益比,在與本益比一樣只使用EPS和收盤價的狀況下,大幅度提高了策略報酬。

形形色色的ETF就像是一個個盲盒,要靠大家自己去實際閱讀、理解、實測,在其中尋找feature的靈感來源,構建出專屬於自己的ETF。

阿榤

我是阿榤,不務正業的電機仔,一個願意接受幸運的人。 很高興認識大家,請多指教!