公司財報與股價的合併觀察


股價變動與公司財報揭露的關係

不管是在做學術研究或個人投資時,股價變動與公司財報揭露的關係,都是大家考慮的一個重點,例如當公司發布超出市場預期的每股盈餘時,股價隔天往往會有上漲的反應。因此為了研究兩者之間的關係,就需要一組時間序列資料,也就是當時間軸日期變動時,要能同時紀錄股價與財務資料的變化。

 

這邊就會有一些值得注意的地方,財報的編製必定需要時間,因此我們不會在當下就看到即時的數據,證交所會公告不同季度的財報發佈期限,目前台灣一般公司(金融業較特殊)的規範是每年的 5/15、8/14、11/14 前要發布第一、二、三季的財報,而隔年的 3/31 前則要發布整年的年度財報。

 

因此如果我們想比對每天的股價變化,是否有受到財報揭露的影響,就要正確地把以「日」為頻率的股價,跟以「季」為頻率的財報做結合,以鴻海(2317-TW)為例,2018 年 Q3 的財報是等到 11/14 時才公布,此時我們才能取得鴻海 Q3 的財務資料。

 

 

 

而這件事可以利用 TEJ 的以下三個資料表結合完成。

 

  • 上市(櫃)未調整股價(日)
  • IFRS 以合併為主簡表(累計)— 全產業
  • IFRS 以合併為主簡表(累計)— 全產業 報表封面資料

 

首先我們可以用下列語法查詢這三個資料表的資料。

 

TEJ API Pyhton程式碼:

import pandas as pd
import numpy as np
import tejapi  
tejapi.ApiConfig.api_key = "Your Key"
#設定要查詢的資料日期區間
sampledates = ['2018-01-01','2018-12-31']
finsheetdates = ['2017-01-01','2018-12-31']
#要查詢的股票代碼
check_coid = ['2317']       #查詢代碼,鴻海2317                
#查詢財報封面揭露資料,內含財報公開日,把TAIM1AA改成TAIM1AQA就是非累計的
announce_data = tejapi.get('TWN/AIM1AQA', coid=check_coid,mdate={'gte':finsheetdates[0],'lte':finsheetdates[1]},opts={"sort":"mdate.desc",'columns':['coid','mdate','a0003']}, paginate=True)
#查詢累計財報資料,把TAIM1A改成TAIM1AQ就是非累計的
cumulative_data = tejapi.get('TWN/AIM1AQ', coid=check_coid,mdate={'gte':finsheetdates[0],'lte':finsheetdates[1]},opts={"sort":"mdate.desc"}, paginate=True)
#查詢每日股價與報酬率資料
price_data = tejapi.get('TRAIL/TAPRCD', coid=check_coid,mdate={'gte':sampledates[0],'lte':sampledates[1]},opts={"sort":"mdate.desc",'columns':['coid','mdate','close_d','roib']}, paginate=True)

 

在財報資料庫「IFRS 以合併為主簡表(累計)-全產業」中雖然有 mdate 這個日期欄位,但 mdate 代表的是財報的「季」別,如 2018/9/1 代表的是 Q3 的財報、2018/12/1 代表的是 Q4 的,如果直接用財報的 mdate 來跟股價資料合併,就會出現 2018/9/1 可以觀察到 Q3 財報數據的不合理現象,正確的作法是應該先將各季的財報資料給予額外欄位,用來記錄揭露日的資訊,如下面的程式碼處理。

TEJ API Pyhton程式碼:

#將財報發布日與財報資料合併
cumulative_data = cumulative_data.merge(announce_data,on=['mdate','coid'], how='inner')

 

如此一來,就可以在股價資料中併入財務資料了,只要依序根據不同的財報揭露日,把財務資料換成最新的數據即可,像下圖這樣。

 

 

執行的程式碼就如同下面的範例。

 

TEJ API Pyhton程式碼:

#將各公司財報資料填入股價資料中,在財報更新日之後的財務資料,就替換為當時最新的資料
#註:"a0003"就是財報揭露日的資料欄位
all_coid = cumulative_data['coid'].unique().tolist()
all_col = cumulative_data['acc_code'].unique().tolist()
all_announce_date = sorted(announce_data['a0003'].unique()) #必須先排序,這樣才能確保從最舊的開始往新的填
for new_col in all_col:
    for this_coid in all_coid:
        for a_date in all_announce_date:        
            this_val = cumulative_data.loc[(cumulative_data['acc_code']==new_col)&(cumulative_data['a0003']==a_date),'acc_value'] 
            if len(this_val)>0:
                price_data.loc[(price_data['mdate']>=a_date)&(price_data['coid']==this_coid),new_col]=this_val.values[0]

 

由於 TEJ 的財務資料庫中,欄位的值是用代碼 acc_code 來查詢,而不是名稱。

 

 

如上圖一般,欄位名稱會是代碼而不是一般熟知的會計科目,用 pandas 指令查詢欄位名稱果然都是代碼如下。

 

TEJ API Pyhton程式碼:

price_data.columns

TEJ API Pyhton程式碼輸出結果:

Index(['coid', 'mdate', 'close_d', 'roib', 'R836', 'R835', 'R834', 'R69B',
       'R615', 'R614',
       ...
       '0170', '0165', '0160', '0130', '012C', '0112', '0100', '0020', '0010',
       '1522'],
      dtype='object', length=198)

 

因此我們可以用「財務科目代碼名稱對照表」,來把原本的 acc_code 都替換為中文名稱 cname,也可以把下面語法中標示說明可以替換的地方更換成 ename,換成英文名稱。

TEJ API Pyhton程式碼:

#查詢財務科目代碼名稱對照表
descript_data = tejapi.get('TRAIL/TAIACC',opts={'columns':['code','cname','ename']}, paginate=True)            
#將欄位代碼轉換為欄位中文名稱
change_name_list={}            
for new_col in all_col:
    col_name = descript_data.loc[descript_data['code']==new_col,'cname'].values[0]  #此處的cname換成ename就是英文
    change_name_list[new_col] = col_name            
price_data = price_data.rename(change_name_list,axis='columns')

 

再看一次price_data中的欄位名稱,果然都換成中文了:

TEJ API Pyhton程式碼:

price_data.columns

TEJ API Pyhton程式碼輸出結果:

Index(['coid', 'mdate', 'close_d', 'roib', '每人配備率', '每人營業利益', '每人營收',
       '自由現金流量(D)', '淨營業週期(日)', '應付帳款付現天數',
       ...
       '存貨', '資金貸予他人-流動', '其他應收款', '應收帳款及票據', 'BS透過損益按公允價值衡量之金融資產-流動(IFRS)',
       '現金及約當現金', '流動資產', '負債及股東權益總額', '資產總額', '預計稅額扣抵比率'],
      dtype='object', length=198)

最後我們來畫圖看看合併後的結果,當我們把鴻海股價跟 EPS 放在一起時,看 EPS 是不是真的隨著公告日出現變化。

TEJ API Pyhton程式碼:

import matplotlib.pyplot as plt
xy = price_data.reindex(columns=['mdate','close_d']).values
mx = price_data.reindex(columns=['mdate','常續性EPS']).values
fig, ax1 = plt.subplots(figsize=(10,8))
ax2 = ax1.twinx()
xy = xy[xy[:,0].argsort()]
mx = mx[mx[:,0].argsort()]
ax1.plot(xy[:, 0], xy[:, 1], linewidth=5,color='green')
ax2.plot(mx[:, 0], mx[:, 1], linewidth=5,color='red' )
ax1.set_xlabel('Date')
yx1 = ax1.set_ylabel('close_price', color='green')
yx1.set_rotation(0)
yx2 = ax2.set_ylabel('ongoing EPS, bar chart', color='red')
yx2.set_rotation(0)
plt.show()
  

果然 EPS 在 8/14、11/13 這兩個日期出現變化,財報資料跟股價正確地按照時間結合了。

 

註:此地方之 EPS 是 TEJ 所計算之近四季常續性 EPS

 

本篇文章所展示之內容係由 TEJ API 工具撈取。

完整程式碼請透過連結下載:完整程式連結

如果對於使用上有任何問題,歡迎透過連結進一步連絡我們,我們將有專人進一步為您服務,謝謝