
目录
投资组合的绩效表现受到许多因素影响,我们不容易清楚区分绩效表现是源于大盘上涨、交易员的选股能力,还是资产配置或产业配置得宜? 故我们可以透过 Brinson(1985)提出的绩效归因方法,帮助我们将投资组合与标竿指数间的超额报酬拆解为选择效果、交互效果与配置效果,了解绩效归因来源,作为日后投资决策的参照。
本文将以台湾50指数做为 00881 ETF的标竿指数,透过 Brinson model来分析 00881 ETF的绩效归因。
本文使用 Windows OS 并以 Jupyter Notebook 作为编辑器
# 功能模组import pandas as pdimport numpy as npimport plotly.graph_objects as go#TEJ APIimport tejapitejapi.ApiConfig.api_key = "Your key"
Step 1. 汇入 ETF成份股、台湾50指数成分股
我们从 TEJ资料库汇入2021年9月至11月每日更新的 00881 ETF与台湾50指数成份股,可以节省我们至发行 ETF之投信公司查询的大量时间。若要抓取大量 ETF成份股的资料,使用 TEJ资料库的优势更加明显。
#%% 汇入 TEJ资料etf = tejapi.get('TWN/AEHOLD',coid = '00881',mdate= {'gte': '2021-09-01','lte':'2021-11-30'},opts={'columns':['mdate', 'no','pct']},chinese_column_name=True,paginate=True)# 标竿指数:台50指数benchmark = tejapi.get('TWN/AIDXS',coid = 'TWN50',mdate= {'gte': '2021-09-01','lte':'2021-11-30'},opts={'columns':['mdate','key3','mv_pct']},chinese_column_name=True,paginate=True)etf = etf[~etf['标的名称'].isin(['申赎应付款','保证金','现金'])]etf['证券码'] = etf['标的名称'].str[0:4]etf['证券码'] = np.where(etf['证券码'] == 'TX 台','Y9999',etf['证券码'])etf['年'] = etf['日期'].dt.yearetf['月'] = etf['日期'].dt.monthetf = etf.drop_duplicates(subset=['年','月','证券码'], keep='first')benchmark['证券码'] = benchmark['成份股'].str[0:4]benchmark['年'] = benchmark['年月日'].dt.yearbenchmark['月'] = benchmark['年月日'].dt.monthbenchmark = benchmark.drop_duplicates(subset=['年','月','证券码'], keep='first')etf.head(10)

Step 2. 汇入成份股的产业名称与调整后股价
我们将得到的 00881 ETF与台湾50指数成份股整理成 list资料型态,并从 TEJ资料库捞取所需的成份股调整后股价与所属产业名称。
# 获得 etf与 benchmark的代码coid_list = etf['证券码'].unique().tolist()coid_list.append('Y9999')coid_list = coid_list + benchmark['证券码'].unique().tolist()# 抓取公司的产业名称code = tejapi.get("TWN/EWNPRCSTD",coid = coid_list,paginate=True,opts={'columns':['coid', 'coid_name','tseindnm']},chinese_column_name=True)code.head(5)

透过 TEJ资料库直接获得成份股每月持有报酬率。
# 股价price = tejapi.get('TWN/AAPRCM1',coid = coid_list,mdate= {'gte': '2021-09-01','lte':'2021-11-30'},opts={'columns':['coid', 'mdate','roi']},chinese_column_name=True,paginate=True)price['年'] = price['年月'].dt.yearprice['月'] = price['年月'].dt.monthprice.head(5)

Step 3. 合并产业名称
我们将产业名称合并至 ETF与指数的 Dataframe,若 00881 ETF与 台湾50指数成份股所属产业存在差集,则以其他 替换 ETF与台湾50指数成份股所属产业差集的产业名称,以确保 00881 ETF成份股皆有相对应 台湾50指数成份股的产业名称。
#%% 合并产业名称 ETF# 合并产业名称etf = pd.merge(etf ,code , how = 'left' , on = ['证券码'])etf = pd.merge(etf ,price ,how = 'left' , left_on=['年','月','证券码'], right_on=['年','月','证券代码'])benchmark = pd.merge(benchmark ,code , how = 'left' , on = ['证券码'])benchmark = pd.merge(benchmark ,price ,how = 'left' ,left_on=['年','月','年月日','证券码'], right_on=['年','月','年月日','证券代码'])# 处理产业不一致问题# 若 benchmark的产业种类没有在 etf的产业种类中找到,则 benchmark中特殊的产业改成其他benchmark['TSE产业名'] = np.where(benchmark['TSE产业名'].isin(etf['TSE产业名'].unique().tolist()),benchmark['TSE产业名'],'其他')# 若 etf的产业种类没有在 benchmark的产业种类中找到,则 etf中特殊的产业改成其他etf['TSE产业名'] = np.where(etf['TSE产业名'].isin(benchmark['TSE产业名'].unique().tolist()),etf['TSE产业名'],'其他')

Step 4. 计算 00881 ETF与台湾50指数的产业月报酬率
分别计算 00881 ETF与台湾50指数中各产业的产业月报酬率与产业权重。
#%% 计算产业与标竿指数的月报酬率,权重
etf = etf.sort_values(by=['年','月','TSE产业名','证券代码']).reset_index(drop=True) # 排序年月日etf['TSE产业名'] = np.where(etf['TSE产业名'].isna(),'其他' ,etf['TSE产业名'])etf['权重'] = etf['权重'] * 0.01etf['产业权重'] = etf.groupby(['TSE产业名','年','月'])['权重'].transform('sum')etf['实际当月报酬率'] = etf['权重'] * etf['当月报酬率']etf['产业当月报酬率'] = etf.groupby(['TSE产业名','年','月'])['实际当月报酬率'].transform('sum') / etf['产业权重']etf['实际产业当月报酬率'] = etf['产业当月报酬率'] * etf['产业权重']etf['ETF 当月报酬率'] = etf.groupby(['年','月'])['实际当月报酬率'].transform('sum')etf = etf[['年','月','TSE产业名','标的名称','权重','当月报酬率','产业权重','产业当月报酬率']]benchmark = benchmark.sort_values(by=['年','月','TSE产业名','证券代码']).reset_index(drop=True) # 排序年月日benchmark = benchmark[['年月日','TSE产业名','成份股','证券代码','年','月','前日市值比重','当月报酬率']]benchmark['前日市值比重'] = benchmark['前日市值比重'] * 0.01benchmark['产业权重'] = benchmark.groupby(['TSE产业名','年','月'])['前日市值比重'].transform('sum')benchmark['实际当月报酬率'] = benchmark['前日市值比重'] * benchmark['当月报酬率']benchmark['产业当月报酬率'] = benchmark.groupby(['TSE产业名','年','月'])['实际当月报酬率'].transform('sum')/ benchmark['产业权重']benchmark['实际产业当月报酬率'] = benchmark['产业当月报酬率'] * benchmark['产业权重']benchmark['ETF 当月报酬率'] = benchmark.groupby(['年','月'])['实际当月报酬率'].transform('sum')benchmark.head(5)

透过下表所示我们可以将主动报酬分拆成配置效果(Q2-Q1)、选择效果(Q3-Q1)与交互效果(Q4-Q3+Q2-Q1)。配置效果主要衡量资产类别、国家、产业偏移对绩效的影响;选择效果主要衡量每项类别下「选择不同标的证券」对绩效所造成的影响;而交互效果实务上常常并入配置效果或选择效果。

#%% 绩效归因表table = pd.merge(etf ,benchmark ,how = 'left' , on=['年','月','TSE产业名'])table['配置效果'] = (table['投组权重'] - table['标竿权重']) *(table['标竿当月报酬率'] - sum(table[:7]['标竿权重'] * table[:7]['标竿当月报酬率']))table['选择效果'] = table['标竿权重'] * (table['投组当月报酬率'] - table['标竿当月报酬率'])table['交互效果'] = (table['投组权重'] - table['标竿权重']) * (table['投组当月报酬率'] - table['标竿当月报酬率'])table['主动报酬'] = table['配置效果'] + table['选择效果'] + table['交互效果']table.loc['合计',:] = table.sum(axis=0)table.loc['合计','投组当月报酬率'] = sum(table[:7]['投组权重'] * table[:7]['投组当月报酬率'])table.loc['合计','标竿当月报酬率'] = sum(table[:7]['标竿权重'] * table[:7]['标竿当月报酬率'])table = (table * 100).round(2)table
我们计算出11月绩效归因表,发现 00881 ETF有更精准的选股能力,其选择效果达 1.61%,而因为 00881 ETF主要是投资半导体、网通、电动车个股,00881 ETF与台湾50指数皆有近 60%的权重在半导体产业,产业重叠性高,所以配置效果仅有 0.47%。

绘制三个月各产业的主动报酬雷达图,可以分析每月各产业贡献的主动报酬,发现产业对投组主动报酬的贡献会随著时间而轮动。轮动是常态,但要注意整体投组主动报酬是否能大于0。
#%%fig = go.Figure()for date in etf['月'].unique():table = pd.merge(etf ,benchmark ,how = 'left' , on=['年','月','TSE产业名'])table = table[table['月'] == date]table = table.drop(['年','月'], axis=1)table = table.set_index(['TSE产业名'])table = table.sort_values(by=['投组权重'], ascending=False)table = table.fillna(0)table['配置效果'] = (table['投组权重'] - table['标竿权重']) *(table['标竿当月报酬率'] - sum(table[:7]['标竿权重'] * table[:7]['标竿当月报酬率']))table['选择效果'] = table['标竿权重'] * (table['投组当月报酬率'] - table['标竿当月报酬率'])table['交互效果'] = (table['投组权重'] - table['标竿权重']) * (table['投组当月报酬率'] - table['标竿当月报酬率'])table['主动报酬'] = table['配置效果'] + table['选择效果'] + table['交互效果']table.loc['合计',:] = table.sum(axis=0)table.loc['合计','投组当月报酬率'] = sum(table[:7]['投组权重'] * table[:7]['投组当月报酬率'])table.loc['合计','标竿当月报酬率'] = sum(table[:7]['标竿权重'] * table[:7]['标竿当月报酬率'])table = (table * 100).round(2)fig.add_trace(go.Scatterpolar(r= table.loc['半导体':'其他','主动报酬'].tolist(),theta= table.drop(['合计']).index,fill='toself',name=str(date) + '月'))
fig.show()

绩效归因可帮助我们厘清投组的选股与产业配置是否得宜,可以作为日后投资分析与投资决定时的参照。最后推荐读者使用 TEJ资料库提供的基金与指数成份股,让我们可以获得不同期间指数的成份股,方便我们配对成份股所属的产业,分析不同投资组合的绩效归因。
本文供参考之用,因为本文仅用三个月的资料分析 ETF的绩效归因,并无法代表未来 ETF的绩效归因。因此本文不构成要约、招揽或邀请、诱使、任何不论种类或形式之申述或订立任何建议及推荐,读者务请运用个人独立思考能力,自行作出投资决定,如因相关建议招致损失,概与作者无涉。
电子报订阅