
Table of Contents
近年來開始興起一種投資,叫做環境、社會和治理(ESG)投資,指的是在投資決策時,不再只考慮公司的財務表現,而是額外考慮企業對於環境、社會的影響力,以及公司的行為與準則等。彼此之間不再是抵換關係,而是一種良善的循環,提供公司未來前景的保證,進而為投資者帶來豐富的報酬。
因子投資,即試圖找出幾個關鍵的影響因素,像是文獻常見的規模因子、帳市值比因子、風險因子等等,並預期這些因素能帶來超額報酬。因此本週我們以TEJ資料庫提供的「員工流動率」當作因子,看看ESG投資的成果吧!
本文使用 Windows OS 並以 Jupyter Notebook 作為編輯器
#功能與視覺化模組 import pandas as pd import numpy as np import plotly.express as px import plotly.graph_objects as go
#TEJ import tejapi tejapi.ApiConfig.api_key = 'Your Key' tejapi.ApiConfig.ignoretz = True
turnover = tejapi.get('TWN/ACSR01A',
        paginate = True,
        opts = {'columns':['coid','mdate','turn_rate','num_staff']},
        chinese_column_name = True)

turnover = turnover[turnover['員工流動率(%)'].isnull() == False]

result = pd.DataFrame() ret_table = pd.DataFrame()
因為員工流動率於年報揭露,為了避免前視偏誤,因此本文以年報最晚公布日 (隔年三月底)作為投組建構日,並持有一年。但由於按照2020年報資訊所建立的投組,投組持有期間為 2021–03–31 ~ 2022–03–31,尚未持滿一年,因此會排除該年。
date_list = sorted(turnover['年度'].unique())[:-1]

接下來以迴圈進行每年報酬率計算,以下內容以第一年 (date = ‘2008–01–01’)的資料處理情形幫助理解,完整迴圈請參考完整程式碼。
#當期資料選取 data = turnover[turnover['年度'] == date].reset_index(drop=True)
#刪除員工人數過少
data = data[data['員工人數'] >= data['員工人數'].quantile(0.1)]
    
#分群
data['group'] = pd.qcut(data['員工流動率(%)'], q=10,labels = [i for i in range(1,11)])
    
#儲存
result = result.append(data)
首先選取該年資料,並且刪除員工人數過低 (小於十分位數)樣本。接著使用 函數 pd.qcut(),依員工流動率由小到大形成十個組別,並呈現在group欄,最後儲存到 result 裡

#投組賣出日期 
sell_date = date + pd.Timedelta(days = 365 + 90 + 365)
    
#投組報酬
port_ret = [date]
根據2008年資訊建立的十個投組,都將於 sell_date (2010–03–31)賣出。而日期 (2008–01–01)與這些投組報酬,將存放於 port_ret 列表
#計算當年各組的投組報酬
for group in range(1,11):
        
      #當年,某組的資料
      sub_data = data[data['group'] == group].reset_index(drop=True)
    
      #報酬率撈取
      ret = tejapi.get('TWN/APRCD2',
                   coid = sub_data['公司碼'].tolist(),
                   mdate = {'gte':sell_date - pd.Timedelta(days = 5), 'lte':sell_date},
                   opts = {'columns':['coid','mdate', 'roi_y']},
                   paginate = True,
                   chinese_column_name = True)
        
       #只需要最後一筆
       ret = ret.groupby(by='證券代碼').last().reset_index()
    
       #投組報酬(%)
       port_ret.append(ret['年報酬率 %'].mean())
#表格 ret_table = ret_table.append(pd.DataFrame(data = np.array(port_ret).reshape((1,11)), columns = ['日期'] + [i for i in range(1,11)])).reset_index(drop=True)
接著按照組別由小到大進行迴圈。首先先進一步篩選出某組的資料,然後根據這個組別包含的公司、賣出日期撈取年報酬率(%)。這邊採用的技巧是先撈取靠近賣出日的年報酬率資料,接著取最靠近賣出日期的年報酬,此即為過去完整一年內的報酬率。最後再取平均值,即為該組投組的等權報酬率,各組都計算完成後,再將列表 port_ret 形成表格後存入 ret_table

其他年份一樣重複以上步驟,透過迴圈不斷地去更新 result 與 ret_table ,最後得到以下結果。



cum_ret = ret_table[[i for i in range(1,11)]].apply(lambda x : (x*0.01 + 1)).cumprod() cum_ret.insert(0, '日期', date_list)
先計算這十組投組的累積報酬率,再補上日期,最後再畫出


sharpe_list = []
for i in range(1,11):
    
    #年化報酬率
    cagr = (cum_ret[i].values[-1]**(1/len(cum_ret)) - 1)*100
    
    #年化標準差
    std = ret_table[i].std()
    
    #更新list
    sharpe_list.append(i)
    sharpe_list.append((cagr-1)/std)
#形成表格 sharpe = pd.DataFrame(np.array(sharpe_list).reshape((10,2)), columns = ["group","夏普比率(%)"])
先計算出夏普比率,再畫出


從累積報酬圖與夏普值可以發現,員工流動率最高的第十組表現最差,即使其在部分年間有好的報酬表現。但這並不意味著員工流動率低,公司的表現就一定會比較好,例如從累積報酬圖來看,第七組是表現最好的,或許這也代表保持一定的員工流動,反而能維持公司的競爭力與創新意識。
整體而言,在市場表現差時,員工流動率較差的公司損失幅度較大,代表ESG因子某種程度提供一定的下行風險保護。如果讀者對於其他ESG資料有興趣,歡迎到 TEJ E-Shop 選擇最適的方案,找出更多創造超額報酬的因子!