現金增資宣告效果,以事件研究法為例

現金增資
Photo Credits: Unsplash

前言

現金增資是上市櫃公司常見的籌資方式,目的為擴大資本支出、償還相關債務以及改善財務結構等,但有不少文獻指出,這種借錢的舉動會向市場發出負面的訊號,公司股價因此時常在宣告現金增資時應聲下跌。

而事件研究法即是研究公司重大事件(購併、增資、股票回購…)或是金融市場事件(制度改革、政策、總經…),對於公司價值影響的一種統計方法。一般而言,主要依循以下步驟進行 : 事件日確定、定義估計期間與事件發生期間、計算異常報酬率、統計檢驗。因此本週我們透過 TEJ API的資料庫,來觀察現金增資宣告是否真的會對台灣上市公司價值造成影響吧!

編輯環境及模組需求

本文使用 Windows OS 並以 Jupyter Notebook 作為編輯器

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

Note: sklearn.linear_model用於迴歸分析;scipy用於統計檢定

本文重點概要

  • 撈取現金增資事件
  • 事件研究法

資料庫使用

上市公司現金增資事件

Step 1. 撈取所有上市(普通股)公司代碼,儲存成 tse_stocks 列表

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. 上市櫃公司事件撈取

events = tejapi.get('TWN/ASTK1',
               x_mt_date = {'gte':'2019-01-01', 'lte':'2021-08-01'},
               opts = {'columns':['coid','cash','x_mt_date']},
               paginate = True,
               chinese_column_name = True)

本文以股東會決議日為事件發生日,並選取 2019~2021年8月之間的事件

cash = events[events['現金增資(仟股)']>0].reset_index(drop=True)

除了現金增資外,亦包含盈餘、公積增資等事件,而本文以現金增資為主

Step 3. 僅選出上市公司的現金增資事件

tse_cash = cash[cash['公司'].isin(tse_stocks)].reset_index(drop=True)

利用上市公司代碼tse_stocksisin(),篩選出 cash表格有涵蓋上市公司代碼的資料

事件研究法 — 資料處理

估計期間 (T0 ~ T1):設 -260期 ~ -10期,此期間的報酬與 Fama & French 5因子投組報酬跑迴歸,得到公司各因子的估計係數,其將用於計算事件期間的預期報酬

事件日 (0): 股東會決議日,因其為市場接收到現金增資事件的最早時點

事件期間 (T2 ~ T3):設 -5期 ~ +5期,計算此期間實際報酬與 Fama & French 模型所預測出的報酬差異,得到異常報酬

因涵蓋 140 起現金增資宣告事件,故以迴圈的方式進行每個事件的運算,並儲存最後結果至 final 變數,而過程中會排除報酬率資料缺漏、初次上市事件。以下擷取迴圈部分程式碼的重點內容加以解釋,並圖示第一筆資料 ( stock = ‘1314’, date = ‘2019–05–24’ ) 處理情況幫助理解每個迴圈的運作。其餘資料處理部分則詳見文章下方的完整程式碼

  • 撈取股價期間
return_set = tejapi.get('TWN/APRCD',
                      coid = stock,
                      mdate = {'gte': date + pd.Timedelta(days =     
                      -450), 'lte': date + pd.Timedelta(days = 25)}, 
                      opts = {'columns' : ['coid', 'mdate','roi']},
                      paginate = True,
                      chinese_column_name = True)

利用每起事件的公司 stock、日期 date,撈取事件日前後的報酬率,這邊取 -450 ~ 25 天是為了確保擁有完整估計期間 (251交易日)、事件期間 (11交易日)的報酬率

  • 切割出估計期間、事件期間報酬
event_index = return_set[return_set['年月日'] == date].index.values.astype(int)[0]

首先找出事件日的索引值,轉成數字後儲存到 event_index以此為基準切割成估計期間、事件期間報酬

estimate_window = return_set[event_index - 260 - 1: event_index - 10].reset_index(drop=True)
event_window = return_set[event_index - 5: event_index + 5 + 1].reset_index(drop=True)
  • 找出對應期間的 Fama & French 五因子投組報酬
#估計起始日期(-260)
estimate_start = estimate_window.loc[0, '年月日']
#事件結束日期(+5) 
event_end = event_window.loc[5*2, '年月日']

分別找出估計期間的起始日期、事件期間的結束日期,並儲存到對應變數。再以此為根據,直接撈出 – 260期 ~ 5期,這段期間的每日市場風險溢酬、規模溢酬、淨值市價比溢酬、盈利能力因子、投資因子投組報酬資料,Y9999代表以所有上市公司、簡單報酬率計算以上因子投組報酬

fama_french = tejapi.get('TWN/AFACTO1D',
                   coid = 'Y9999',
                   mdate = {'gte':estimate_start, 'lte': event_end}, 
                   opts = {'columns': ['coid', 
                   'mdate','mrp','smbn','bp','op','inv']},
                   paginate = True,
                   chinese_column_name = True)
  • 估計迴歸參數
ols_data = estimate_window.merge(fama_french, on = '年月日')
x = ols_data.loc[:,['市場風險溢酬', '規模溢酬(5因子)','淨值市價比溢酬','盈利能力因子','投資因子']].values
y = ols_data['報酬率%'].values.reshape(-1,1)
model = LinearRegression()
model.fit(x,y)

估計期間報酬率與 Fame-French 因子報酬合併,並形成帶入迴歸模型所需的二維陣列應變數(公司報酬率) 與自變數 (因子報酬),最後再帶入模型

  • 異常報酬率計算
predict_data = event_window.merge(fama_french, on = '年月日')    
event_x = predict_data.loc[:,['市場風險溢酬','規模溢酬(5因子)','淨值市價比溢酬','盈利能力因子','投資因子']].values

事件期間報酬率與 Fame & French 因子報酬合併,並將這段期間的因子投組報酬儲存,其將用於計算預期報酬的自變數,因此將其置入已有估計係數的模型 model.predict()算出預期報酬,接著即可算出異常報酬率、累計異常報酬率。這邊最後加上相對天數欄位,將用於最後所有事件的分群計算。

event_window['預期報酬率'] = model.predict(event_x)
event_window['異常報酬率'] = event_window['報酬率%'] - event_window['預期報酬率'] 
event_window['累計異常報酬率'] = event_window['異常報酬率'].cumsum()    
event_window['相對天數'] = [i for i in range(-5, 5 + 1)]

每個事件 (迴圈) 重複以上基本步驟,並將最後將結果都附加在 final 表格,以下為 final 最終結果

事件研究法 — 視覺化與統計檢定

  • 計算平均異常報酬、累計異常報酬
sorted_data = final.groupby(by = '相對天數').mean().reset_index()
  • 視覺化
plt.plot(sorted_data['相對天數'], sorted_data['異常報酬率'], label = 'Average Abnormal Retrun')
plt.plot(sorted_data['相對天數'], sorted_data['累計異常報酬率'], label = 'Cumulative Abnormal Return')
plt.xlabel('Event Day')
plt.title('Event Study')
plt.xticks(np.arange(-5, 5+1 , 1)) 
plt.legend()
plt.show()

可以看到,在宣告現金增資日後的幾天,出現明顯地異常負報酬,但到了第三天後,異常報酬恢復到 0 左右,可能代表市場已經消化這個資訊

  • 統計檢定
ttest = pd.DataFrame()
for day in range(-5,5+1): 
    sample = final[final['相對天數'] == day]['異常報酬率'].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 = ''  
    ttest = ttest.append(pd.DataFrame(np.array([day,t,p_value, significance]).reshape((1,4)), columns = ['相對天數','T檢定值', 'P-value','顯著水準'],)).reset_index(drop=True)
ttest = ttest.set_index('相對天數')

利用 stats.ttest_lsamp() 進行T檢定,檢驗每個相對日的異常報酬是否顯著異於 0。最後再根據 P 值自行加上顯著性星星,最後整理成 ttest 表格

此時可以推論,在現金增資宣告日的後1、2日,公司的價值顯著受到影響,推測是因為公司通常於盤後宣告重大決議,因此股價於次日交易日開始反應

結論

經過這一連串的操作,相信讀者對於事件研究有了進一步的認識!過程中有許多假設可以自行調整、或是參考對應領域的文獻,例如估計期間、事件期間與異常報酬的計算方式,而這當中運用到的資料,都能從 TEJ API 資料庫快速撈取下來,直接省略事前資料爬取、清理、計算等繁瑣步驟。若讀者有興趣驗證其他經濟事件對公司的影響,推薦到 TEJ E Shop,選購最符合自身需求的方案!

完整程式碼

延伸閱讀

相關連結

返回總覽頁
Procesing