春节期间台股绩效表现

本文重点概要

  • 文章难度:★★☆☆☆
  • 整理自2008年起台股封关前后的报酬&利用事件研究法讨论具有异常报酬之标的
  • 阅读建议:本文会先讨论台股大盘在封关前后的涨跌,再借由事件研究法选出这段期间具有异常报酬之标的,若读者对于事件研究法感到陌生,可以先行阅读现金增资宣告效果,以事件研究法为例,了解事件研究法的基础原理。

前言

农历春节是华人社会中最重要的节日,传统来说这段期间才是年末与新年的开始,证券市场参与者也会在此时对过去的市况进行总结并于年节后表态对于未来的展望。因此内文会以农历年节为主题,讨论这段期间台股市场的行情;此外也会对个股进行事件研究,以正负5日为事件期间,讨论封关前后的异常报酬情形。

Note: 本文提及之个股标的仅供说明使用,不代表任何金融商品之推荐或建议。

编辑环境及模组需求

本文使用MacOS 并以 Jupyter Notebook 作为编辑器。

import pandas as pd import numpy as np from sklearn.linear_model import LinearRegression from scipy import stats from datetime import datetime import tejapi tejapi.ApiConfig.api_key = 'Your Key' tejapi.ApiConfig.ignoretz = True

资料库使用

大盘行情

Step 1. 资料捞取

twn = tejapi.get('TWN/APRCD',                 coid = 'Y9997',                 mdate = {'gte': '2008-01-01', 'lte':'2021-03-30'},                 opts = {'columns':['mdate', 'roia']},                 paginate = True,                 chinese_column_name = True)

捞取报酬指数(Y9997),并以2008–01–01至2021–03–30为区间,确保涵盖各年度农历年节前后的范围。

资料表(一)

Step 2. 输入封关最后交易日&对比大盘资料日期

# 历年春节前最后交易日 last_day = ['2008-02-01', '2009-01-21', '2010-02-10', '2011-01-28', '2012-01-18', '2013-02-06', '2014-01-27', '2015-02-13', '2016-02-03', '2017-01-24', '2018-02-12', '2019-01-30', '2020-01-20', '2021-02-05'] # 确保可以使用last_day为日资料型态,将其转换成时间物件 last_day = [datetime.fromisoformat(i) for i in last_day] date = [] for i in last_day:     date.append(int(twn[twn['年月日'] == i].index.values))

Step 3. 计算封关前后15日、5日以及当日报酬率

market = pd.DataFrame(columns = ['前十五日累计报酬', '前五日累计报酬', '封关日报酬', '后五日累计报酬', '后十五日累计报酬' ]) for i in date:     # 封关前十五日累计报酬     market.loc[i, '前十五日累计报酬'] = ((twn.loc[i, '收盘价(元)'] / twn.loc[i-15, '收盘价(元)']) - 1) * 100     # 封关前五日累计报酬     market.loc[i, '前五日累计报酬'] = ((twn.loc[i, '收盘价(元)'] / twn.loc[i-5, '收盘价(元)']) - 1) * 100     # 封关当天报酬     market.loc[i, '封关日报酬'] = ((twn.loc[i, '收盘价(元)'] / twn.loc[i-1, '收盘价(元)']) - 1) * 100     # 封关后五日累计报酬     market.loc[i, '后五日累计报酬'] = ((twn.loc[i+5, '收盘价(元)'] / twn.loc[i, '收盘价(元)']) - 1) * 100     # 封关后十五日累计报酬     market.loc[i, '后十五日累计报酬'] = ((twn.loc[i+15, '收盘价(元)'] / twn.loc[i, '收盘价(元)']) - 1) * 100 market = market.reset_index().drop(columns = 'index')
资料表(二)

Step 4. 计算平均涨跌幅&涨跌幅情况

for i in range(0,5):     data['平均涨幅'][i] = market.iloc[:, i].mean()        pos = list(filter(lambda x: (x > 0), market.iloc[:,i]))     data['上涨次数'][i] = len(pos)     data['上涨机率'][i] = len(pos) / len(last_day)     data['上涨平均涨幅'][i] = np.mean(pos)     neg = list(filter(lambda x: (x < 0), market.iloc[:,i]))         data['下跌次数'][i] = len(neg)     data['下跌机率'][i] = len(neg) / len(last_day)     data['下跌平均跌幅'][i] = np.mean(neg)
资料表(三)

从表中可以发现年节前三个礼拜的交易日出现跌大于涨的情形,并且在年节后有涨大于跌的趋势。这应该是源于年节前市场参与者会倾向出场,以避免接下来休市期间可能发生意外事件所导致的冲击;而年后往往也会展开新年的计划,进场布局,因此以多头为主。

个股事件研究 — 封关前

Note:再次提醒,若是对事件研究法不熟悉的读者,可以先行阅读现金增资宣告效果,以事件研究法为例

Step 1. 取得普通股代码

security = tejapi.get('TWN/ANPRCSTD',               mkt = 'TSE',               stypenm = '普通股',               opts = {'columns':['coid','mdate','stypenm','mkt']},               paginate = True,               chinese_column_name = True) tse_stocks = security['证券码'].tolist()

Step 2. 事件研究过程(详见完整程式码)

  1. 通过估计期间(每年度各251期)的资料建置Fama-French五因子模型。
  2. 应用五因子模型对事件期间(封关前后5日与当天,共11期)进行股价报酬预测。
  3. 将实际报酬减去预期报酬,得到异常报酬率,并计算每日累计异常报酬率。
资料表(四)

Step 3. 检定封关前的异常报酬显著性

通过t值、P-value检定个股的异常报酬显著水准。

pre_close = pd.DataFrame() for stock in tse_stocks:     #以封关当天累计异常报酬进行检定      df = test[test['证券代码'] == stock].reset_index(drop=True)     df = df[df['相对天数'] == 0]     #删除样本过少公司     if len(df) < 4:         print(stock, 'has no enough data')         continue     sample = df['累计异常报酬率'].values     #检定     t, p_value = stats.ttest_1samp(sample, 0)     if p_value <= 0.01:         significance = '***'     elif 0.01 < p_value <= 0.05:         significance = '**'     elif 0.05 < p_value <= 0.1:         significance = '*'     else:         significance = ''       pre_close = pre_close.append(pd.DataFrame(np.array([stock,t,p_value, significance]).reshape((1,4)), columns = ['证券代码','T检定值', 'P-value','显著水准'],)).reset_index(drop=True)
资料表(五)

以最严格的检定水准(P-value < 0.01),挑选异常报酬具有显著性之个股标的。

result = pre_close[(pre_close['T检定值'] > 0) & (pre_close['P-value'] < 0.01) ].reset_index(drop=True)
资料表(六)

Step 4. 计算异常报酬

首先,计算各年度递延n期的平均报酬,例如:各年度封关前5日的平均异常报酬;接著,计算期间的累计平均报酬,也就是封关前五日到当天的平均累计报酬。

# 各期平均报酬 for i in range(-5, 1):     result['第' + str(i) + '天平均异常报酬'] = test[test['证券代码'].isin(positive_list) & (test['相对天数'] == i)].groupby('证券代码')['异常报酬率'].mean().reset_index(drop=True) # 加总累计各平均报酬 result['期间累计平均异常报酬率'] = test[test['证券代码'].isin(positive_list) & (test['相对天数'] == 0)].groupby('证券代码')['累计异常报酬率'].mean().reset_index(drop=True)
资料表(七)

Step 5. 合并公司资料表

result['公司中文简称'] = '' result['TEJ产业名'] = '' result['TEJ子产业名'] = '' for i in range(len(positive_list)):     firm_info = tejapi.get('TWN/AIND',                           coid = positive_list[i],                           opts = {'columns':['coid', 'inamec', 'tejind2_c', 'tejind3_c']},                           chinese_column_name = True,                           paginate = True)     result.loc[i, '公司中文简称'] = firm_info.loc[0, '公司中文简称']     result.loc[i, 'TEJ产业名'] = firm_info.loc[0, 'TEJ产业名']     result.loc[i, 'TEJ子产业名'] = firm_info.loc[0, 'TEJ子产业名']
资料表(八)
资料表(九)

透过公司产业属性可以发现,三家在农历年封关前异常报酬最具显著性的公司为工业类股或加工品制造类股。

个股事件研究 — 封关后

封关前后的处理过程基本上大同小异,只需要更改事件期间进行计算即可,本文因篇幅考量,此处直接呈现最终结果,供读者参考。(详见完整程式码)。

资料表(十)
资料表(十一)

透过公司产业属性了解,工业类股在年节封关后依然还是相对台股其他族群具有显著异常报酬的个股。

Note: 本文所提及之个股标的仅供说明使用,不代表任何金融商品之推荐或建议。

结论

首先,本文借由台股报酬指数的封关前后表现,分析市场参与者对于休市期间的态度,发现休市前一段时间市场会出现些微空头趋势,并且在新年开始会有多头的行情出现。

其次,本文第二部分的主题为事件研究法讨论个股异常报酬,通过封关前后的结果,读者应该可以发现工业类股的异常报酬较有显著性,而本文认为这是综合考量年度初期基础工业加工品订单开始增加且国际金属价格经常在二月底会呈现滑落的状态,所以市场预期工业类股公司的未来营收成长且成本下降而进场该类型个股。

最后,还是要再次提醒本文所提及之标的仅供说明使用,不代表任何金融商品之推荐或建议。因此,若读者对于事件研究等议题有兴趣,欢迎选购 TEJ E Shop中的方案,具有齐全的资料库,就能轻易的完成各种检定。

完整程式码

延伸阅读

相关连结

返回总览页